diff mbox

jffs2 about highest_ino

Message ID 1409230554.31489.29.camel@infradead.org
State Not Applicable
Delegated to: David Woodhouse
Headers show

Commit Message

David Woodhouse Aug. 28, 2014, 12:55 p.m. UTC
On Thu, 2014-08-28 at 16:49 +0800, 蓝宇の幽深 wrote:
> according to the jffs2 source code when creat a new file
> the highest_ino will be add one,I used sqlite-3 in my system
> linux2.6.30 and cpu sam9260 ,when "update" or "insert" sqlite-3 will
> creat a Statement Journal file and delete it after finish,so once
> operate will be increase  "highest_ino",after the "highest_ino" more
> than 10 million,I reset my system run to ‍‍‍
> 
> 
> if (!xattr)
> xattr = jffs2_verify_xattr(c);
> 
> 
> spin_lock(&c->inocache_lock);
> 
> 
> ic = jffs2_get_ino_cache(c, c->checked_ino++);
> 
> 
> if (!ic) {
> spin_unlock(&c->inocache_lock);
> continue;
> }‍
> the jffs2 will eat cpu 100% for about 10 seconds and can not feed
> watchdog, case the system reset again . so my problem is that whether
> the "highest_ino" will  decrease ???

Hm, at the very least there ought to be a cond_resched() in there
somewhere. But really, the problem is that the iteration over inodes to
be checked is *entirely* naïve.

We have a hash table with all the existing inodes in it; we should just
iterate over its contents, instead of iterating through the available
number space from 1 to c->highest_ino and doing all the lookups in the
hash table.

Something like this...
diff mbox

Patch

diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 5a2dec2..6ef5246 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -135,6 +135,8 @@  int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 		return -EINTR;
 
 	for (;;) {
+		int bucket, want_ino;
+
 		spin_lock(&c->erase_completion_lock);
 		if (!c->unchecked_size)
 			break;
@@ -142,29 +144,37 @@  int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 		/* We can't start doing GC yet. We haven't finished checking
 		   the node CRCs etc. Do it now. */
 
-		/* checked_ino is protected by the alloc_sem */
-		if (c->checked_ino > c->highest_ino && xattr) {
-			pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n",
-				c->unchecked_size);
-			jffs2_dbg_dump_block_lists_nolock(c);
-			spin_unlock(&c->erase_completion_lock);
-			mutex_unlock(&c->alloc_sem);
-			return -ENOSPC;
-		}
-
 		spin_unlock(&c->erase_completion_lock);
 
 		if (!xattr)
 			xattr = jffs2_verify_xattr(c);
 
 		spin_lock(&c->inocache_lock);
+		want_ino = c->checked_ino;
+		for (bucket = c->checked_ino % c->inocache_hashsize ; bucket < c->inocache_hashsize; bucket++) {
+			for (ic = c->inocache_list[bucket]; ic; ic = ic->next) {
+				if (ic->ino >= want_ino)
+					goto got_next;
+			}
+			want_ino = 0;
+		}
 
-		ic = jffs2_get_ino_cache(c, c->checked_ino++);
+		/* Point c->checked_ino past the end of the last bucket. */
+		c->checked_ino = c->highest_ino + c->inocache_hashsize;
+		c->checked_ino -= (c->checked_ino % c->inocache_hashsize) + 1;
 
-		if (!ic) {
-			spin_unlock(&c->inocache_lock);
-			continue;
-		}
+		spin_unlock(&c->inocache_lock);
+
+		pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n",
+			c->unchecked_size);
+		jffs2_dbg_dump_block_lists_nolock(c);
+		mutex_unlock(&c->alloc_sem);
+		return -ENOSPC;
+
+	got_next:
+		/* c->checked_ino is actually the *next* one we want to check. And since
+		   we're walking the buckets rather than doing it sequentially, it's: */
+		c->checked_ino = ic->ino + c->inocache_hashsize;
 
 		if (!ic->pino_nlink) {
 			jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n",
@@ -196,7 +206,7 @@  int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 				  ic->ino);
 			/* We need to come back again for the _same_ inode. We've
 			 made no progress in this case, but that should be OK */
-			c->checked_ino--;
+			c->checked_ino = ic->ino;
 
 			mutex_unlock(&c->alloc_sem);
 			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);