Skip to contentSkip to conversation

parallel directory operations

RFC In review Alex Tomas 3 participants 4 messages Jul 8, 2003

Subsystems (2)
lore
Share & tools
lore

Alex Tomas

author
#0 lore raw

RFC 1/1 parallel directory operations

hi all!

this time linux can't perform many operations in the single directory. following patches are intended to solve this limitation. locking is broken into two stages: at first we protect dcache locking 'part of directory' using hash, at second (inside ext3) we protect htree structure. in both cases I use dynamic locks. there are several know issues to be solved yet (say, this patches don't let several renames in single dir).

Designed by Peter Braam.

simple benchmarks:

creation of 500K files by two processes:

before: 1m41.744s
after:  1m28.837s

creation of 250K files by two processes:

before: 0m28.913s
after:  0m20.851s

creation of 500K hardlinks by two processes:

before: 1m3.126s
after:  0m48.669s

creation of 250K hardlinks by two processes:

before: 0m17.874s
after:  0m10.427s

I would to like to see any comments/suggestions.

with best regards, Alex

dynamic locks. supports exclusive and shared locks. exclusive lock may be taken several times by first owner.

include/linux/dynlocks.h new file +35 -0
diff -puN /dev/null include/linux/dynlocks.h
@@ -0,0 +1,35 @@
+#ifndef _LINUX_DYNLOCKS_H
+#define _LINUX_DYNLOCKS_H
+
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+struct dynlock_member {
+	struct list_head	dl_list;
+	unsigned long		dl_value;	/* lock value */
+	int			dl_refcount;	/* number of users */
+	int			dl_readers;
+	int			dl_writers;
+	int			dl_pid;		/* holder of the lock */
+	wait_queue_head_t	dl_wait;
+};
+
+/*
+ * lock's namespace:
+ *   - list of locks
+ *   - lock to protect this list
+ */
+struct dynlock {
+	struct list_head dl_list;
+	spinlock_t dl_list_lock;
+};
+
+void dynlock_init(struct dynlock *dl);
+void *dynlock_lock(struct dynlock *dl, unsigned long value, int rw, int gfp);
+void dynlock_unlock(struct dynlock *dl, void *lock);
+
+
+#endif
+
lib/dynlocks.c new file +150 -0

150 lines not on this page · raw message

lib/Makefile renamed from lib/Makefile~dynamic-locks +1 -1
diff -puN lib/Makefile~dynamic-locks lib/Makefile
@@ -5,7 +5,7 @@
 
 lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \
 	 bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
-	 kobject.o idr.o
+	 kobject.o idr.o dynlocks.o
 
 lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
 lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o

_

support for parallel directory operations for VFS

fs/namei.c renamed from fs/namei.c~vfs-pdirops +93 -39

344 lines not on this page · raw message

include/linux/fs.h renamed from include/linux/fs.h~vfs-pdirops +4 -0
diff -puN include/linux/fs.h~vfs-pdirops include/linux/fs.h
@@ -19,6 +19,7 @@
 #include <linux/cache.h>
 #include <linux/radix-tree.h>
 #include <linux/kobject.h>
+#include <linux/dynlocks.h>
 #include <asm/atomic.h>
 
 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;
include/linux/namei.h renamed from include/linux/namei.h~vfs-pdirops +3 -2
diff -puN include/linux/namei.h~vfs-pdirops include/linux/namei.h
@@ -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 */
fs/inode.c renamed from fs/inode.c~vfs-pdirops +2 -0
diff -puN fs/inode.c~vfs-pdirops fs/inode.c
@@ -19,6 +19,7 @@
 #include <linux/swap.h>
 #include <linux/security.h>
 #include <linux/cdev.h>
