mtd/fs/jffs2 build.c,1.43,1.44 gc.c,1.91,1.92 nodelist.c,1.73,1.74 nodelist.h,1.89,1.90 os-linux.h,1.22,1.23 readinode.c,1.97,1.98

David Woodhouse dwmw2 at infradead.org
Fri Jan 17 11:04:19 EST 2003


Update of /home/cvs/mtd/fs/jffs2
In directory phoenix.infradead.org:/tmp/cvs-serv26967

Modified Files:
	build.c gc.c nodelist.c nodelist.h os-linux.h readinode.c 
Log Message:
Revamp locking and add wait queue handling for inocache state.


Index: build.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/build.c,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -r1.43 -r1.44
--- build.c	9 Jan 2003 13:59:14 -0000	1.43
+++ build.c	17 Jan 2003 16:04:16 -0000	1.44
@@ -230,6 +230,7 @@
 	init_MUTEX(&c->alloc_sem);
 	init_MUTEX(&c->erase_free_sem);
 	init_waitqueue_head(&c->erase_wait);
+	init_waitqueue_head(&c->inocache_wq);
 	spin_lock_init(&c->erase_completion_lock);
 	spin_lock_init(&c->inocache_lock);
 

Index: gc.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/gc.c,v
retrieving revision 1.91
retrieving revision 1.92
diff -u -r1.91 -r1.92
--- gc.c	14 Jan 2003 14:54:32 -0000	1.91
+++ gc.c	17 Jan 2003 16:04:16 -0000	1.92
@@ -21,6 +21,9 @@
 #include <linux/stat.h>
 #include "nodelist.h"
 
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
+					  struct jffs2_inode_cache *ic,
+					  struct jffs2_raw_node_ref *raw);
 static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
 					struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
 static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
@@ -108,6 +111,7 @@
  */
 int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 {
+	struct jffs2_inode_cache *ic;
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_inode_info *f;
 	struct jffs2_raw_node_ref *raw;
@@ -135,7 +139,9 @@
 			D1(jffs2_dump_block_lists(c));
 			BUG();
 		}
+		spin_lock(&c->inocache_lock);
 		ic = jffs2_get_ino_cache(c, c->checked_ino++);
+		spin_unlock(&c->inocache_lock);
 		if (!ic)
 			continue;
 		if (!ic->nlink) {
@@ -219,7 +225,94 @@
 
 	spin_unlock(&c->erase_completion_lock);
 
-	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, ref_offset(raw), inum));
+	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum));
+
+	/* Three possibilities:
+	   1. Inode is already in-core. We must iget it and do proper
+	      updating to its fragtree, etc.
+	   2. Inode is not in-core, node is REF_PRISTINE. We lock the
+	      inocache to prevent a read_inode(), copy the node intact.
+	   3. Inode is not in-core, node is not pristine. We must iget()
+	      and take the slow path.
+	*/
+	spin_lock(&c->inocache_lock);
+	ic = jffs2_get_ino_cache(c, inum);
+
+	/* This should never fail unless I'm particularly stupid.
+	   So we don't check before dereferencing it */
+
+	switch(ic->state) {
+	case INO_STATE_CHECKEDABSENT:
+		/* It's been checked, but it's not currently in-core. 
+		   We can just copy any pristine nodes, but have
+		   to prevent anyone else from doing read_inode() while
+		   we're at it, so we set the state accordingly */
+		if (ref_flags(raw) == REF_PRISTINE)
+			ic->state = INO_STATE_GC;
+		D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n", 
+			  inum));
+		break;
+
+	case INO_STATE_PRESENT:
+	case INO_STATE_UNCHECKED:
+		/* It's in-core or hasn't been checked. GC must iget() it. */
+		break;
+
+	case INO_STATE_CHECKING:
+		/* Should never happen. We should have finished checking
+		   by the time we actually start doing any GC. */
+		BUG();
+
+	
+	case INO_STATE_GC:
+		/* Should never happen. We are holding the alloc_sem, 
+		   no other garbage collection can happen. Note that we
+		   do depend on this later when deciding to do a simple
+		   node copy */
+		BUG();
+			 
+	case INO_STATE_READING:
+		/* Someone's currently trying to read it. We must wait for
+		   them to finish and then go through the full iget() route
+		   to do the GC. However, sometimes read_inode() needs to get
+		   the alloc_sem() (for marking nodes invalid) so we must
+		   drop the alloc_sem before sleeping. */
+
+		up(&c->alloc_sem);
+		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
+			  inum, ic->state));
+		sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+		/* And because we dropped the alloc_sem we must start again from the 
+		   beginning. Ponder chance of livelock here -- we're returning success
+		   without actually making any progress.
+
+		   Q: What are the chances that the inode is back in INO_STATE_READING 
+		   again by the time we next enter this function? And that this happens
+		   enough times to cause a real delay?
+
+		   A: Small enough that I don't care :) 
+		*/
+		return 0;
+
+	}
+
+	spin_unlock(&c->inocache_lock);
+
+	/* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
+	   node intact, and we don't have to muck about with the fragtree etc. 
+	   because we know it's not in-core. If it _was_ in-core, we go through
+	   all the iget() crap anyway */
+
+	if (ic->state == INO_STATE_GC) {
+		ret = jffs2_garbage_collect_pristine(c, ic, raw);
+		if (ret != -EBADFD) {
+			up(&c->alloc_sem);
+			goto eraseit_lock;
+		}
+		/* Fall through if it wanted us to */
+		jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
+	}
+
 
 	inode = iget(OFNI_BS_2SFFJ(c), inum);
 	if (is_bad_inode(inode)) {
@@ -325,6 +418,13 @@
 	spin_unlock(&c->erase_completion_lock);
 
 	return ret;
+}
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
+					  struct jffs2_inode_cache *ic,
+					  struct jffs2_raw_node_ref *raw)
+{
+	D1(printk(KERN_DEBUG "Wanted to GC REF_PRISTINE node. Not implemented yet :)\n"));
+	return -EBADFD;
 }
 
 static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 

