From 607eff9b7cb03edeaca42e0f38a4c0bf1567fc0d Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 15 Feb 2017 08:49:47 -0800 Subject: [PATCH] Add range locking to xattr ops We can use easy xattrs to test range locking and item consistency between mounts. Signed-off-by: Zach Brown --- kmod/src/xattr.c | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/kmod/src/xattr.c b/kmod/src/xattr.c index afe1cc14..c3d10c2c 100644 --- a/kmod/src/xattr.c +++ b/kmod/src/xattr.c @@ -23,6 +23,7 @@ #include "item.h" #include "trans.h" #include "xattr.h" +#include "lock.h" /* * In the simple case an xattr is stored in a single item whose key and @@ -149,7 +150,9 @@ ssize_t scoutfs_getxattr(struct dentry *dentry, const char *name, void *buffer, struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct scoutfs_xattr_val_header vh; struct scoutfs_key_buf *key = NULL; + struct scoutfs_key_buf *last = NULL; SCOUTFS_DECLARE_KVEC(val); + struct scoutfs_lock lck; unsigned int total; unsigned int bytes; unsigned int off; @@ -169,8 +172,15 @@ ssize_t scoutfs_getxattr(struct dentry *dentry, const char *name, void *buffer, return SCOUTFS_XATTR_MAX_SIZE; key = alloc_xattr_key(sb, scoutfs_ino(inode), name, name_len, 0); - if (!key) - return -ENOMEM; + last = alloc_xattr_key(sb, scoutfs_ino(inode), name, name_len, 0xff); + if (!key || !last) { + ret = -ENOMEM; + goto out; + } + + ret = scoutfs_lock_range(sb, SCOUTFS_LOCK_MODE_READ, key, last, &lck); + if (ret) + goto out; down_read(&si->xattr_rwsem); @@ -219,8 +229,11 @@ ssize_t scoutfs_getxattr(struct dentry *dentry, const char *name, void *buffer, ret = -ERANGE; up_read(&si->xattr_rwsem); - scoutfs_key_free(sb, key); + scoutfs_unlock_range(sb, &lck); +out: + scoutfs_key_free(sb, key); + scoutfs_key_free(sb, last); return ret; } @@ -250,6 +263,7 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name, struct scoutfs_xattr_val_header vh; size_t name_len = strlen(name); SCOUTFS_DECLARE_KVEC(val); + struct scoutfs_lock lck; unsigned int bytes; unsigned int off; LIST_HEAD(list); @@ -274,6 +288,10 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name, goto out; } + ret = scoutfs_lock_range(sb, SCOUTFS_LOCK_MODE_WRITE, key, last, &lck); + if (ret) + goto out; + /* build up batch of new items for the new xattr */ if (value) { for_each_xattr_item(key, val, &vh, (void *)value, size, @@ -281,7 +299,7 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name, ret = scoutfs_item_add_batch(sb, &list, key, val); if (ret) - goto out; + goto unlock; } } @@ -299,7 +317,7 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name, ret = scoutfs_hold_trans(sb); if (ret) - goto out; + goto unlock; down_write(&si->xattr_rwsem); @@ -315,6 +333,9 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name, up_write(&si->xattr_rwsem); scoutfs_release_trans(sb); +unlock: + scoutfs_unlock_range(sb, &lck); + out: scoutfs_item_free_batch(sb, &list); scoutfs_key_free(sb, key); @@ -346,6 +367,7 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size) struct scoutfs_xattr_key *xkey; struct scoutfs_key_buf *key; struct scoutfs_key_buf *last; + struct scoutfs_lock lck; ssize_t total; int name_len; int ret; @@ -362,6 +384,10 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size) xkey = key->data; xkey->name[0] = '\0'; + ret = scoutfs_lock_range(sb, SCOUTFS_LOCK_MODE_READ, key, last, &lck); + if (ret) + goto out; + down_read(&si->xattr_rwsem); total = 0; @@ -408,6 +434,7 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size) } up_read(&si->xattr_rwsem); + scoutfs_unlock_range(sb, &lck); out: scoutfs_key_free(sb, key); scoutfs_key_free(sb, last); @@ -428,6 +455,7 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino) { struct scoutfs_key_buf *key; struct scoutfs_key_buf *last; + struct scoutfs_lock lck; int ret; key = alloc_xattr_key(sb, ino, NULL, SCOUTFS_XATTR_MAX_NAME_LEN, 0); @@ -438,6 +466,11 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino) goto out; } + /* while we read to delete we need to writeback others */ + ret = scoutfs_lock_range(sb, SCOUTFS_LOCK_MODE_WRITE, key, last, &lck); + if (ret) + goto out; + /* the inode is dead so we don't need the xattr sem */ for (;;) { @@ -455,6 +488,8 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino) /* don't need to increment past deleted key */ } + scoutfs_unlock_range(sb, &lck); + out: scoutfs_key_free(sb, key); scoutfs_key_free(sb, last);