| From 9afc9a8a4909fece0e911e72b1060614ba2f7969 Mon Sep 17 00:00:00 2001 |
| From: Zhe Li <lizhe67@huawei.com> |
| Date: Fri, 29 May 2020 11:37:11 +0800 |
| Subject: jffs2: Fix GC exit abnormally |
| |
| From: Zhe Li <lizhe67@huawei.com> |
| |
| commit 9afc9a8a4909fece0e911e72b1060614ba2f7969 upstream. |
| |
| The log of this problem is: |
| jffs2: Error garbage collecting node at 0x***! |
| jffs2: No space for garbage collection. Aborting GC thread |
| |
| This is because GC believe that it do nothing, so it abort. |
| |
| After going over the image of jffs2, I find a scene that |
| can trigger this problem stably. |
| The scene is: there is a normal dirent node at summary-area, |
| but abnormal at corresponding not-summary-area with error |
| name_crc. |
| |
| The reason that GC exit abnormally is because it find that |
| abnormal dirent node to GC, but when it goes to function |
| jffs2_add_fd_to_list, it cannot meet the condition listed |
| below: |
| |
| if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) |
| |
| So no node is marked obsolete, statistical information of |
| erase_block do not change, which cause GC exit abnormally. |
| |
| The root cause of this problem is: we do not check the |
| name_crc of the abnormal dirent node with summary is enabled. |
| |
| Noticed that in function jffs2_scan_dirent_node, we use |
| function jffs2_scan_dirty_space to deal with the dirent |
| node with error name_crc. So this patch add a checking |
| code in function read_direntry to ensure the correctness |
| of dirent node. If checked failed, the dirent node will |
| be marked obsolete so GC will pass this node and this |
| problem will be fixed. |
| |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Zhe Li <lizhe67@huawei.com> |
| Signed-off-by: Richard Weinberger <richard@nod.at> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/jffs2/readinode.c | 16 ++++++++++++++++ |
| 1 file changed, 16 insertions(+) |
| |
| --- a/fs/jffs2/readinode.c |
| +++ b/fs/jffs2/readinode.c |
| @@ -672,6 +672,22 @@ static inline int read_direntry(struct j |
| jffs2_free_full_dirent(fd); |
| return -EIO; |
| } |
| + |
| +#ifdef CONFIG_JFFS2_SUMMARY |
| + /* |
| + * we use CONFIG_JFFS2_SUMMARY because without it, we |
| + * have checked it while mounting |
| + */ |
| + crc = crc32(0, fd->name, rd->nsize); |
| + if (unlikely(crc != je32_to_cpu(rd->name_crc))) { |
| + JFFS2_NOTICE("name CRC failed on dirent node at" |
| + "%#08x: read %#08x,calculated %#08x\n", |
| + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); |
| + jffs2_mark_node_obsolete(c, ref); |
| + jffs2_free_full_dirent(fd); |
| + return 0; |
| + } |
| +#endif |
| } |
| |
| fd->nhash = full_name_hash(NULL, fd->name, rd->nsize); |