Index: nodelist.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/nodelist.c,v
retrieving revision 1.73
retrieving revision 1.74
diff -u -r1.73 -r1.74
--- nodelist.c	14 Jan 2003 14:54:32 -0000	1.73
+++ nodelist.c	17 Jan 2003 16:04:16 -0000	1.74
@@ -417,12 +417,24 @@
 	return err;
 }
 
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
+{
+	spin_lock(&c->inocache_lock);
+	ic->state = state;
+	wake_up(&c->inocache_wq);
+	spin_unlock(&c->inocache_lock);
+}
+
+/* During mount, this needs no locking. During normal operation, its
+   callers want to do other stuff while still holding the inocache_lock.
+   Rather than introducing special case get_ino_cache functions or 
+   callbacks, we just let the caller do the locking itself. */
+   
 struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino)
 {
 	struct jffs2_inode_cache *ret;
 
 	D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
-	spin_lock (&c->inocache_lock);
 
 	ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
 	while (ret && ret->ino < ino) {
@@ -431,8 +443,6 @@
 	
 	if (ret && ret->ino != ino)
 		ret = NULL;
-
-	spin_unlock(&c->inocache_lock);
 
 	D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino));
 	return ret;

Index: nodelist.h
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/nodelist.h,v
retrieving revision 1.89
retrieving revision 1.90
diff -u -r1.89 -r1.90
--- nodelist.h	12 Jan 2003 13:21:28 -0000	1.89
+++ nodelist.h	17 Jan 2003 16:04:16 -0000	1.90
@@ -103,13 +103,18 @@
 	uint32_t ino;
 	int nlink;
 	int state;
-#define INO_STATE_UNCHECKED 0
-#define INO_STATE_CHECKING 1
-#define INO_STATE_CHECKEDABSENT 2
-#define INO_STATE_READINGINODE 3
-#define INO_STATE_PRESENT 5
 };
 
