this is a first attempt at fixing up branching race conditions in
unionfs, it's by no means complete.

1) It introduce a rw semaphore.  The semantic is, that "write" is an
operation that can change the branching structure.  while "read" is
operations that depend on the branching structure not changing.

2) I've removed the use of lock_super() in place of read/write locks as
that would possibly kill performance due to being unable to run multiple
branchput/gets at once and forcing a process to sleep.  I haven't
touched all the places that matter, just putting this out for
discussion.

my read_lock/unlock's around branchput in commonfops.c should probably
be made much wider, but again this is just for proof of concept.
diff -ru unionfs-20050929-0844.bak/branchman.c unionfs-20050929-0844/branchman.c
--- unionfs-20050929-0844.bak/branchman.c	2005-10-16 03:07:39.000000000 -0400
+++ unionfs-20050929-0844/branchman.c	2005-10-16 03:19:59.000000000 -0400
@@ -159,7 +159,7 @@
 
 	sb = file->f_dentry->d_sb;
 
-	lock_super(sb);
+	unionfs_write_lock(sb);
 	if ((err = newputmap(sb)))
 		goto out;
 
@@ -170,7 +170,7 @@
 	atomic_set(&itopd(sb->s_root->d_inode)->uii_generation, err);
 
       out:
-	unlock_super(sb);
+	unionfs_write_unlock(sb);
 	print_exit_status(err);
 	return err;
 }
@@ -232,7 +232,7 @@
 		goto out;
 	}
 
-	lock_super(inode->i_sb);
+	unionfs_write_lock(inode->i_sb);
 	lock_dentry(inode->i_sb->s_root);
 
 	err = -EINVAL;
@@ -372,7 +372,7 @@
 
       out:
 	unlock_dentry(inode->i_sb->s_root);
-	unlock_super(inode->i_sb);
+	unionfs_write_unlock(inode->i_sb);
 
 	KFREE(new_hidden_mnt);
 	KFREE(new_udi_dentry);
@@ -483,7 +483,7 @@
 
 	print_entry_location();
 
-	lock_super(inode->i_sb);
+	unionfs_write_lock(inode->i_sb);
 	lock_dentry(inode->i_sb->s_root);
 
 	if ((err = newputmap(inode->i_sb)))
@@ -518,8 +518,8 @@
 	err = 0;
 
       out:
-	unlock_super(inode->i_sb);
 	unlock_dentry(inode->i_sb->s_root);
+	unionfs_write_unlock(inode->i_sb);
 	KFREE(rdwrargs);
 
 	print_exit_status(err);
diff -ru unionfs-20050929-0844.bak/commonfops.c unionfs-20050929-0844/commonfops.c
--- unionfs-20050929-0844.bak/commonfops.c	2005-10-16 03:07:39.000000000 -0400
+++ unionfs-20050929-0844/commonfops.c	2005-10-16 03:16:35.000000000 -0400
@@ -28,9 +28,11 @@
 {
 	struct putmap *putmap;
 
+	unionfs_read_lock(sb);
+
 	if (generation == atomic_read(&stopd(sb)->usi_generation)) {
 		branchput(sb, index);
-		return;
+		goto out;
 	}
 
 	ASSERT(stopd(sb)->usi_firstputmap <= generation);
@@ -49,6 +51,9 @@
 		fist_dprint(8, "Freeing putmap %d.\n", generation);
 		KFREE(putmap);
 	}
+
+out:
+	unionfs_read_unlock(sb);
 }
 
 char *get_random_name(int size, unsigned char *name)