+#include <linux/dynlocks.h>
 
 /*
  * 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;
 }

_

parallel directory operations for EXT3.

htree consists of index blocks and leaves. leaves are protected against modifications and lookups. so, any process which wants to operate on some leaf have to lock this leaf. in order to decrease lock collisions while splitting we use bit-based spinlock to protect index blocks. this mean we can't sleep in the middle of index block modification and we have to make modifications in strict order, so that any user walking through the htree will not lose any leaf and will not select wrong leaf.

fs/ext3/namei.c renamed from fs/ext3/namei.c~ext3-pdirops +495 -122

1,134 lines not on this page · raw message

fs/ext3/super.c renamed from fs/ext3/super.c~ext3-pdirops +9 -0
diff -puN fs/ext3/super.c~ext3-pdirops fs/ext3/super.c
@@ -504,6 +504,9 @@ static struct inode *ext3_alloc_inode(st
 	ei->i_default_acl = EXT3_ACL_NOT_CACHED;
 #endif
 	ei->vfs_inode.i_version = 1;
+	dynlock_init(&ei->i_htree_lock);
+	sema_init(&ei->i_append_sem, 1);
+	sema_init(&ei->i_rename_sem, 1);
 	return &ei->vfs_inode;
 }
 
@@ -712,6 +715,8 @@ static int parse_options (char * options
 				return 0;
 			}
 		}
+		else if (!strcmp (this_char, "pdirops"))
+			set_opt (sbi->s_mount_opt, PDIROPS);
 		else if (!strcmp (this_char, "grpid") ||
 			 !strcmp (this_char, "bsdgroups"))
 			set_opt (sbi->s_mount_opt, GRPID);
@@ -890,6 +895,10 @@ static int ext3_setup_super(struct super
 		ext3_check_blocks_bitmap (sb);
 		ext3_check_inodes_bitmap (sb);
 	}
+#endif
+#ifdef S_PDIROPS
+	if (test_opt (sb, PDIROPS))
+		sb->s_flags |= S_PDIROPS;
 #endif
 	setup_ro_after(sb);
 	return res;
include/linux/ext3_fs.h renamed from include/linux/ext3_fs.h~ext3-pdirops +1 -0
diff -puN include/linux/ext3_fs.h~ext3-pdirops include/linux/ext3_fs.h
@@ -312,6 +312,7 @@ struct ext3_inode {
 /*
  * Mount flags
  */
+#define EXT3_MOUNT_PDIROPS		0x800000/* Parallel dir operations */
 #define EXT3_MOUNT_CHECK		0x0001	/* Do mount-time checks */
 #define EXT3_MOUNT_OLDALLOC		0x0002  /* Don't use the new Orlov allocator */
 #define EXT3_MOUNT_GRPID		0x0004	/* Create files with directory's group */
include/linux/ext3_fs_i.h renamed from include/linux/ext3_fs_i.h~ext3-pdirops +6 -0
diff -puN include/linux/ext3_fs_i.h~ext3-pdirops include/linux/ext3_fs_i.h
@@ -17,6 +17,7 @@
 #define _LINUX_EXT3_FS_I
 
 #include <linux/rwsem.h>
+#include <linux/dynlocks.h>
 
 /*
  * second extended file system inode data in memory
@@ -98,6 +99,11 @@ struct ext3_inode_info {
 	 */
 	struct rw_semaphore truncate_sem;
 	struct inode vfs_inode;
+
+	/* following fields for parallel directory operations -bzzz */
+	struct dynlock i_htree_lock;
+	struct semaphore i_append_sem;
+	struct semaphore i_rename_sem;
 };
 
 #endif	/* _LINUX_EXT3_FS_I */

_

#1 lore raw
Quoted 1 line · Alex Tomas

I use dynamic locks

dynamic locks doesn't really look like a name that matches what it does.. how about "spinarecuphore" ?

#2 lore raw
In reply to Alex Tomas
Quoted 4 lines · Alex Tomas

I would to like to see any comments/suggestions.

dynamic locks. supports exclusive and shared locks. exclusive lock may be taken several times by first owner.

Yuck. It spins, it sleeps, it can be acquired recursively by the same process. Ugly stuff.

Signature · 3 lines
"It's not Hollywood.  War is real, war is primarily not about defeat or
victory, it is about death.  I've seen thousands and thousands of dead bodies.
Do you think I want to have an academic debate on this subject?" -- Robert Fisk
#3 lore raw
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

Matthew Wilcox (MW) writes:

Quoted 4 lines · Alex Tomas
Quoted 4 lines

I would to like to see any comments/suggestions.

dynamic locks. supports exclusive and shared locks. exclusive lock may be taken several times by first owner.

MW> Yuck. It spins, it sleeps, it can be acquired recursively by the same MW> process. Ugly stuff.

it allows to simplify rename case very much