+/* Inode states for 'state' above. We need the 'GC' state to prevent
+   someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
+   node without going through all the iget() nonsense */
+#define INO_STATE_UNCHECKED	0	/* CRC checks not yet done */
+#define INO_STATE_CHECKING	1	/* CRC checks in progress */
+#define INO_STATE_PRESENT	2	/* In core */
+#define INO_STATE_CHECKEDABSENT	3	/* Checked, cleared again */
+#define INO_STATE_GC		4	/* GCing a 'pristine' node */
+#define INO_STATE_READING	5	/* In read_inode() */
+
 #define INOCACHE_HASHSIZE 128
 
 struct jffs2_scan_info {
@@ -286,6 +291,7 @@
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
 			  uint32_t *highest_version, uint32_t *latest_mctime,
 			  uint32_t *mctime_ver);
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
 struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino);
 void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
 void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);

Index: os-linux.h
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/os-linux.h,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- os-linux.h	14 Jan 2003 14:06:22 -0000	1.22
+++ os-linux.h	17 Jan 2003 16:04:16 -0000	1.23
@@ -69,6 +69,16 @@
 #define current_sig_lock current->sig->siglock
 #endif
 
+#define sleep_on_spinunlock(wq, s)				\
+	do {							\
+		DECLARE_WAITQUEUE(__wait, current);		\
+		add_wait_queue((wq), &__wait);			\
+		set_current_state(TASK_UNINTERRUPTIBLE);	\
+		spin_unlock(s);					\
+		schedule();					\
+		remove_wait_queue((wq), &__wait);		\
+	} while(0)
+
 static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 {
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)

Index: readinode.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/readinode.c,v
retrieving revision 1.97
retrieving revision 1.98
diff -u -r1.97 -r1.98
--- readinode.c	14 Jan 2003 14:06:22 -0000	1.97
+++ readinode.c	17 Jan 2003 16:04:16 -0000	1.98
@@ -329,10 +329,43 @@
 
 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
 
+retry_inocache:
+	spin_lock(&c->inocache_lock);
 	f->inocache = jffs2_get_ino_cache(c, ino);
 
 	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
 
+	if (f->inocache) {
+		/* Check its state. We may need to wait before we can use it */
+		switch(f->inocache->state) {
+		case INO_STATE_UNCHECKED:
+		case INO_STATE_CHECKEDABSENT:
+			f->inocache->state = INO_STATE_READING;
+			break;
+			
+		case INO_STATE_CHECKING:
+		case INO_STATE_GC:
+			/* If it's in either of these states, we need
+			   to wait for whoever's got it to finish and
+			   put it back. */
+			D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
+				  ino, f->inocache->state));
+			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			goto retry_inocache;
+
+		case INO_STATE_READING:
+		case INO_STATE_PRESENT:
+			/* Eep. This should never happen. It can
+			happen if Linux calls read_inode() again
+			before clear_inode() has finished though. */
+			printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
+			/* Fail. That's probably better than allowing it to succeed */
+			f->inocache = NULL;
+			break;
+		}
+	}
+	spin_unlock(&c->inocache_lock);
+
 	if (!f->inocache && ino == 1) {
 		/* Special case - no root inode on medium */
 		f->inocache = jffs2_alloc_inode_cache();
@@ -344,6 +377,7 @@
 		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
 		f->inocache->ino = f->inocache->nlink = 1;
 		f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+		f->inocache->state = INO_STATE_READING;
 		jffs2_add_ino_cache(c, f->inocache);
 	}
 	if (!f->inocache) {
@@ -357,6 +391,7 @@
 
 	if (ret) {
 		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", ino, ret);
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 		return ret;
 	}
 	f->dents = fd_list;
@@ -391,6 +426,7 @@
 		if (ino != 1) {
 			printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", ino);
 			if (!fd_list) {
+				jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 				return -EIO;
 			}
 			printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
@@ -401,6 +437,7 @@
 		latest_node->isize = cpu_to_je32(0);
 		latest_node->gid = cpu_to_je16(0);
 		latest_node->uid = cpu_to_je16(0);
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
 		return 0;
 	}
 
@@ -476,7 +513,7 @@
 		f->fragtree = RB_ROOT;
 		break;
 	}
-	f->inocache->state = INO_STATE_PRESENT;
+	jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
 
 	return 0;
 }
@@ -520,7 +557,7 @@
 	}
 
 	if (f->inocache)
-		f->inocache->state = INO_STATE_CHECKEDABSENT;
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 
 	up(&f->sem);
 





More information about the linux-mtd-cvs mailing list