mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 11:50:43 +09:00
AMerge tag 'netfs-fixes-20221115' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
Pull netfx fixes from David Howells:
"Two fixes, affecting the functions that iterates over the pagecache
unmarking or unlocking pages after an op is complete:
- xas_for_each() loops must call xas_retry() first thing and
immediately do a "continue" in the case that the extracted value is
a special value that indicates that the walk raced with a
modification. Fix the unlock and unmark loops to do this.
- The maths in the unlock loop is dodgy as it could, theoretically,
at some point in the future end up with a starting file pointer
that is in the middle of a folio. This will cause a subtraction to
go negative - but the number is unsigned. Fix the maths to use
absolute file positions instead of relative page indices"
* tag 'netfs-fixes-20221115' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
netfs: Fix dodgy maths
netfs: Fix missing xas_retry() calls in xarray iteration
This commit is contained in:
@@ -17,9 +17,9 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
|
|||||||
{
|
{
|
||||||
struct netfs_io_subrequest *subreq;
|
struct netfs_io_subrequest *subreq;
|
||||||
struct folio *folio;
|
struct folio *folio;
|
||||||
unsigned int iopos, account = 0;
|
|
||||||
pgoff_t start_page = rreq->start / PAGE_SIZE;
|
pgoff_t start_page = rreq->start / PAGE_SIZE;
|
||||||
pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1;
|
pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1;
|
||||||
|
size_t account = 0;
|
||||||
bool subreq_failed = false;
|
bool subreq_failed = false;
|
||||||
|
|
||||||
XA_STATE(xas, &rreq->mapping->i_pages, start_page);
|
XA_STATE(xas, &rreq->mapping->i_pages, start_page);
|
||||||
@@ -39,18 +39,23 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
|
|||||||
*/
|
*/
|
||||||
subreq = list_first_entry(&rreq->subrequests,
|
subreq = list_first_entry(&rreq->subrequests,
|
||||||
struct netfs_io_subrequest, rreq_link);
|
struct netfs_io_subrequest, rreq_link);
|
||||||
iopos = 0;
|
|
||||||
subreq_failed = (subreq->error < 0);
|
subreq_failed = (subreq->error < 0);
|
||||||
|
|
||||||
trace_netfs_rreq(rreq, netfs_rreq_trace_unlock);
|
trace_netfs_rreq(rreq, netfs_rreq_trace_unlock);
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
xas_for_each(&xas, folio, last_page) {
|
xas_for_each(&xas, folio, last_page) {
|
||||||
unsigned int pgpos = (folio_index(folio) - start_page) * PAGE_SIZE;
|
loff_t pg_end;
|
||||||
unsigned int pgend = pgpos + folio_size(folio);
|
|
||||||
bool pg_failed = false;
|
bool pg_failed = false;
|
||||||
|
|
||||||
|
if (xas_retry(&xas, folio))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pg_end = folio_pos(folio) + folio_size(folio) - 1;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
loff_t sreq_end;
|
||||||
|
|
||||||
if (!subreq) {
|
if (!subreq) {
|
||||||
pg_failed = true;
|
pg_failed = true;
|
||||||
break;
|
break;
|
||||||
@@ -58,11 +63,11 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
|
|||||||
if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags))
|
if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags))
|
||||||
folio_start_fscache(folio);
|
folio_start_fscache(folio);
|
||||||
pg_failed |= subreq_failed;
|
pg_failed |= subreq_failed;
|
||||||
if (pgend < iopos + subreq->len)
|
sreq_end = subreq->start + subreq->len - 1;
|
||||||
|
if (pg_end < sreq_end)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
account += subreq->transferred;
|
account += subreq->transferred;
|
||||||
iopos += subreq->len;
|
|
||||||
if (!list_is_last(&subreq->rreq_link, &rreq->subrequests)) {
|
if (!list_is_last(&subreq->rreq_link, &rreq->subrequests)) {
|
||||||
subreq = list_next_entry(subreq, rreq_link);
|
subreq = list_next_entry(subreq, rreq_link);
|
||||||
subreq_failed = (subreq->error < 0);
|
subreq_failed = (subreq->error < 0);
|
||||||
@@ -70,7 +75,8 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
|
|||||||
subreq = NULL;
|
subreq = NULL;
|
||||||
subreq_failed = false;
|
subreq_failed = false;
|
||||||
}
|
}
|
||||||
if (pgend == iopos)
|
|
||||||
|
if (pg_end == sreq_end)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ static void netfs_rreq_unmark_after_write(struct netfs_io_request *rreq,
|
|||||||
XA_STATE(xas, &rreq->mapping->i_pages, subreq->start / PAGE_SIZE);
|
XA_STATE(xas, &rreq->mapping->i_pages, subreq->start / PAGE_SIZE);
|
||||||
|
|
||||||
xas_for_each(&xas, folio, (subreq->start + subreq->len - 1) / PAGE_SIZE) {
|
xas_for_each(&xas, folio, (subreq->start + subreq->len - 1) / PAGE_SIZE) {
|
||||||
|
if (xas_retry(&xas, folio))
|
||||||
|
continue;
|
||||||
|
|
||||||
/* We might have multiple writes from the same huge
|
/* We might have multiple writes from the same huge
|
||||||
* folio, but we mustn't unlock a folio more than once.
|
* folio, but we mustn't unlock a folio more than once.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user