Use the RCU list mechanism instead of a simple lock to access/modify simple_xattrs. The performance benefit is probably negligible, but it will help avoid lock nesting concerns for an upcoming patch. Signed-off-by: Ondrej Mosnacek <omosnace@xxxxxxxxxx> --- fs/xattr.c | 36 ++++++++++++++++++++++-------------- include/linux/xattr.h | 1 + 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/fs/xattr.c b/fs/xattr.c index a1f4998bc6be..fad2344f1168 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -22,6 +22,8 @@ #include <linux/audit.h> #include <linux/vmalloc.h> #include <linux/posix_acl_xattr.h> +#include <linux/rculist.h> +#include <linux/rcupdate.h> #include <linux/uaccess.h> @@ -1030,8 +1032,8 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, struct simple_xattr *xattr; int ret = -ENODATA; - spin_lock(&xattrs->lock); - list_for_each_entry(xattr, &xattrs->head, list) { + rcu_read_lock(); + list_for_each_entry_rcu(xattr, &xattrs->head, list) { if (strcmp(name, xattr->name)) continue; @@ -1044,10 +1046,18 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, } break; } - spin_unlock(&xattrs->lock); + rcu_read_unlock(); return ret; } +static void simple_xattr_free_rcu(struct rcu_head *rcu) +{ + struct simple_xattr *xattr = container_of(rcu, struct simple_xattr, rcu); + + kfree(xattr->name); + kvfree(xattr); +} + /** * simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems * @xattrs: target simple_xattr list @@ -1094,11 +1104,11 @@ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name, xattr = new_xattr; err = -EEXIST; } else if (new_xattr) { - list_replace(&xattr->list, &new_xattr->list); + list_replace_rcu(&xattr->list, &new_xattr->list); if (removed_size) *removed_size = xattr->size; } else { - list_del(&xattr->list); + list_del_rcu(&xattr->list); if (removed_size) *removed_size = xattr->size; } @@ -1109,15 +1119,13 @@ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name, xattr = new_xattr; err = -ENODATA; } else { - list_add(&new_xattr->list, &xattrs->head); + list_add_rcu(&new_xattr->list, &xattrs->head); xattr = NULL; } out: spin_unlock(&xattrs->lock); - if (xattr) { - kfree(xattr->name); - kvfree(xattr); - } + if (xattr) + call_rcu(&xattr->rcu, simple_xattr_free_rcu); return err; } @@ -1169,8 +1177,8 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, } #endif - spin_lock(&xattrs->lock); - list_for_each_entry(xattr, &xattrs->head, list) { + rcu_read_lock(); + list_for_each_entry_rcu(xattr, &xattrs->head, list) { /* skip "trusted." attributes for unprivileged callers */ if (!trusted && xattr_is_trusted(xattr->name)) continue; @@ -1179,7 +1187,7 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, if (err) break; } - spin_unlock(&xattrs->lock); + rcu_read_unlock(); return err ? err : size - remaining_size; } @@ -1191,6 +1199,6 @@ void simple_xattr_list_add(struct simple_xattrs *xattrs, struct simple_xattr *new_xattr) { spin_lock(&xattrs->lock); - list_add(&new_xattr->list, &xattrs->head); + list_add_rcu(&new_xattr->list, &xattrs->head); spin_unlock(&xattrs->lock); } diff --git a/include/linux/xattr.h b/include/linux/xattr.h index 979a9d3e5bfb..3236c469aaac 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -86,6 +86,7 @@ struct simple_xattrs { struct simple_xattr { struct list_head list; + struct rcu_head rcu; char *name; size_t size; char value[]; -- 2.37.2