Commit 8b6dd72a authored by Jeff Mahoney's avatar Jeff Mahoney Committed by Linus Torvalds

reiserfs: make per-inode xattr locking more fine grained

The per-inode locking can be made more fine-grained to surround just the
interaction with the filesystem itself.  This really only applies to
protecting reads during a write, since concurrent writes are barred with
inode->i_mutex at the vfs level.
Signed-off-by: default avatarJeff Mahoney <jeffm@suse.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d984561b
...@@ -29,10 +29,8 @@ ...@@ -29,10 +29,8 @@
* to the inode so that unnecessary lookups are avoided. * to the inode so that unnecessary lookups are avoided.
* *
* Locking works like so: * Locking works like so:
* The xattr root (/.reiserfs_priv/xattrs) is protected by its i_mutex. * Directory components (xattr root, xattr dir) are protectd by their i_mutex.
* The xattr dir (/.reiserfs_priv/xattrs/<oid>.<gen>) is protected by * The xattrs themselves are protected by the xattr_sem.
* inode->xattr_sem.
* The xattrs themselves are likewise protected by the xattr_sem.
*/ */
#include <linux/reiserfs_fs.h> #include <linux/reiserfs_fs.h>
...@@ -55,6 +53,8 @@ ...@@ -55,6 +53,8 @@
#define PRIVROOT_NAME ".reiserfs_priv" #define PRIVROOT_NAME ".reiserfs_priv"
#define XAROOT_NAME "xattrs" #define XAROOT_NAME "xattrs"
static struct reiserfs_xattr_handler *find_xattr_handler_prefix(const char *);
/* Helpers for inode ops. We do this so that we don't have all the VFS /* Helpers for inode ops. We do this so that we don't have all the VFS
* overhead and also for proper i_mutex annotation. * overhead and also for proper i_mutex annotation.
* dir->i_mutex must be held for all of them. */ * dir->i_mutex must be held for all of them. */
...@@ -339,12 +339,14 @@ int xattr_readdir(struct inode *inode, filldir_t filler, void *buf) ...@@ -339,12 +339,14 @@ int xattr_readdir(struct inode *inode, filldir_t filler, void *buf)
return res; return res;
} }
/* expects xadir->d_inode->i_mutex to be locked */
static int static int
__reiserfs_xattr_del(struct dentry *xadir, const char *name, int namelen) __reiserfs_xattr_del(struct dentry *xadir, const char *name, int namelen)
{ {
struct dentry *dentry; struct dentry *dentry;
struct inode *dir = xadir->d_inode; struct inode *dir = xadir->d_inode;
int err = 0; int err = 0;
struct reiserfs_xattr_handler *xah;
dentry = lookup_one_len(name, xadir, namelen); dentry = lookup_one_len(name, xadir, namelen);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
...@@ -372,6 +374,14 @@ __reiserfs_xattr_del(struct dentry *xadir, const char *name, int namelen) ...@@ -372,6 +374,14 @@ __reiserfs_xattr_del(struct dentry *xadir, const char *name, int namelen)
return -EIO; return -EIO;
} }
/* Deletion pre-operation */
xah = find_xattr_handler_prefix(name);
if (xah && xah->del) {
err = xah->del(dentry->d_inode, name);
if (err)
goto out;
}
err = xattr_unlink(dir, dentry); err = xattr_unlink(dir, dentry);
out_file: out_file:
...@@ -398,7 +408,7 @@ reiserfs_delete_xattrs_filler(void *buf, const char *name, int namelen, ...@@ -398,7 +408,7 @@ reiserfs_delete_xattrs_filler(void *buf, const char *name, int namelen,
/* This is called w/ inode->i_mutex downed */ /* This is called w/ inode->i_mutex downed */
int reiserfs_delete_xattrs(struct inode *inode) int reiserfs_delete_xattrs(struct inode *inode)
{ {
int err = 0; int err = -ENODATA;
struct dentry *dir, *root; struct dentry *dir, *root;
struct reiserfs_transaction_handle th; struct reiserfs_transaction_handle th;
int blocks = JOURNAL_PER_BALANCE_CNT * 2 + 2 + int blocks = JOURNAL_PER_BALANCE_CNT * 2 + 2 +
...@@ -414,14 +424,19 @@ int reiserfs_delete_xattrs(struct inode *inode) ...@@ -414,14 +424,19 @@ int reiserfs_delete_xattrs(struct inode *inode)
goto out; goto out;
} else if (!dir->d_inode) { } else if (!dir->d_inode) {
dput(dir); dput(dir);
return 0; goto out;
} }
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_XATTR); mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_XATTR);
err = xattr_readdir(dir->d_inode, reiserfs_delete_xattrs_filler, dir); err = xattr_readdir(dir->d_inode, reiserfs_delete_xattrs_filler, dir);
mutex_unlock(&dir->d_inode->i_mutex); mutex_unlock(&dir->d_inode->i_mutex);
if (err) if (err) {
goto out_dir; dput(dir);
goto out;
}
root = dget(dir->d_parent);
dput(dir);
/* We start a transaction here to avoid a ABBA situation /* We start a transaction here to avoid a ABBA situation
* between the xattr root's i_mutex and the journal lock. * between the xattr root's i_mutex and the journal lock.
...@@ -435,19 +450,14 @@ int reiserfs_delete_xattrs(struct inode *inode) ...@@ -435,19 +450,14 @@ int reiserfs_delete_xattrs(struct inode *inode)
err = journal_begin(&th, inode->i_sb, blocks); err = journal_begin(&th, inode->i_sb, blocks);
if (!err) { if (!err) {
int jerror; int jerror;
root = dget(dir->d_parent);
mutex_lock_nested(&root->d_inode->i_mutex, I_MUTEX_XATTR); mutex_lock_nested(&root->d_inode->i_mutex, I_MUTEX_XATTR);
err = xattr_rmdir(root->d_inode, dir); err = xattr_rmdir(root->d_inode, dir);
jerror = journal_end(&th, inode->i_sb, blocks); jerror = journal_end(&th, inode->i_sb, blocks);
mutex_unlock(&root->d_inode->i_mutex); mutex_unlock(&root->d_inode->i_mutex);
dput(root);
err = jerror ?: err; err = jerror ?: err;
} }
out_dir: dput(root);
dput(dir);
out: out:
if (!err) if (!err)
REISERFS_I(inode)->i_flags = REISERFS_I(inode)->i_flags =
...@@ -484,7 +494,7 @@ reiserfs_chown_xattrs_filler(void *buf, const char *name, int namelen, ...@@ -484,7 +494,7 @@ reiserfs_chown_xattrs_filler(void *buf, const char *name, int namelen,
if (!S_ISDIR(xafile->d_inode->i_mode)) { if (!S_ISDIR(xafile->d_inode->i_mode)) {
mutex_lock_nested(&xafile->d_inode->i_mutex, I_MUTEX_CHILD); mutex_lock_nested(&xafile->d_inode->i_mutex, I_MUTEX_CHILD);
err = notify_change(xafile, attrs); err = reiserfs_setattr(xafile, attrs);
mutex_unlock(&xafile->d_inode->i_mutex); mutex_unlock(&xafile->d_inode->i_mutex);
} }
dput(xafile); dput(xafile);
...@@ -520,13 +530,16 @@ int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs) ...@@ -520,13 +530,16 @@ int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs)
err = xattr_readdir(dir->d_inode, reiserfs_chown_xattrs_filler, &buf); err = xattr_readdir(dir->d_inode, reiserfs_chown_xattrs_filler, &buf);
if (!err) if (!err)
err = notify_change(dir, attrs); err = reiserfs_setattr(dir, attrs);
mutex_unlock(&dir->d_inode->i_mutex); mutex_unlock(&dir->d_inode->i_mutex);
attrs->ia_valid = ia_valid; attrs->ia_valid = ia_valid;
out_dir: out_dir:
dput(dir); dput(dir);
out: out:
if (err)
reiserfs_warning(inode->i_sb, "jdm-20007",
"Couldn't chown all xattrs (%d)\n", err);
return err; return err;
} }
...@@ -635,9 +648,8 @@ reiserfs_xattr_set(struct inode *inode, const char *name, const void *buffer, ...@@ -635,9 +648,8 @@ reiserfs_xattr_set(struct inode *inode, const char *name, const void *buffer,
if (get_inode_sd_version(inode) == STAT_DATA_V1) if (get_inode_sd_version(inode) == STAT_DATA_V1)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* Empty xattrs are ok, they're just empty files, no hash */ if (!buffer)
if (buffer && buffer_size) return reiserfs_xattr_del(inode, name);
xahash = xattr_hash(buffer, buffer_size);
dentry = get_xa_file_dentry(inode, name, flags); dentry = get_xa_file_dentry(inode, name, flags);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
...@@ -645,13 +657,19 @@ reiserfs_xattr_set(struct inode *inode, const char *name, const void *buffer, ...@@ -645,13 +657,19 @@ reiserfs_xattr_set(struct inode *inode, const char *name, const void *buffer,
goto out; goto out;
} }
down_write(&REISERFS_I(inode)->i_xattr_sem);
xahash = xattr_hash(buffer, buffer_size);
REISERFS_I(inode)->i_flags |= i_has_xattr_dir; REISERFS_I(inode)->i_flags |= i_has_xattr_dir;
/* Resize it so we're ok to write there */ /* Resize it so we're ok to write there */
newattrs.ia_size = buffer_size; newattrs.ia_size = buffer_size;
newattrs.ia_ctime = current_fs_time(inode->i_sb);
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_XATTR); mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_XATTR);
err = notify_change(dentry, &newattrs); down_write(&dentry->d_inode->i_alloc_sem);
err = reiserfs_setattr(dentry, &newattrs);
up_write(&dentry->d_inode->i_alloc_sem);
mutex_unlock(&dentry->d_inode->i_mutex); mutex_unlock(&dentry->d_inode->i_mutex);
if (err) if (err)
goto out_filp; goto out_filp;
...@@ -712,6 +730,7 @@ reiserfs_xattr_set(struct inode *inode, const char *name, const void *buffer, ...@@ -712,6 +730,7 @@ reiserfs_xattr_set(struct inode *inode, const char *name, const void *buffer,
} }
out_filp: out_filp:
up_write(&REISERFS_I(inode)->i_xattr_sem);
dput(dentry); dput(dentry);
out: out:
...@@ -747,10 +766,7 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer, ...@@ -747,10 +766,7 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer,
goto out; goto out;
} }
/* protect against concurrent access. xattrs are backed by down_read(&REISERFS_I(inode)->i_xattr_sem);
* regular files, but they're not regular files. The updates
* must be atomic from the perspective of the user. */
mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_XATTR);
isize = i_size_read(dentry->d_inode); isize = i_size_read(dentry->d_inode);
REISERFS_I(inode)->i_flags |= i_has_xattr_dir; REISERFS_I(inode)->i_flags |= i_has_xattr_dir;
...@@ -758,12 +774,12 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer, ...@@ -758,12 +774,12 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer,
/* Just return the size needed */ /* Just return the size needed */
if (buffer == NULL) { if (buffer == NULL) {
err = isize - sizeof(struct reiserfs_xattr_header); err = isize - sizeof(struct reiserfs_xattr_header);
goto out_dput; goto out_unlock;
} }
if (buffer_size < isize - sizeof(struct reiserfs_xattr_header)) { if (buffer_size < isize - sizeof(struct reiserfs_xattr_header)) {
err = -ERANGE; err = -ERANGE;
goto out_dput; goto out_unlock;
} }
while (file_pos < isize) { while (file_pos < isize) {
...@@ -778,7 +794,7 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer, ...@@ -778,7 +794,7 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer,
page = reiserfs_get_page(dentry->d_inode, file_pos); page = reiserfs_get_page(dentry->d_inode, file_pos);
if (IS_ERR(page)) { if (IS_ERR(page)) {
err = PTR_ERR(page); err = PTR_ERR(page);
goto out_dput; goto out_unlock;
} }
lock_page(page); lock_page(page);
...@@ -797,7 +813,7 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer, ...@@ -797,7 +813,7 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer,
"associated with %k", name, "associated with %k", name,
INODE_PKEY(inode)); INODE_PKEY(inode));
err = -EIO; err = -EIO;
goto out_dput; goto out_unlock;
} }
hash = le32_to_cpu(rxh->h_hash); hash = le32_to_cpu(rxh->h_hash);
} }
...@@ -818,8 +834,8 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer, ...@@ -818,8 +834,8 @@ reiserfs_xattr_get(const struct inode *inode, const char *name, void *buffer,
err = -EIO; err = -EIO;
} }
out_dput: out_unlock:
mutex_unlock(&dentry->d_inode->i_mutex); up_read(&REISERFS_I(inode)->i_xattr_sem);
dput(dentry); dput(dentry);
out: out:
...@@ -852,8 +868,6 @@ int reiserfs_xattr_del(struct inode *inode, const char *name) ...@@ -852,8 +868,6 @@ int reiserfs_xattr_del(struct inode *inode, const char *name)
} }
/* Actual operations that are exported to VFS-land */ /* Actual operations that are exported to VFS-land */
static struct reiserfs_xattr_handler *find_xattr_handler_prefix(const char *);
/* /*
* Inode operation getxattr() * Inode operation getxattr()
*/ */
...@@ -868,9 +882,7 @@ reiserfs_getxattr(struct dentry * dentry, const char *name, void *buffer, ...@@ -868,9 +882,7 @@ reiserfs_getxattr(struct dentry * dentry, const char *name, void *buffer,
get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1) get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
return -EOPNOTSUPP; return -EOPNOTSUPP;
reiserfs_read_lock_xattr_i(dentry->d_inode);
err = xah->get(dentry->d_inode, name, buffer, size); err = xah->get(dentry->d_inode, name, buffer, size);
reiserfs_read_unlock_xattr_i(dentry->d_inode);
return err; return err;
} }
...@@ -890,9 +902,7 @@ reiserfs_setxattr(struct dentry *dentry, const char *name, const void *value, ...@@ -890,9 +902,7 @@ reiserfs_setxattr(struct dentry *dentry, const char *name, const void *value,
get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1) get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
return -EOPNOTSUPP; return -EOPNOTSUPP;
reiserfs_write_lock_xattr_i(dentry->d_inode);
err = xah->set(dentry->d_inode, name, value, size, flags); err = xah->set(dentry->d_inode, name, value, size, flags);
reiserfs_write_unlock_xattr_i(dentry->d_inode);
return err; return err;
} }
...@@ -910,21 +920,11 @@ int reiserfs_removexattr(struct dentry *dentry, const char *name) ...@@ -910,21 +920,11 @@ int reiserfs_removexattr(struct dentry *dentry, const char *name)
get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1) get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
return -EOPNOTSUPP; return -EOPNOTSUPP;
reiserfs_write_lock_xattr_i(dentry->d_inode);
/* Deletion pre-operation */
if (xah->del) {
err = xah->del(dentry->d_inode, name);
if (err)
goto out;
}
err = reiserfs_xattr_del(dentry->d_inode, name); err = reiserfs_xattr_del(dentry->d_inode, name);
dentry->d_inode->i_ctime = CURRENT_TIME_SEC; dentry->d_inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(dentry->d_inode); mark_inode_dirty(dentry->d_inode);
out:
reiserfs_write_unlock_xattr_i(dentry->d_inode);
return err; return err;
} }
...@@ -986,7 +986,6 @@ ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size) ...@@ -986,7 +986,6 @@ ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size)
get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1) get_inode_sd_version(dentry->d_inode) == STAT_DATA_V1)
return -EOPNOTSUPP; return -EOPNOTSUPP;
reiserfs_read_lock_xattr_i(dentry->d_inode);
dir = open_xa_dir(dentry->d_inode, XATTR_REPLACE); dir = open_xa_dir(dentry->d_inode, XATTR_REPLACE);
if (IS_ERR(dir)) { if (IS_ERR(dir)) {
err = PTR_ERR(dir); err = PTR_ERR(dir);
...@@ -1005,19 +1004,16 @@ ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size) ...@@ -1005,19 +1004,16 @@ ssize_t reiserfs_listxattr(struct dentry * dentry, char *buffer, size_t size)
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_XATTR); mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_XATTR);
err = xattr_readdir(dir->d_inode, reiserfs_listxattr_filler, &buf); err = xattr_readdir(dir->d_inode, reiserfs_listxattr_filler, &buf);
mutex_unlock(&dir->d_inode->i_mutex); mutex_unlock(&dir->d_inode->i_mutex);
if (err)
goto out_dir;
if (buf.r_pos > buf.r_size && buffer != NULL) if (!err) {
err = -ERANGE; if (buf.r_pos > buf.r_size && buffer != NULL)
else err = -ERANGE;
err = buf.r_pos; else
err = buf.r_pos;
}
out_dir:
dput(dir); dput(dir);
out:
out:
reiserfs_read_unlock_xattr_i(dentry->d_inode);
return err; return err;
} }
...@@ -1115,12 +1111,8 @@ static int reiserfs_check_acl(struct inode *inode, int mask) ...@@ -1115,12 +1111,8 @@ static int reiserfs_check_acl(struct inode *inode, int mask)
struct posix_acl *acl; struct posix_acl *acl;
int error = -EAGAIN; /* do regular unix permission checks by default */ int error = -EAGAIN; /* do regular unix permission checks by default */
reiserfs_read_lock_xattr_i(inode);
acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS); acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
reiserfs_read_unlock_xattr_i(inode);
if (acl) { if (acl) {
if (!IS_ERR(acl)) { if (!IS_ERR(acl)) {
error = posix_acl_permission(inode, acl, mask); error = posix_acl_permission(inode, acl, mask);
......
...@@ -418,9 +418,7 @@ int reiserfs_cache_default_acl(struct inode *inode) ...@@ -418,9 +418,7 @@ int reiserfs_cache_default_acl(struct inode *inode)
int ret = 0; int ret = 0;
if (reiserfs_posixacl(inode->i_sb) && !IS_PRIVATE(inode)) { if (reiserfs_posixacl(inode->i_sb) && !IS_PRIVATE(inode)) {
struct posix_acl *acl; struct posix_acl *acl;
reiserfs_read_lock_xattr_i(inode);
acl = reiserfs_get_acl(inode, ACL_TYPE_DEFAULT); acl = reiserfs_get_acl(inode, ACL_TYPE_DEFAULT);
reiserfs_read_unlock_xattr_i(inode);
ret = (acl && !IS_ERR(acl)); ret = (acl && !IS_ERR(acl));
if (ret) if (ret)
posix_acl_release(acl); posix_acl_release(acl);
...@@ -452,11 +450,8 @@ int reiserfs_acl_chmod(struct inode *inode) ...@@ -452,11 +450,8 @@ int reiserfs_acl_chmod(struct inode *inode)
if (!clone) if (!clone)
return -ENOMEM; return -ENOMEM;
error = posix_acl_chmod_masq(clone, inode->i_mode); error = posix_acl_chmod_masq(clone, inode->i_mode);
if (!error) { if (!error)
reiserfs_write_lock_xattr_i(inode);
error = reiserfs_set_acl(inode, ACL_TYPE_ACCESS, clone); error = reiserfs_set_acl(inode, ACL_TYPE_ACCESS, clone);
reiserfs_write_unlock_xattr_i(inode);
}
posix_acl_release(clone); posix_acl_release(clone);
return error; return error;
} }
......
...@@ -59,7 +59,7 @@ struct reiserfs_inode_info { ...@@ -59,7 +59,7 @@ struct reiserfs_inode_info {
struct posix_acl *i_acl_default; struct posix_acl *i_acl_default;
#endif #endif
#ifdef CONFIG_REISERFS_FS_XATTR #ifdef CONFIG_REISERFS_FS_XATTR
struct rw_semaphore xattr_sem; struct rw_semaphore i_xattr_sem;
#endif #endif
struct inode vfs_inode; struct inode vfs_inode;
}; };
......
...@@ -67,24 +67,6 @@ extern struct reiserfs_xattr_handler user_handler; ...@@ -67,24 +67,6 @@ extern struct reiserfs_xattr_handler user_handler;
extern struct reiserfs_xattr_handler trusted_handler; extern struct reiserfs_xattr_handler trusted_handler;
extern struct reiserfs_xattr_handler security_handler; extern struct reiserfs_xattr_handler security_handler;
static inline void reiserfs_write_lock_xattr_i(struct inode *inode)
{
down_write(&REISERFS_I(inode)->i_xattr_sem);
}
static inline void reiserfs_write_unlock_xattr_i(struct inode *inode)
{
up_write(&REISERFS_I(inode)->i_xattr_sem);
}
static inline void reiserfs_read_lock_xattr_i(struct inode *inode)
{
down_read(&REISERFS_I(inode)->i_xattr_sem);
}
static inline void reiserfs_read_unlock_xattr_i(struct inode *inode)
{
up_read(&REISERFS_I(inode)->i_xattr_sem);
}
static inline void reiserfs_init_xattr_rwsem(struct inode *inode) static inline void reiserfs_init_xattr_rwsem(struct inode *inode)
{ {
init_rwsem(&REISERFS_I(inode)->i_xattr_sem); init_rwsem(&REISERFS_I(inode)->i_xattr_sem);
...@@ -96,10 +78,6 @@ static inline void reiserfs_init_xattr_rwsem(struct inode *inode) ...@@ -96,10 +78,6 @@ static inline void reiserfs_init_xattr_rwsem(struct inode *inode)
#define reiserfs_setxattr NULL #define reiserfs_setxattr NULL
#define reiserfs_listxattr NULL #define reiserfs_listxattr NULL
#define reiserfs_removexattr NULL #define reiserfs_removexattr NULL
#define reiserfs_write_lock_xattrs(sb) do {;} while(0)
#define reiserfs_write_unlock_xattrs(sb) do {;} while(0)
#define reiserfs_read_lock_xattrs(sb)
#define reiserfs_read_unlock_xattrs(sb)
#define reiserfs_permission NULL #define reiserfs_permission NULL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment