]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mm: shmem: fallback to page size splice if large folio has poisoned pages
authorBaolin Wang <baolin.wang@linux.alibaba.com>
Sat, 26 Oct 2024 13:51:52 +0000 (21:51 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 1 Nov 2024 04:29:27 +0000 (21:29 -0700)
The tmpfs has already supported the PMD-sized large folios, and splice()
can not read any pages if the large folio has a poisoned page, which is
not good as Matthew pointed out in a previous email[1]:

"so if we have hwpoison set on one page in a folio, we now can't read
bytes from any page in the folio?  That seems like we've made a bad
situation worse."

Thus add a fallback to the PAGE_SIZE splice() still allows reading normal
pages if the large folio has hwpoisoned pages.

[1] https://lore.kernel.org/all/Zw_d0EVAJkpNJEbA@casper.infradead.org/
Link: https://lkml.kernel.org/r/e3737fbd5366c4de4337bf5f2044817e77a5235b.1729915173.git.baolin.wang@linux.alibaba.com
Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: David Howells <dhowells@redhat.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Yang Shi <shy828301@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/shmem.c

index 06da05f984dabb225f2645534f1f026ce833f377..275251abd5960478d892481b5b80027eae958c7e 100644 (file)
@@ -3288,11 +3288,16 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
        len = min_t(size_t, len, npages * PAGE_SIZE);
 
        do {
+               bool fallback_page_splice = false;
+               struct page *page = NULL;
+               pgoff_t index;
+               size_t size;
+
                if (*ppos >= i_size_read(inode))
                        break;
 
-               error = shmem_get_folio(inode, *ppos / PAGE_SIZE, 0, &folio,
-                                       SGP_READ);
+               index = *ppos >> PAGE_SHIFT;
+               error = shmem_get_folio(inode, index, 0, &folio, SGP_READ);
                if (error) {
                        if (error == -EINVAL)
                                error = 0;
@@ -3301,12 +3306,15 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
                if (folio) {
                        folio_unlock(folio);
 
-                       if (folio_test_hwpoison(folio) ||
-                           (folio_test_large(folio) &&
-                            folio_test_has_hwpoisoned(folio))) {
+                       page = folio_file_page(folio, index);
+                       if (PageHWPoison(page)) {
                                error = -EIO;
                                break;
                        }
+
+                       if (folio_test_large(folio) &&
+                           folio_test_has_hwpoisoned(folio))
+                               fallback_page_splice = true;
                }
 
                /*
@@ -3320,7 +3328,18 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
                isize = i_size_read(inode);
                if (unlikely(*ppos >= isize))
                        break;
-               part = min_t(loff_t, isize - *ppos, len);
+               /*
+                * Fallback to PAGE_SIZE splice if the large folio has hwpoisoned
+                * pages.
+                */
+               if (likely(!fallback_page_splice)) {
+                       size = len;
+               } else {
+                       size_t offset = *ppos & ~PAGE_MASK;
+
+                       size = min_t(loff_t, PAGE_SIZE - offset, len);
+               }
+               part = min_t(loff_t, isize - *ppos, size);
 
                if (folio) {
                        /*
@@ -3328,8 +3347,12 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
                         * virtual addresses, take care about potential aliasing
                         * before reading the page on the kernel side.
                         */
-                       if (mapping_writably_mapped(mapping))
-                               flush_dcache_folio(folio);
+                       if (mapping_writably_mapped(mapping)) {
+                               if (likely(!fallback_page_splice))
+                                       flush_dcache_folio(folio);
+                               else
+                                       flush_dcache_page(page);
+                       }
                        folio_mark_accessed(folio);
                        /*
                         * Ok, we have the page, and it's up-to-date, so we can