From: Andreas Gruenbacher Andrew Morton found that there is lock contention between extended attribute operations (like reading ACLs, which `ls -l' needs to do) and other operations on the same files. This is due to the fact that all extended attribute syscalls take inode->i_sem before calling into the filesystem code. To fix this problem, this patch no longer takes inode->i_sem in the getxattr and listxattr syscalls, and moves the lock taking code into the file systems. (Another patch improves the locking strategy in ext2 and ext3.) Documentation/filesystems/Locking | 4 ++-- fs/ext2/xattr.c | 15 +++++++++++++-- fs/ext3/xattr.c | 15 +++++++++++++-- fs/jfs/xattr.c | 22 ++++++++++++++++++++-- fs/xattr.c | 4 ---- fs/xfs/linux/xfs_iops.c | 34 ++++++++++++++++++++++++++++++++-- 6 files changed, 80 insertions(+), 14 deletions(-) diff -puN Documentation/filesystems/Locking~xattr-fine-grained-locking-prep Documentation/filesystems/Locking --- 25/Documentation/filesystems/Locking~xattr-fine-grained-locking-prep 2003-07-04 22:16:53.000000000 -0700 +++ 25-akpm/Documentation/filesystems/Locking 2003-07-04 22:16:53.000000000 -0700 @@ -68,8 +68,8 @@ setattr: yes permission: no getattr: no setxattr: yes -getxattr: yes -listxattr: yes +getxattr: no +listxattr: no removexattr: yes Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_sem on victim. diff -puN fs/ext2/xattr.c~xattr-fine-grained-locking-prep fs/ext2/xattr.c --- 25/fs/ext2/xattr.c~xattr-fine-grained-locking-prep 2003-07-04 22:16:53.000000000 -0700 +++ 25-akpm/fs/ext2/xattr.c 2003-07-04 22:16:53.000000000 -0700 @@ -204,11 +204,16 @@ ext2_getxattr(struct dentry *dentry, con { struct ext2_xattr_handler *handler; struct inode *inode = dentry->d_inode; + ssize_t error; handler = ext2_xattr_resolve_name(&name); if (!handler) return -EOPNOTSUPP; - return handler->get(inode, name, buffer, size); + down(&inode->i_sem); + error = handler->get(inode, name, buffer, size); + up(&inode->i_sem); + + return error; } /* @@ -219,7 +224,13 @@ ext2_getxattr(struct dentry *dentry, con ssize_t ext2_listxattr(struct dentry *dentry, char *buffer, size_t size) { - return ext2_xattr_list(dentry->d_inode, buffer, size); + ssize_t error; + + down(&dentry->d_inode->i_sem); + error = ext2_xattr_list(dentry->d_inode, buffer, size); + up(&dentry->d_inode->i_sem); + + return error; } /* diff -puN fs/ext3/xattr.c~xattr-fine-grained-locking-prep fs/ext3/xattr.c --- 25/fs/ext3/xattr.c~xattr-fine-grained-locking-prep 2003-07-04 22:16:53.000000000 -0700 +++ 25-akpm/fs/ext3/xattr.c 2003-07-04 22:16:53.000000000 -0700 @@ -199,11 +199,16 @@ ext3_getxattr(struct dentry *dentry, con { struct ext3_xattr_handler *handler; struct inode *inode = dentry->d_inode; + ssize_t error; handler = ext3_xattr_resolve_name(&name); if (!handler) return -EOPNOTSUPP; - return handler->get(inode, name, buffer, size); + down(&inode->i_sem); + error = handler->get(inode, name, buffer, size); + up(&inode->i_sem); + + return error; } /* @@ -214,7 +219,13 @@ ext3_getxattr(struct dentry *dentry, con ssize_t ext3_listxattr(struct dentry *dentry, char *buffer, size_t size) { - return ext3_xattr_list(dentry->d_inode, buffer, size); + ssize_t error; + + down(&dentry->d_inode->i_sem); + error = ext3_xattr_list(dentry->d_inode, buffer, size); + up(&dentry->d_inode->i_sem); + + return error; } /* diff -puN fs/jfs/xattr.c~xattr-fine-grained-locking-prep fs/jfs/xattr.c --- 25/fs/jfs/xattr.c~xattr-fine-grained-locking-prep 2003-07-04 22:16:53.000000000 -0700 +++ 25-akpm/fs/jfs/xattr.c 2003-07-04 22:16:53.000000000 -0700 @@ -964,10 +964,17 @@ ssize_t __jfs_getxattr(struct inode *ino ssize_t jfs_getxattr(struct dentry *dentry, const char *name, void *data, size_t buf_size) { - return __jfs_getxattr(dentry->d_inode, name, data, buf_size); + int err; + + down(&dentry->d_inode->i_sem); + err = __jfs_getxattr(dentry->d_inode, name, data, buf_size); + up(&dentry->d_inode->i_sem); + + return err; } -ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) +static ssize_t __jfs_listxattr(struct dentry * dentry, char *data, + size_t buf_size) { struct inode *inode = dentry->d_inode; char *buffer; @@ -1013,6 +1020,17 @@ ssize_t jfs_listxattr(struct dentry * de return size; } +ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) +{ + int err; + + down(&dentry->d_inode->i_sem); + err = __jfs_listxattr(dentry, data, buf_size); + up(&dentry->d_inode->i_sem); + + return err; +} + int jfs_removexattr(struct dentry *dentry, const char *name) { return __jfs_setxattr(dentry->d_inode, name, 0, 0, XATTR_REPLACE); diff -puN fs/xattr.c~xattr-fine-grained-locking-prep fs/xattr.c --- 25/fs/xattr.c~xattr-fine-grained-locking-prep 2003-07-04 22:16:53.000000000 -0700 +++ 25-akpm/fs/xattr.c 2003-07-04 22:16:53.000000000 -0700 @@ -160,9 +160,7 @@ getxattr(struct dentry *d, char *name, v error = security_inode_getxattr(d, kname); if (error) goto out; - down(&d->d_inode->i_sem); error = d->d_inode->i_op->getxattr(d, kname, kvalue, size); - up(&d->d_inode->i_sem); } if (kvalue && error > 0) @@ -233,9 +231,7 @@ listxattr(struct dentry *d, char *list, error = security_inode_listxattr(d); if (error) goto out; - down(&d->d_inode->i_sem); error = d->d_inode->i_op->listxattr(d, klist, size); - up(&d->d_inode->i_sem); } if (klist && error > 0) diff -puN fs/xfs/linux/xfs_iops.c~xattr-fine-grained-locking-prep fs/xfs/linux/xfs_iops.c --- 25/fs/xfs/linux/xfs_iops.c~xattr-fine-grained-locking-prep 2003-07-04 22:16:53.000000000 -0700 +++ 25-akpm/fs/xfs/linux/xfs_iops.c 2003-07-04 22:16:53.000000000 -0700 @@ -642,7 +642,7 @@ linvfs_setxattr( } STATIC ssize_t -linvfs_getxattr( +__linvfs_getxattr( struct dentry *dentry, const char *name, void *data, @@ -697,9 +697,24 @@ linvfs_getxattr( return -EOPNOTSUPP; } +STATIC ssize_t +linvfs_getxattr( + struct dentry *dentry, + const char *name, + void *data, + size_t size) +{ + int error; + + down(&dentry->d_inode->i_sem); + error = __linvfs_getxattr(dentry, name, data, size); + up(&dentry->d_inode->i_sem); + + return error; +} STATIC ssize_t -linvfs_listxattr( +__linvfs_listxattr( struct dentry *dentry, char *data, size_t size) @@ -741,6 +756,21 @@ linvfs_listxattr( return result; } +STATIC ssize_t +linvfs_listxattr( + struct dentry *dentry, + char *data, + size_t size) +{ + int error; + + down(&dentry->d_inode->i_sem); + error = __linvfs_listxattr(dentry, data, size); + up(&dentry->d_inode->i_sem); + + return error; +} + STATIC int linvfs_removexattr( struct dentry *dentry, _