Skip to content

Commit 184cee5

Browse files
OGAWAHirofumitorvalds
authored andcommitted
mm/highmem.c: fix zero_user_segments() with start > end
zero_user_segments() is used from __block_write_begin_int(), for example like the following zero_user_segments(page, 4096, 1024, 512, 918) But new the zero_user_segments() implementation for for HIGHMEM + TRANSPARENT_HUGEPAGE doesn't handle "start > end" case correctly, and hits BUG_ON(). (we can fix __block_write_begin_int() instead though, it is the old and multiple usage) Also it calls kmap_atomic() unnecessarily while start == end == 0. Link: https://lkml.kernel.org/r/87v9ab60r4.fsf@mail.parknet.co.jp Fixes: 0060ef3 ("mm: support THPs in zero_user_segments") Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Cc: Matthew Wilcox <willy@infradead.org> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 4eae4ef commit 184cee5

File tree

1 file changed

+12
-5
lines changed

1 file changed

+12
-5
lines changed

mm/highmem.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -368,20 +368,24 @@ void zero_user_segments(struct page *page, unsigned start1, unsigned end1,
368368

369369
BUG_ON(end1 > page_size(page) || end2 > page_size(page));
370370

371+
if (start1 >= end1)
372+
start1 = end1 = 0;
373+
if (start2 >= end2)
374+
start2 = end2 = 0;
375+
371376
for (i = 0; i < compound_nr(page); i++) {
372377
void *kaddr = NULL;
373378

374-
if (start1 < PAGE_SIZE || start2 < PAGE_SIZE)
375-
kaddr = kmap_atomic(page + i);
376-
377379
if (start1 >= PAGE_SIZE) {
378380
start1 -= PAGE_SIZE;
379381
end1 -= PAGE_SIZE;
380382
} else {
381383
unsigned this_end = min_t(unsigned, end1, PAGE_SIZE);
382384

383-
if (end1 > start1)
385+
if (end1 > start1) {
386+
kaddr = kmap_atomic(page + i);
384387
memset(kaddr + start1, 0, this_end - start1);
388+
}
385389
end1 -= this_end;
386390
start1 = 0;
387391
}
@@ -392,8 +396,11 @@ void zero_user_segments(struct page *page, unsigned start1, unsigned end1,
392396
} else {
393397
unsigned this_end = min_t(unsigned, end2, PAGE_SIZE);
394398

395-
if (end2 > start2)
399+
if (end2 > start2) {
400+
if (!kaddr)
401+
kaddr = kmap_atomic(page + i);
396402
memset(kaddr + start2, 0, this_end - start2);
403+
}
397404
end2 -= this_end;
398405
start2 = 0;
399406
}

0 commit comments

Comments
 (0)