From: Miklos Szeredi BUG_ON(!entry) in shmem_writepage() is triggered in rare circumstances. The cause is that shmem_truncate_range() erroneously removes partially truncated directory pages at the end of the range. A later reclaim on pages pointing to these removed directories triggers the BUG. Signed-off-by: Miklos Szeredi Cc: Hugh Dickins Cc: Badari Pulavarty Signed-off-by: Andrew Morton --- mm/shmem.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff -puN mm/shmem.c~shmem-fix-bug-in-shmem_writepage mm/shmem.c --- a/mm/shmem.c~shmem-fix-bug-in-shmem_writepage +++ a/mm/shmem.c @@ -543,7 +543,9 @@ static void shmem_truncate_range(struct if (*dir) { diroff = ((idx - ENTRIES_PER_PAGEPAGE/2) % ENTRIES_PER_PAGEPAGE) / ENTRIES_PER_PAGE; - if (!diroff && !offset) { + if (!diroff && !offset && + (!punch_hole || + limit - idx >= ENTRIES_PER_PAGEPAGE)) { *dir = NULL; nr_pages_to_free++; list_add(&middir->lru, &pages_to_free); @@ -570,9 +572,12 @@ static void shmem_truncate_range(struct } stage = idx + ENTRIES_PER_PAGEPAGE; middir = *dir; - *dir = NULL; - nr_pages_to_free++; - list_add(&middir->lru, &pages_to_free); + if (!punch_hole || + limit - idx >= ENTRIES_PER_PAGEPAGE) { + *dir = NULL; + nr_pages_to_free++; + list_add(&middir->lru, &pages_to_free); + } shmem_dir_unmap(dir); cond_resched(); dir = shmem_dir_map(middir); @@ -598,7 +603,8 @@ static void shmem_truncate_range(struct } if (offset) offset = 0; - else if (subdir && !page_private(subdir)) { + else if (subdir && + (!punch_hole || limit - idx >= ENTRIES_PER_PAGE)) { dir[diroff] = NULL; nr_pages_to_free++; list_add(&subdir->lru, &pages_to_free); _