Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

jffs2: Improve post-mount CRC scan efficiency

We need to finish doing the CRC checks before we can allow writes to
happen, and we currently process the inodes in order. This means a call
to jffs2_get_ino_cache() for each possible inode# up to c->highest_ino.

There may be a lot of lookups which fail, if the inode# space is used
sparsely. And the inode# space is *often* used sparsely, if a file
system contains a lot of stuff that was put there in the original
image, followed by lots of creation and deletion of new files.

Instead of processing them numerically with a lookup each time, just
walk the hash buckets instead.

[fix locking typo reported by Dan Carpenter]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

+44 -24
+41 -21
fs/jffs2/gc.c
··· 134 134 if (mutex_lock_interruptible(&c->alloc_sem)) 135 135 return -EINTR; 136 136 137 + 137 138 for (;;) { 139 + /* We can't start doing GC until we've finished checking 140 + the node CRCs etc. */ 141 + int bucket, want_ino; 142 + 138 143 spin_lock(&c->erase_completion_lock); 139 144 if (!c->unchecked_size) 140 145 break; 141 - 142 - /* We can't start doing GC yet. We haven't finished checking 143 - the node CRCs etc. Do it now. */ 144 - 145 - /* checked_ino is protected by the alloc_sem */ 146 - if (c->checked_ino > c->highest_ino && xattr) { 147 - pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n", 148 - c->unchecked_size); 149 - jffs2_dbg_dump_block_lists_nolock(c); 150 - spin_unlock(&c->erase_completion_lock); 151 - mutex_unlock(&c->alloc_sem); 152 - return -ENOSPC; 153 - } 154 - 155 146 spin_unlock(&c->erase_completion_lock); 156 147 157 148 if (!xattr) 158 149 xattr = jffs2_verify_xattr(c); 159 150 160 151 spin_lock(&c->inocache_lock); 152 + /* Instead of doing the inodes in numeric order, doing a lookup 153 + * in the hash for each possible number, just walk the hash 154 + * buckets of *existing* inodes. This means that we process 155 + * them out-of-order, but it can be a lot faster if there's 156 + * a sparse inode# space. Which there often is. */ 157 + want_ino = c->check_ino; 158 + for (bucket = c->check_ino % c->inocache_hashsize ; bucket < c->inocache_hashsize; bucket++) { 159 + for (ic = c->inocache_list[bucket]; ic; ic = ic->next) { 160 + if (ic->ino < want_ino) 161 + continue; 161 162 162 - ic = jffs2_get_ino_cache(c, c->checked_ino++); 163 + if (ic->state != INO_STATE_CHECKEDABSENT && 164 + ic->state != INO_STATE_PRESENT) 165 + goto got_next; /* with inocache_lock held */ 163 166 164 - if (!ic) { 165 - spin_unlock(&c->inocache_lock); 166 - continue; 167 + jffs2_dbg(1, "Skipping ino #%u already checked\n", 168 + ic->ino); 169 + } 170 + want_ino = 0; 167 171 } 172 + 173 + /* Point c->check_ino past the end of the last bucket. */ 174 + c->check_ino = ((c->highest_ino + c->inocache_hashsize + 1) & 175 + ~c->inocache_hashsize) - 1; 176 + 177 + spin_unlock(&c->inocache_lock); 178 + 179 + pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n", 180 + c->unchecked_size); 181 + jffs2_dbg_dump_block_lists_nolock(c); 182 + mutex_unlock(&c->alloc_sem); 183 + return -ENOSPC; 184 + 185 + got_next: 186 + /* For next time round the loop, we want c->checked_ino to indicate 187 + * the *next* one we want to check. And since we're walking the 188 + * buckets rather than doing it sequentially, it's: */ 189 + c->check_ino = ic->ino + c->inocache_hashsize; 168 190 169 191 if (!ic->pino_nlink) { 170 192 jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n", ··· 198 176 switch(ic->state) { 199 177 case INO_STATE_CHECKEDABSENT: 200 178 case INO_STATE_PRESENT: 201 - jffs2_dbg(1, "Skipping ino #%u already checked\n", 202 - ic->ino); 203 179 spin_unlock(&c->inocache_lock); 204 180 continue; 205 181 ··· 216 196 ic->ino); 217 197 /* We need to come back again for the _same_ inode. We've 218 198 made no progress in this case, but that should be OK */ 219 - c->checked_ino--; 199 + c->check_ino = ic->ino; 220 200 221 201 mutex_unlock(&c->alloc_sem); 222 202 sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+1 -1
fs/jffs2/jffs2_fs_sb.h
··· 49 49 struct mtd_info *mtd; 50 50 51 51 uint32_t highest_ino; 52 - uint32_t checked_ino; 52 + uint32_t check_ino; /* *NEXT* inode to be checked */ 53 53 54 54 unsigned int flags; 55 55
+2 -2
fs/jffs2/nodemgmt.c
··· 846 846 return 1; 847 847 848 848 if (c->unchecked_size) { 849 - jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", 850 - c->unchecked_size, c->checked_ino); 849 + jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, check_ino #%d\n", 850 + c->unchecked_size, c->check_ino); 851 851 return 1; 852 852 } 853 853