diff options
| author | David Woodhouse <David.Woodhouse@intel.com> | 2016-02-01 07:00:25 -0500 |
|---|---|---|
| committer | David Woodhouse <David.Woodhouse@intel.com> | 2016-02-29 17:29:10 -0500 |
| commit | 5817b9dc9cc1225feedd9e1282707165fc64c384 (patch) | |
| tree | 309d8459630ce24498e02b78091aa3e3594c8678 /fs/jffs2 | |
| parent | d267aefc54a28efc5bda7f009598dc83b5f98734 (diff) | |
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>
Diffstat (limited to 'fs/jffs2')
| -rw-r--r-- | fs/jffs2/gc.c | 64 | ||||
| -rw-r--r-- | fs/jffs2/jffs2_fs_sb.h | 2 | ||||
| -rw-r--r-- | fs/jffs2/nodemgmt.c | 4 |
3 files changed, 45 insertions, 25 deletions
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 5a2dec2b064c..61c55470b1ab 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c | |||
| @@ -134,37 +134,59 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
| 134 | if (mutex_lock_interruptible(&c->alloc_sem)) | 134 | if (mutex_lock_interruptible(&c->alloc_sem)) |
| 135 | return -EINTR; | 135 | return -EINTR; |
| 136 | 136 | ||
| 137 | |||
| 137 | for (;;) { | 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 | spin_lock(&c->erase_completion_lock); | 143 | spin_lock(&c->erase_completion_lock); |
| 139 | if (!c->unchecked_size) | 144 | if (!c->unchecked_size) |
| 140 | break; | 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 | spin_unlock(&c->erase_completion_lock); | 146 | spin_unlock(&c->erase_completion_lock); |
| 156 | 147 | ||
| 157 | if (!xattr) | 148 | if (!xattr) |
| 158 | xattr = jffs2_verify_xattr(c); | 149 | xattr = jffs2_verify_xattr(c); |
| 159 | 150 | ||
| 160 | spin_lock(&c->inocache_lock); | 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; | ||
| 162 | |||
| 163 | if (ic->state != INO_STATE_CHECKEDABSENT && | ||
| 164 | ic->state != INO_STATE_PRESENT) | ||
| 165 | goto got_next; /* with inocache_lock held */ | ||
| 166 | |||
| 167 | jffs2_dbg(1, "Skipping ino #%u already checked\n", | ||
| 168 | ic->ino); | ||
| 169 | } | ||
| 170 | want_ino = 0; | ||
| 171 | } | ||
| 161 | 172 | ||
| 162 | ic = jffs2_get_ino_cache(c, c->checked_ino++); | 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; | ||
| 163 | 176 | ||
| 164 | if (!ic) { | 177 | spin_unlock(&c->inocache_lock); |
| 165 | spin_unlock(&c->inocache_lock); | 178 | |
| 166 | continue; | 179 | pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n", |
| 167 | } | 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 | if (!ic->pino_nlink) { | 191 | if (!ic->pino_nlink) { |
| 170 | jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n", | 192 | jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n", |
| @@ -176,8 +198,6 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
| 176 | switch(ic->state) { | 198 | switch(ic->state) { |
| 177 | case INO_STATE_CHECKEDABSENT: | 199 | case INO_STATE_CHECKEDABSENT: |
| 178 | case INO_STATE_PRESENT: | 200 | case INO_STATE_PRESENT: |
| 179 | jffs2_dbg(1, "Skipping ino #%u already checked\n", | ||
| 180 | ic->ino); | ||
| 181 | spin_unlock(&c->inocache_lock); | 201 | spin_unlock(&c->inocache_lock); |
| 182 | continue; | 202 | continue; |
| 183 | 203 | ||
| @@ -196,7 +216,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
| 196 | ic->ino); | 216 | ic->ino); |
| 197 | /* We need to come back again for the _same_ inode. We've | 217 | /* We need to come back again for the _same_ inode. We've |
| 198 | made no progress in this case, but that should be OK */ | 218 | made no progress in this case, but that should be OK */ |
| 199 | c->checked_ino--; | 219 | c->check_ino = ic->ino; |
| 200 | 220 | ||
| 201 | mutex_unlock(&c->alloc_sem); | 221 | mutex_unlock(&c->alloc_sem); |
| 202 | sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); | 222 | sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); |
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 046fee8b6e9b..778275f48a87 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h | |||
| @@ -49,7 +49,7 @@ struct jffs2_sb_info { | |||
| 49 | struct mtd_info *mtd; | 49 | struct mtd_info *mtd; |
| 50 | 50 | ||
| 51 | uint32_t highest_ino; | 51 | uint32_t highest_ino; |
| 52 | uint32_t checked_ino; | 52 | uint32_t check_ino; /* *NEXT* inode to be checked */ |
| 53 | 53 | ||
| 54 | unsigned int flags; | 54 | unsigned int flags; |
| 55 | 55 | ||
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index b6bd4affd9ad..cda0774c2c9c 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c | |||
| @@ -846,8 +846,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c) | |||
| 846 | return 1; | 846 | return 1; |
| 847 | 847 | ||
| 848 | if (c->unchecked_size) { | 848 | if (c->unchecked_size) { |
| 849 | jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", | 849 | jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, check_ino #%d\n", |
| 850 | c->unchecked_size, c->checked_ino); | 850 | c->unchecked_size, c->check_ino); |
| 851 | return 1; | 851 | return 1; |
| 852 | } | 852 | } |
| 853 | 853 | ||
