Skip to content

Commit 729881f

Browse files
Baolin Wangakpm00
authored andcommitted
mm: shmem: fallback to page size splice if large folio has poisoned pages
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/ [baolin.wang@linux.alibaba.com: code layout cleaup, per dhowells] Link: https://lkml.kernel.org/r/32dd938c-3531-49f7-93e4-b7ff21fec569@linux.alibaba.com 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>
1 parent 477327e commit 729881f

File tree

1 file changed

+30
-8
lines changed

1 file changed

+30
-8
lines changed

mm/shmem.c

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3288,11 +3288,16 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
32883288
len = min_t(size_t, len, npages * PAGE_SIZE);
32893289

32903290
do {
3291+
bool fallback_page_splice = false;
3292+
struct page *page = NULL;
3293+
pgoff_t index;
3294+
size_t size;
3295+
32913296
if (*ppos >= i_size_read(inode))
32923297
break;
32933298

3294-
error = shmem_get_folio(inode, *ppos / PAGE_SIZE, 0, &folio,
3295-
SGP_READ);
3299+
index = *ppos >> PAGE_SHIFT;
3300+
error = shmem_get_folio(inode, index, 0, &folio, SGP_READ);
32963301
if (error) {
32973302
if (error == -EINVAL)
32983303
error = 0;
@@ -3301,12 +3306,15 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
33013306
if (folio) {
33023307
folio_unlock(folio);
33033308

3304-
if (folio_test_hwpoison(folio) ||
3305-
(folio_test_large(folio) &&
3306-
folio_test_has_hwpoisoned(folio))) {
3309+
page = folio_file_page(folio, index);
3310+
if (PageHWPoison(page)) {
33073311
error = -EIO;
33083312
break;
33093313
}
3314+
3315+
if (folio_test_large(folio) &&
3316+
folio_test_has_hwpoisoned(folio))
3317+
fallback_page_splice = true;
33103318
}
33113319

33123320
/*
@@ -3320,16 +3328,30 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
33203328
isize = i_size_read(inode);
33213329
if (unlikely(*ppos >= isize))
33223330
break;
3323-
part = min_t(loff_t, isize - *ppos, len);
3331+
/*
3332+
* Fallback to PAGE_SIZE splice if the large folio has hwpoisoned
3333+
* pages.
3334+
*/
3335+
size = len;
3336+
if (unlikely(fallback_page_splice)) {
3337+
size_t offset = *ppos & ~PAGE_MASK;
3338+
3339+
size = umin(size, PAGE_SIZE - offset);
3340+
}
3341+
part = min_t(loff_t, isize - *ppos, size);
33243342

33253343
if (folio) {
33263344
/*
33273345
* If users can be writing to this page using arbitrary
33283346
* virtual addresses, take care about potential aliasing
33293347
* before reading the page on the kernel side.
33303348
*/
3331-
if (mapping_writably_mapped(mapping))
3332-
flush_dcache_folio(folio);
3349+
if (mapping_writably_mapped(mapping)) {
3350+
if (likely(!fallback_page_splice))
3351+
flush_dcache_folio(folio);
3352+
else
3353+
flush_dcache_page(page);
3354+
}
33333355
folio_mark_accessed(folio);
33343356
/*
33353357
* Ok, we have the page, and it's up-to-date, so we can

0 commit comments

Comments
 (0)