highmem: Add memcpy_folio()
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Mon, 31 Mar 2025 20:11:11 +0000 (21:11 +0100)
committerJaegeuk Kim <jaegeuk@kernel.org>
Mon, 28 Apr 2025 15:26:33 +0000 (15:26 +0000)
The folio equivalent of memcpy_page().  It should correctly and
efficiently manage large folios:

 - If one, neither or both is highmem
 - If (either or both) offset+len crosses a page boundary
 - If the two offsets are congruent or not

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
include/linux/highmem.h

index 5c6bea81a90ecf3bf2bd9bce838d684c3d5351ee..fd72f66b872a2fcc1b6c498b92d205075873ab09 100644 (file)
@@ -404,6 +404,33 @@ static inline void memcpy_page(struct page *dst_page, size_t dst_off,
        kunmap_local(dst);
 }
 
+static inline void memcpy_folio(struct folio *dst_folio, size_t dst_off,
+               struct folio *src_folio, size_t src_off, size_t len)
+{
+       VM_BUG_ON(dst_off + len > folio_size(dst_folio));
+       VM_BUG_ON(src_off + len > folio_size(src_folio));
+
+       do {
+               char *dst = kmap_local_folio(dst_folio, dst_off);
+               const char *src = kmap_local_folio(src_folio, src_off);
+               size_t chunk = len;
+
+               if (folio_test_highmem(dst_folio) &&
+                   chunk > PAGE_SIZE - offset_in_page(dst_off))
+                       chunk = PAGE_SIZE - offset_in_page(dst_off);
+               if (folio_test_highmem(src_folio) &&
+                   chunk > PAGE_SIZE - offset_in_page(src_off))
+                       chunk = PAGE_SIZE - offset_in_page(src_off);
+               memcpy(dst, src, chunk);
+               kunmap_local(src);
+               kunmap_local(dst);
+
+               dst_off += chunk;
+               src_off += chunk;
+               len -= chunk;
+       } while (len > 0);
+}
+
 static inline void memset_page(struct page *page, size_t offset, int val,
                               size_t len)
 {