fs/inode.c | 1 fs/namei.c | 66 ++++++++++++++++++++++++++++++++++++++--------------- include/linux/fs.h | 11 ++++---- 3 files changed, 54 insertions(+), 24 deletions(-) Index: linux-2.6.10/fs/inode.c =================================================================== --- linux-2.6.10.orig/fs/inode.c 2004-12-25 05:35:40.000000000 +0800 +++ linux-2.6.10/fs/inode.c 2005-03-31 18:03:53.551688872 +0800 @@ -166,6 +166,7 @@ } memset(&inode->u, 0, sizeof(inode->u)); inode->i_mapping = mapping; + dynlock_init(&inode->i_dcache_lock); } return inode; } Index: linux-2.6.10/fs/namei.c =================================================================== --- linux-2.6.10.orig/fs/namei.c 2005-03-31 17:57:10.767921312 +0800 +++ linux-2.6.10/fs/namei.c 2005-03-31 18:05:52.839554360 +0800 @@ -104,6 +104,38 @@ * any extra contention... */ +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); +} +EXPORT_SYMBOL(lock_dir); + +void unlock_dir(struct inode *dir, void *lock) +{ + if (!IS_PDIROPS(dir)) { + up(&dir->i_sem); + return; + } + dynlock_unlock(&dir->i_dcache_lock, lock); +} +EXPORT_SYMBOL(unlock_dir); + /* 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.. @@ -390,8 +422,9 @@ { struct dentry * result; struct inode *dir = parent->d_inode; + void *lock; - down(&dir->i_sem); + lock = lock_dir(dir, name); /* * First re-do the cached lookup just in case it was created * while we waited for the directory semaphore.. @@ -417,7 +450,7 @@ else result = dentry; } - up(&dir->i_sem); + unlock_dir(dir, lock); return result; } @@ -425,7 +458,7 @@ * 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, nd) && !d_invalidate(result)) { dput(result); @@ -1461,7 +1494,7 @@ dir = nd->dentry; nd->flags &= ~LOOKUP_PARENT; - down(&dir->d_inode->i_sem); + nd->lock = lock_dir(dir->d_inode, &nd->last); nd->flags |= LOOKUP_LAST; dentry = __lookup_hash(&nd->last, nd->dentry, nd); nd->flags &= ~LOOKUP_LAST; @@ -1469,7 +1502,7 @@ 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; } @@ -1478,7 +1511,7 @@ if (!IS_POSIXACL(dir->d_inode)) mode &= ~current->fs->umask; error = vfs_create(dir->d_inode, dentry, mode, nd); - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, nd->lock); dput(nd->dentry); nd->dentry = dentry; if (error) @@ -1492,7 +1525,7 @@ /* * It already exists. */ - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, nd->lock); error = -EEXIST; if (flag & O_EXCL) @@ -1576,7 +1609,7 @@ goto exit; } dir = nd->dentry; - down(&dir->d_inode->i_sem); + nd->lock = lock_dir(dir->d_inode, &nd->last); nd->flags |= LOOKUP_LAST; dentry = __lookup_hash(&nd->last, nd->dentry, nd); nd->flags &= ~LOOKUP_LAST; @@ -1596,7 +1629,7 @@ { 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; @@ -1688,7 +1721,7 @@ } dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); out2: path_release(&nd); out: @@ -1747,7 +1780,7 @@ error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); out2: path_release(&nd); out: @@ -1852,14 +1885,14 @@ error = -EBUSY; goto exit1; } - down(&nd.dentry->d_inode->i_sem); + nd.lock = lock_dir(nd.dentry->d_inode, &nd.last); dentry = lookup_hash(&nd.last, nd.dentry); 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: @@ -1925,7 +1958,7 @@ error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; - down(&nd.dentry->d_inode->i_sem); + nd.lock = lock_dir(nd.dentry->d_inode, &nd.last); dentry = lookup_hash(&nd.last, nd.dentry); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { @@ -1939,7 +1972,7 @@ exit2: dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); if (inode) iput(inode); /* truncate the inode here */ exit1: @@ -2005,7 +2038,7 @@ error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO); dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); out2: path_release(&nd); out: @@ -2094,7 +2127,7 @@ 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: Index: linux-2.6.10/include/linux/fs.h =================================================================== --- linux-2.6.10.orig/include/linux/fs.h 2005-03-31 17:57:13.330531736 +0800 +++ linux-2.6.10/include/linux/fs.h 2005-03-31 18:08:59.645155592 +0800 @@ -19,6 +19,7 @@ #include #include #include +#include struct iovec; struct nameidata; @@ -151,7 +152,7 @@ #define S_DIRSYNC 64 /* Directory modifications are synchronous */ #define S_NOCMTIME 128 /* Do not update file c/mtime */ #define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */ - +#define S_PDIROPS 512 /* Parallel directory operations */ /* * Note that nosuid etc flags are inode-specific: setting some file-system * flags just means all the inodes inherit those flags by default. It might be @@ -181,6 +182,7 @@ #define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME) #define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL) #define IS_ONE_SECOND(inode) __IS_FLG(inode, MS_ONE_SECOND) +#define IS_PDIROPS(inode) __IS_FLG(inode, S_PDIROPS) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) #define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME) @@ -482,6 +484,7 @@ atomic_t i_writecount; void *i_security; + struct dynlock i_dcache_lock; /* for parallel directory ops */ union { void *generic_ip; } u; Index: linux-2.6.10/include/linux/namei.h =================================================================== --- linux-2.6.10.orig/include/linux/namei.h 2005-03-31 17:50:12.533502608 +0800 +++ linux-2.6.10/include/linux/namei.h 2005-03-31 18:10:30.237383480 +0800 @@ -63,7 +63,8 @@ int last_type; unsigned depth; char *saved_names[MAX_NESTED_LINKS + 1]; - + + void *lock; /* Intent data */ union { struct open_intent open; @@ -91,7 +92,7 @@ #define LOOKUP_ATOMIC 64 #define LOOKUP_LAST 128 #define LOOKUP_LINK_NOTLAST 256 -+ + /* * Intent data */