On Fri, May 15, 2020 at 06:16:31AM -0700, Matthew Wilcox wrote: > From: "Matthew Wilcox (Oracle)" <willy@xxxxxxxxxxxxx> > > We can only kmap() one subpage of a THP at a time, so loop over all > relevant subpages, skipping ones which don't need to be zeroed. This is > too large to inline when THPs are enabled and we actually need highmem, > so put it in highmem.c. > > Signed-off-by: Matthew Wilcox (Oracle) <willy@xxxxxxxxxxxxx> > --- > include/linux/highmem.h | 15 +++++++--- > mm/highmem.c | 62 +++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 71 insertions(+), 6 deletions(-) > > diff --git a/include/linux/highmem.h b/include/linux/highmem.h > index ea5cdbd8c2c3..74614903619d 100644 > --- a/include/linux/highmem.h > +++ b/include/linux/highmem.h > @@ -215,13 +215,18 @@ static inline void clear_highpage(struct page *page) > kunmap_atomic(kaddr); > } > > +#if defined(CONFIG_HIGHMEM) && defined(CONFIG_TRANSPARENT_HUGEPAGE) > +void zero_user_segments(struct page *page, unsigned start1, unsigned end1, > + unsigned start2, unsigned end2); > +#else /* !HIGHMEM || !TRANSPARENT_HUGEPAGE */ > static inline void zero_user_segments(struct page *page, > - unsigned start1, unsigned end1, > - unsigned start2, unsigned end2) > + unsigned start1, unsigned end1, > + unsigned start2, unsigned end2) > { > + unsigned long i; > void *kaddr = kmap_atomic(page); > > - BUG_ON(end1 > PAGE_SIZE || end2 > PAGE_SIZE); > + BUG_ON(end1 > thp_size(page) || end2 > thp_size(page)); > > if (end1 > start1) > memset(kaddr + start1, 0, end1 - start1); > @@ -230,8 +235,10 @@ static inline void zero_user_segments(struct page *page, > memset(kaddr + start2, 0, end2 - start2); > > kunmap_atomic(kaddr); > - flush_dcache_page(page); > + for (i = 0; i < hpage_nr_pages(page); i++) > + flush_dcache_page(page + i); Well, we need to settle on whether flush_dcache_page() has to be aware about compound pages. There are already architectures that know how to flush compound page, see ARM. -- Kirill A. Shutemov