fs/inode.c | 1 fs/namei.c | 65 +++++++++++++++++++++++++++++++++++++++-------------- include/linux/fs.h | 11 ++++---- 3 files changed, 54 insertions(+), 23 deletions(-) --- linux-2.4.18-chaos/fs/namei.c~vfs-pdirops-2.4.18-chaos 2003-09-16 23:33:47.000000000 +0400 +++ linux-2.4.18-chaos-alexey/fs/namei.c 2003-09-17 00:18:45.000000000 +0400 @@ -101,6 +101,36 @@ void intent_release(struct lookup_intent } +static void *lock_dir(struct inode *dir, struct qstr *name) +{ + unsigned long hash; + + if (!IS_PDIROPS(dir)) { + down(&dir->i_sem); + return 0; + } + + /* OK. fs understands parallel directory operations. + * so, we try to acquire lock for hash of requested + * filename in order to prevent any operations with + * same name in same time -bzzz */ + + /* calculate name hash */ + hash = full_name_hash(name->name, name->len); + + /* lock this hash */ + return dynlock_lock(&dir->i_dcache_lock, hash, 1, GFP_ATOMIC); +} + +static void unlock_dir(struct inode *dir, void *lock) +{ + if (!IS_PDIROPS(dir)) { + up(&dir->i_sem); + return; + } + dynlock_unlock(&dir->i_dcache_lock, lock); +} + /* In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the * kernel data space before using them.. @@ -303,10 +333,11 @@ static struct dentry *real_lookup(struct struct dentry * result; struct inode *dir = parent->d_inode; int counter = 0; + void *lock; again: + lock = lock_dir(dir, name); counter++; - down(&dir->i_sem); /* * First re-do the cached lookup just in case it was created * while we waited for the directory semaphore.. @@ -330,7 +361,7 @@ again: else result = dentry; } - up(&dir->i_sem); + unlock_dir(dir, lock); return result; } @@ -338,7 +369,7 @@ again: * Uhhuh! Nasty case: the cache was re-populated while * we waited on the semaphore. Need to revalidate. */ - up(&dir->i_sem); + unlock_dir(dir, lock); if (result->d_op && result->d_op->d_revalidate) { if (!result->d_op->d_revalidate(result, flags) && !d_invalidate(result)) { dput(result); @@ -1240,13 +1271,13 @@ struct file *filp_open(const char * path goto exit; dir = nd.dentry; - down(&dir->d_inode->i_sem); + nd.lock = lock_dir(dir->d_inode, &nd.last); dentry = lookup_hash_it(&nd.last, nd.dentry, &it); do_last: error = PTR_ERR(dentry); if (IS_ERR(dentry)) { - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, nd.lock); goto exit; } @@ -1255,7 +1286,7 @@ do_last: if (!dentry->d_inode) { error = vfs_create_it(dir->d_inode, dentry, mode & ~current->fs->umask, &it); - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, nd.lock); dput(nd.dentry); nd.dentry = dentry; if (error) @@ -1270,7 +1301,7 @@ do_last: /* * It already exists. */ - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, nd.lock); error = -EEXIST; if (flag & O_EXCL) @@ -1350,7 +1381,7 @@ do_link: goto exit; } dir = nd.dentry; - down(&dir->d_inode->i_sem); + nd.lock = lock_dir(dir->d_inode, &nd.last); dentry = lookup_hash_it(&nd.last, nd.dentry, &it); putname(nd.last.name); goto do_last; @@ -1363,7 +1394,7 @@ static struct dentry *lookup_create(stru { struct dentry *dentry; - down(&nd->dentry->d_inode->i_sem); + nd->lock = lock_dir(nd->dentry->d_inode, &nd->last); dentry = ERR_PTR(-EEXIST); if (nd->last_type != LAST_NORM) goto fail; @@ -1452,7 +1483,7 @@ asmlinkage long sys_mknod(const char * f } dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); out2: path_release(&nd); out: @@ -1515,7 +1546,7 @@ asmlinkage long sys_mkdir(const char * p mode & ~current->fs->umask); dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); out2: path_release(&nd); out: @@ -1625,14 +1656,14 @@ asmlinkage long sys_rmdir(const char * p if (error != -EOPNOTSUPP) goto exit1; } - down(&nd.dentry->d_inode->i_sem); + nd.lock = lock_dir(nd.dentry->d_inode, &nd.last); dentry = lookup_hash_it(&nd.last, nd.dentry, NULL); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { error = vfs_rmdir(nd.dentry->d_inode, dentry); dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); exit1: path_release(&nd); exit: @@ -1691,7 +1722,7 @@ asmlinkage long sys_unlink(const char * if (error != -EOPNOTSUPP) goto exit1; } - down(&nd.dentry->d_inode->i_sem); + nd.lock = lock_dir(nd.dentry->d_inode, &nd.last); dentry = lookup_hash_it(&nd.last, nd.dentry, NULL); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { @@ -1702,7 +1733,7 @@ asmlinkage long sys_unlink(const char * exit2: dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); exit1: path_release(&nd); exit: @@ -1772,7 +1803,7 @@ asmlinkage long sys_symlink(const char * error = vfs_symlink(nd.dentry->d_inode, dentry, from); dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); out2: path_release(&nd); out: @@ -1864,7 +1895,7 @@ asmlinkage long sys_link(const char * ol error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); dput(new_dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); out_release: path_release(&nd); out: --- linux-2.4.18-chaos/include/linux/fs.h~vfs-pdirops-2.4.18-chaos 2003-09-16 23:33:47.000000000 +0400 +++ linux-2.4.18-chaos-alexey/include/linux/fs.h 2003-09-17 00:16:08.000000000 +0400 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -136,6 +137,7 @@ extern int leases_enable, dir_notify_ena #define S_IMMUTABLE 16 /* Immutable file */ #define S_DEAD 32 /* removed, but still open directory */ #define S_NOQUOTA 64 /* Inode is not counted to quota */ +#define S_PDIROPS 256 /* Parallel directory operations */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -162,6 +164,7 @@ extern int leases_enable, dir_notify_ena #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_NOATIME(inode) (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME)) #define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME) +#define IS_PDIROPS(inode) __IS_FLG(inode, S_PDIROPS) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) @@ -491,6 +494,7 @@ struct inode { atomic_t i_writecount; unsigned int i_attr_flags; __u32 i_generation; + struct dynlock i_dcache_lock; /* for parallel directory ops */ union { struct minix_inode_info minix_i; struct ext2_inode_info ext2_i; @@ -714,6 +718,7 @@ struct nameidata { unsigned int flags; int last_type; struct lookup_intent *intent; + void *lock; }; #define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */ @@ -1611,12 +1616,6 @@ static inline struct dentry *get_parent( return dget(dentry->d_parent); } -static inline void unlock_dir(struct dentry *dir) -{ - up(&dir->d_inode->i_sem); - dput(dir); -} - /* * Whee.. Deadlock country. Happily there are only two VFS * operations that does this.. --- linux-2.4.18-chaos/fs/inode.c~vfs-pdirops-2.4.18-chaos 2003-09-16 23:33:48.000000000 +0400 +++ linux-2.4.18-chaos-alexey/fs/inode.c 2003-09-16 23:47:45.000000000 +0400 @@ -119,6 +119,7 @@ static struct inode *alloc_inode(struct mapping->host = inode; mapping->gfp_mask = GFP_HIGHUSER; inode->i_mapping = mapping; + dynlock_init(&inode->i_dcache_lock); } return inode; } _