Skip to content

Commit 38c1ddb

Browse files
Jiaqi Yanakpm00
authored andcommitted
hugetlbfs: improve read HWPOISON hugepage
When a hugepage contains HWPOISON pages, read() fails to read any byte of the hugepage and returns -EIO, although many bytes in the HWPOISON hugepage are readable. Improve this by allowing hugetlbfs_read_iter returns as many bytes as possible. For a requested range [offset, offset + len) that contains HWPOISON page, return [offset, first HWPOISON page addr); the next read attempt will fail and return -EIO. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Jiaqi Yan <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Reviewed-by: Naoya Horiguchi <[email protected]> Cc: James Houghton <[email protected]> Cc: Miaohe Lin <[email protected]> Cc: Muchun Song <[email protected]> Cc: Yang Shi <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent b79f8eb commit 38c1ddb

File tree

1 file changed

+51
-6
lines changed

1 file changed

+51
-6
lines changed

fs/hugetlbfs/inode.c

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,41 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
282282
}
283283
#endif
284284

285+
/*
286+
* Someone wants to read @bytes from a HWPOISON hugetlb @page from @offset.
287+
* Returns the maximum number of bytes one can read without touching the 1st raw
288+
* HWPOISON subpage.
289+
*
290+
* The implementation borrows the iteration logic from copy_page_to_iter*.
291+
*/
292+
static size_t adjust_range_hwpoison(struct page *page, size_t offset, size_t bytes)
293+
{
294+
size_t n = 0;
295+
size_t res = 0;
296+
297+
/* First subpage to start the loop. */
298+
page += offset / PAGE_SIZE;
299+
offset %= PAGE_SIZE;
300+
while (1) {
301+
if (is_raw_hwpoison_page_in_hugepage(page))
302+
break;
303+
304+
/* Safe to read n bytes without touching HWPOISON subpage. */
305+
n = min(bytes, (size_t)PAGE_SIZE - offset);
306+
res += n;
307+
bytes -= n;
308+
if (!bytes || !n)
309+
break;
310+
offset += n;
311+
if (offset == PAGE_SIZE) {
312+
page++;
313+
offset = 0;
314+
}
315+
}
316+
317+
return res;
318+
}
319+
285320
/*
286321
* Support for read() - Find the page attached to f_mapping and copy out the
287322
* data. This provides functionality similar to filemap_read().
@@ -300,7 +335,7 @@ static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
300335

301336
while (iov_iter_count(to)) {
302337
struct page *page;
303-
size_t nr, copied;
338+
size_t nr, copied, want;
304339

305340
/* nr is the maximum number of bytes to copy from this page */
306341
nr = huge_page_size(h);
@@ -328,16 +363,26 @@ static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
328363
} else {
329364
unlock_page(page);
330365

331-
if (PageHWPoison(page)) {
332-
put_page(page);
333-
retval = -EIO;
334-
break;
366+
if (!PageHWPoison(page))
367+
want = nr;
368+
else {
369+
/*
370+
* Adjust how many bytes safe to read without
371+
* touching the 1st raw HWPOISON subpage after
372+
* offset.
373+
*/
374+
want = adjust_range_hwpoison(page, offset, nr);
375+
if (want == 0) {
376+
put_page(page);
377+
retval = -EIO;
378+
break;
379+
}
335380
}
336381

337382
/*
338383
* We have the page, copy it to user space buffer.
339384
*/
340-
copied = copy_page_to_iter(page, offset, nr, to);
385+
copied = copy_page_to_iter(page, offset, want, to);
341386
put_page(page);
342387
}
343388
offset += copied;

0 commit comments

Comments
 (0)