support for parallel directory operations for VFS diff -puN fs/namei.c~vfs-pdirops fs/namei.c --- linux-2.5.73/fs/namei.c~vfs-pdirops Mon Jul 7 00:00:25 2003 +++ linux-2.5.73-alexey/fs/namei.c Mon Jul 7 00:00:25 2003 @@ -27,6 +27,7 @@ #include #include #include +#include #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) @@ -108,6 +109,37 @@ * POSIX.1 2.4: an empty pathname is invalid (ENOENT). * PATH_MAX includes the nul terminator --RR. */ + +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_KERNEL); +} + +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); +} + static inline int do_getname(const char __user *filename, char *page) { int retval; @@ -340,8 +372,9 @@ static struct dentry * real_lookup(struc { 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.. @@ -367,7 +400,7 @@ static struct dentry * real_lookup(struc else result = dentry; } - up(&dir->i_sem); + unlock_dir(dir, lock); return result; } @@ -375,7 +408,7 @@ static struct dentry * real_lookup(struc * 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); @@ -1056,12 +1089,19 @@ static inline int lookup_flags(unsigned /* * p1 and p2 should be directories on the same fs. */ -struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) +struct dentry *lock_rename(struct nameidata *newnd, struct nameidata *oldnd) { + struct dentry *p1 = newnd->dentry; + struct dentry *p2 = oldnd->dentry; struct dentry *p; if (p1 == p2) { - down(&p1->d_inode->i_sem); + if (!IS_PDIROPS(p1->d_inode)) { + down(&p1->d_inode->i_sem); + return NULL; + } + oldnd->lock = lock_dir(p2->d_inode, &oldnd->last); + newnd->lock = lock_dir(p1->d_inode, &newnd->last); return NULL; } @@ -1069,32 +1109,42 @@ struct dentry *lock_rename(struct dentry for (p = p1; p->d_parent != p; p = p->d_parent) { if (p->d_parent == p2) { - down(&p2->d_inode->i_sem); - down(&p1->d_inode->i_sem); + oldnd->lock = lock_dir(p2->d_inode, &oldnd->last); + newnd->lock = lock_dir(p1->d_inode, &newnd->last); return p; } } for (p = p2; p->d_parent != p; p = p->d_parent) { if (p->d_parent == p1) { - down(&p1->d_inode->i_sem); - down(&p2->d_inode->i_sem); + newnd->lock = lock_dir(p1->d_inode, &newnd->last); + oldnd->lock = lock_dir(p2->d_inode, &oldnd->last); return p; } } - down(&p1->d_inode->i_sem); - down(&p2->d_inode->i_sem); + newnd->lock = lock_dir(p1->d_inode, &newnd->last); + oldnd->lock = lock_dir(p2->d_inode, &oldnd->last); return NULL; } -void unlock_rename(struct dentry *p1, struct dentry *p2) +void unlock_rename(struct nameidata *newnd, struct nameidata *oldnd) { - up(&p1->d_inode->i_sem); - if (p1 != p2) { - up(&p2->d_inode->i_sem); - up(&p1->d_inode->i_sb->s_vfs_rename_sem); - } + struct dentry *p1 = newnd->dentry; + struct dentry *p2 = oldnd->dentry; + + if (p1 == p2) { + if (!IS_PDIROPS(p1->d_inode)) { + up(&p1->d_inode->i_sem); + return; + } + unlock_dir(p2->d_inode, oldnd->lock); + unlock_dir(p1->d_inode, newnd->lock); + return; + } + unlock_dir(p1->d_inode, newnd->lock); + unlock_dir(p2->d_inode, oldnd->lock); + up(&p1->d_inode->i_sb->s_vfs_rename_sem); } int vfs_create(struct inode *dir, struct dentry *dentry, int mode) @@ -1214,6 +1264,7 @@ int open_namei(const char * pathname, in struct dentry *dentry; struct dentry *dir; int count = 0; + void *lock; acc_mode = ACC_MODE(flag); @@ -1250,13 +1301,13 @@ int open_namei(const char * pathname, in goto exit; dir = nd->dentry; - down(&dir->d_inode->i_sem); + lock = lock_dir(dir->d_inode, &nd->last); dentry = lookup_hash(&nd->last, nd->dentry); do_last: error = PTR_ERR(dentry); if (IS_ERR(dentry)) { - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, lock); goto exit; } @@ -1265,7 +1316,7 @@ do_last: if (!IS_POSIXACL(dir->d_inode)) mode &= ~current->fs->umask; error = vfs_create(dir->d_inode, dentry, mode); - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, lock); dput(nd->dentry); nd->dentry = dentry; if (error) @@ -1279,7 +1330,7 @@ do_last: /* * It already exists. */ - up(&dir->d_inode->i_sem); + unlock_dir(dir->d_inode, lock); error = -EEXIST; if (flag & O_EXCL) @@ -1353,7 +1404,7 @@ do_link: goto exit; } dir = nd->dentry; - down(&dir->d_inode->i_sem); + lock = lock_dir(dir->d_inode, &nd->last); dentry = lookup_hash(&nd->last, nd->dentry); putname(nd->last.name); goto do_last; @@ -1364,7 +1415,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; @@ -1444,7 +1495,7 @@ asmlinkage long sys_mknod(const char __u } dput(dentry); } - up(&nd.dentry->d_inode->i_sem); + unlock_dir(nd.dentry->d_inode, nd.lock); path_release(&nd); out: putname(tmp); @@ -1498,7 +1549,7 @@ asmlinkage long sys_mkdir(const char __u 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); path_release(&nd); out: putname(tmp); @@ -1542,6 +1593,7 @@ static void d_unhash(struct dentry *dent int vfs_rmdir(struct inode *dir, struct dentry *dentry) { int error = may_delete(dir, dentry, 1); + void *lock; if (error) return error; @@ -1551,7 +1603,7 @@ int vfs_rmdir(struct inode *dir, struct DQUOT_INIT(dir); - down(&dentry->d_inode->i_sem); + lock = lock_dir(dentry->d_inode, &dentry->d_name); d_unhash(dentry); if (d_mountpoint(dentry)) error = -EBUSY; @@ -1563,7 +1615,7 @@ int vfs_rmdir(struct inode *dir, struct dentry->d_inode->i_flags |= S_DEAD; } } - up(&dentry->d_inode->i_sem); + unlock_dir(dentry->d_inode, lock); if (!error) { inode_dir_notify(dir, DN_DELETE); d_delete(dentry); @@ -1599,14 +1651,14 @@ asmlinkage long sys_rmdir(const char __u 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: @@ -1617,6 +1669,7 @@ exit: int vfs_unlink(struct inode *dir, struct dentry *dentry) { int error = may_delete(dir, dentry, 0); + void *lock; if (error) return error; @@ -1626,7 +1679,7 @@ int vfs_unlink(struct inode *dir, struct DQUOT_INIT(dir); - down(&dentry->d_inode->i_sem); + lock = lock_dir(dentry->d_inode, &dentry->d_name); if (d_mountpoint(dentry)) error = -EBUSY; else { @@ -1634,7 +1687,7 @@ int vfs_unlink(struct inode *dir, struct if (!error) error = dir->i_op->unlink(dir, dentry); } - up(&dentry->d_inode->i_sem); + unlock_dir(dentry->d_inode, lock); /* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { @@ -1668,7 +1721,7 @@ asmlinkage long sys_unlink(const char __ 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)) { @@ -1682,7 +1735,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: @@ -1745,7 +1798,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); path_release(&nd); out: putname(to); @@ -1758,6 +1811,7 @@ int vfs_link(struct dentry *old_dentry, { struct inode *inode = old_dentry->d_inode; int error; + void *lock; if (!inode) return -ENOENT; @@ -1783,10 +1837,10 @@ int vfs_link(struct dentry *old_dentry, if (error) return error; - down(&old_dentry->d_inode->i_sem); + lock = lock_dir(old_dentry->d_inode, &old_dentry->d_name); DQUOT_INIT(dir); error = dir->i_op->link(old_dentry, dir, new_dentry); - up(&old_dentry->d_inode->i_sem); + unlock_dir(old_dentry->d_inode, lock); if (!error) { inode_dir_notify(dir, DN_CREATE); security_inode_post_link(old_dentry, dir, new_dentry); @@ -1829,7 +1883,7 @@ asmlinkage long sys_link(const char __us 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: @@ -2017,7 +2071,7 @@ static inline int do_rename(const char * if (newnd.last_type != LAST_NORM) goto exit2; - trap = lock_rename(new_dir, old_dir); + trap = lock_rename(&newnd, &oldnd); old_dentry = lookup_hash(&oldnd.last, old_dir); error = PTR_ERR(old_dentry); @@ -2055,7 +2109,7 @@ exit5: exit4: dput(old_dentry); exit3: - unlock_rename(new_dir, old_dir); + unlock_rename(&newnd, &oldnd); exit2: path_release(&newnd); exit1: diff -puN include/linux/fs.h~vfs-pdirops include/linux/fs.h --- linux-2.5.73/include/linux/fs.h~vfs-pdirops Mon Jul 7 00:00:25 2003 +++ linux-2.5.73-alexey/include/linux/fs.h Mon Jul 7 00:00:25 2003 @@ -19,6 +19,7 @@ #include #include #include +#include #include struct iovec; @@ -136,6 +137,7 @@ extern int leases_enable, dir_notify_ena #define S_DEAD 32 /* removed, but still open directory */ #define S_NOQUOTA 64 /* Inode is not counted to quota */ #define S_DIRSYNC 128 /* Directory modifications are synchronous */ +#define S_PDIROPS 256 /* Parallel directory operations */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -167,6 +169,7 @@ extern int leases_enable, dir_notify_ena #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) @@ -396,6 +399,7 @@ struct inode { atomic_t i_writecount; void *i_security; __u32 i_generation; + struct dynlock i_dcache_lock; /* for parallel directory ops */ union { void *generic_ip; } u; diff -puN include/linux/namei.h~vfs-pdirops include/linux/namei.h --- linux-2.5.73/include/linux/namei.h~vfs-pdirops Mon Jul 7 00:00:25 2003 +++ linux-2.5.73-alexey/include/linux/namei.h Mon Jul 7 00:00:25 2003 @@ -11,6 +11,7 @@ struct nameidata { struct qstr last; unsigned int flags; int last_type; + void *lock; }; /* @@ -49,7 +50,7 @@ extern struct dentry * lookup_hash(struc extern int follow_down(struct vfsmount **, struct dentry **); extern int follow_up(struct vfsmount **, struct dentry **); -extern struct dentry *lock_rename(struct dentry *, struct dentry *); -extern void unlock_rename(struct dentry *, struct dentry *); +extern struct dentry *lock_rename(struct nameidata *, struct nameidata *); +extern void unlock_rename(struct nameidata *, struct nameidata *); #endif /* _LINUX_NAMEI_H */ diff -puN fs/inode.c~vfs-pdirops fs/inode.c --- linux-2.5.73/fs/inode.c~vfs-pdirops Mon Jul 7 00:00:25 2003 +++ linux-2.5.73-alexey/fs/inode.c Mon Jul 7 00:00:25 2003 @@ -19,6 +19,7 @@ #include #include #include +#include /* * This is needed for the following functions: @@ -149,6 +150,7 @@ static struct inode *alloc_inode(struct mapping->backing_dev_info = sb->s_bdev->bd_inode->i_mapping->backing_dev_info; memset(&inode->u, 0, sizeof(inode->u)); inode->i_mapping = mapping; + dynlock_init(&inode->i_dcache_lock); } return inode; } _