@@ -344,7 +349,9 @@
 			bend = fbend(file);
 			for (bindex = bstart; bindex <= bend; bindex++) {
 				if (ftohf_index(file, bindex)) {
+					unionfs_read_lock(dentry->d_sb);
 					branchput(dentry->d_sb, bindex);
+					unionfs_read_unlock(dentry->d_sb);
 					fput(ftohf_index(file, bindex));
 					set_ftohf_index(file, bindex, NULL);
 				}
@@ -490,7 +497,9 @@
 		for (bindex = bstart; bindex <= bend; bindex++) {
 			hidden_file = ftohf_index(file, bindex);
 			if (hidden_file) {
+				unionfs_read_lock(file->f_dentry->d_sb);
 				branchput(file->f_dentry->d_sb, bindex);
+				unionfs_read_unlock(file->f_dentry->d_sb);
 				/* fput calls dput for hidden_dentry */
 				fput(hidden_file);
 			}
diff -ru unionfs-20050929-0844.bak/dentry.c unionfs-20050929-0844/dentry.c
--- unionfs-20050929-0844.bak/dentry.c	2005-10-16 03:07:39.000000000 -0400
+++ unionfs-20050929-0844/dentry.c	2005-10-16 03:21:24.000000000 -0400
@@ -62,7 +62,7 @@
 		struct dentry *result;
 		int pdgen;
 
-		lock_super(dentry->d_sb);
+		unionfs_read_lock(dentry->d_sb);
 		locked = 1;
 
 		/* The root entry should always be valid */
@@ -74,7 +74,7 @@
 		if (!restart && (pdgen != sbgen)) {
 			PASSERT(dentry->d_parent->d_op);
 			PASSERT(dentry->d_parent->d_op->d_revalidate);
-			unlock_super(dentry->d_sb);
+			unionfs_read_unlock(dentry->d_sb);
 			locked = 0;
 			/* We must be locked before our parent. */
 			lock_dentry(dentry->d_parent);
@@ -172,7 +172,7 @@
 
       out:
 	if (locked)
-		unlock_super(dentry->d_sb);
+		unionfs_read_unlock(dentry->d_sb);
 	if (invalid)
 		err = 0;
 	fist_print_dentry("revalidate out", dentry);
diff -ru unionfs-20050929-0844.bak/main.c unionfs-20050929-0844/main.c
--- unionfs-20050929-0844.bak/main.c	2005-10-16 03:07:39.000000000 -0400
+++ unionfs-20050929-0844/main.c	2005-10-16 03:17:11.000000000 -0400
@@ -740,6 +740,7 @@
 	memset(stopd(sb), 0, sizeof(struct unionfs_sb_info));
 	stopd(sb)->b_end = -1;
 	atomic_set(&stopd(sb)->usi_generation, 1);
+	init_rwsem(&stopd(sb)->usi_rwsem);
 
 	hidden_root_info = unionfs_parse_options(sb, raw_data);
 	if (IS_ERR(hidden_root_info)) {
diff -ru unionfs-20050929-0844.bak/unionfs.h unionfs-20050929-0844/unionfs.h
--- unionfs-20050929-0844.bak/unionfs.h	2005-10-16 03:07:39.000000000 -0400
+++ unionfs-20050929-0844/unionfs.h	2005-10-16 03:15:09.000000000 -0400
@@ -127,6 +127,7 @@
 
 	atomic_t usi_generation;
 	unsigned long usi_mount_flag;
+	struct rw_semaphore usi_rwsem;
 
 	int usi_persistent;
 
@@ -261,6 +262,11 @@
 #define stohiddenmnt_ptr(super) (stopd(super)->usi_hidden_mnt_p)
 #define stohiddenmnt_inline(super) (stopd(super)->usi_hidden_mnt_i)
 
+#define unionfs_read_lock(sb) down_read(&stopd(sb)->usi_rwsem)
+#define unionfs_read_unlock(sb) up_read(&stopd(sb)->usi_rwsem)
+#define unionfs_write_lock(sb) down_write(&stopd(sb)->usi_rwsem)
+#define unionfs_write_unlock(sb) up_write(&stopd(sb)->usi_rwsem)
+
 /* The UNIONFS_NDEBUG versions are defines, the debug versions are inline
  * functions with extra checks. */
 #ifdef UNIONFS_NDEBUG
_______________________________________________
unionfs mailing list
unionfs@mail.fsl.cs.sunysb.edu
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs

Reply via email to