1*ee4cdf7bSDavid Howells // SPDX-License-Identifier: GPL-2.0-only 2*ee4cdf7bSDavid Howells /* Network filesystem read subrequest result collection, assessment and 3*ee4cdf7bSDavid Howells * retrying. 4*ee4cdf7bSDavid Howells * 5*ee4cdf7bSDavid Howells * Copyright (C) 2024 Red Hat, Inc. All Rights Reserved. 6*ee4cdf7bSDavid Howells * Written by David Howells (dhowells@redhat.com) 7*ee4cdf7bSDavid Howells */ 8*ee4cdf7bSDavid Howells 9*ee4cdf7bSDavid Howells #include <linux/export.h> 10*ee4cdf7bSDavid Howells #include <linux/fs.h> 11*ee4cdf7bSDavid Howells #include <linux/mm.h> 12*ee4cdf7bSDavid Howells #include <linux/pagemap.h> 13*ee4cdf7bSDavid Howells #include <linux/slab.h> 14*ee4cdf7bSDavid Howells #include <linux/task_io_accounting_ops.h> 15*ee4cdf7bSDavid Howells #include "internal.h" 16*ee4cdf7bSDavid Howells 17*ee4cdf7bSDavid Howells /* 18*ee4cdf7bSDavid Howells * Clear the unread part of an I/O request. 19*ee4cdf7bSDavid Howells */ 20*ee4cdf7bSDavid Howells static void netfs_clear_unread(struct netfs_io_subrequest *subreq) 21*ee4cdf7bSDavid Howells { 22*ee4cdf7bSDavid Howells netfs_reset_iter(subreq); 23*ee4cdf7bSDavid Howells WARN_ON_ONCE(subreq->len - subreq->transferred != iov_iter_count(&subreq->io_iter)); 24*ee4cdf7bSDavid Howells iov_iter_zero(iov_iter_count(&subreq->io_iter), &subreq->io_iter); 25*ee4cdf7bSDavid Howells if (subreq->start + subreq->transferred >= subreq->rreq->i_size) 26*ee4cdf7bSDavid Howells __set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags); 27*ee4cdf7bSDavid Howells } 28*ee4cdf7bSDavid Howells 29*ee4cdf7bSDavid Howells /* 30*ee4cdf7bSDavid Howells * Flush, mark and unlock a folio that's now completely read. If we want to 31*ee4cdf7bSDavid Howells * cache the folio, we set the group to NETFS_FOLIO_COPY_TO_CACHE, mark it 32*ee4cdf7bSDavid Howells * dirty and let writeback handle it. 33*ee4cdf7bSDavid Howells */ 34*ee4cdf7bSDavid Howells static void netfs_unlock_read_folio(struct netfs_io_subrequest *subreq, 35*ee4cdf7bSDavid Howells struct netfs_io_request *rreq, 36*ee4cdf7bSDavid Howells struct folio_queue *folioq, 37*ee4cdf7bSDavid Howells int slot) 38*ee4cdf7bSDavid Howells { 39*ee4cdf7bSDavid Howells struct netfs_folio *finfo; 40*ee4cdf7bSDavid Howells struct folio *folio = folioq_folio(folioq, slot); 41*ee4cdf7bSDavid Howells 42*ee4cdf7bSDavid Howells flush_dcache_folio(folio); 43*ee4cdf7bSDavid Howells folio_mark_uptodate(folio); 44*ee4cdf7bSDavid Howells 45*ee4cdf7bSDavid Howells if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { 46*ee4cdf7bSDavid Howells finfo = netfs_folio_info(folio); 47*ee4cdf7bSDavid Howells if (finfo) { 48*ee4cdf7bSDavid Howells trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); 49*ee4cdf7bSDavid Howells if (finfo->netfs_group) 50*ee4cdf7bSDavid Howells folio_change_private(folio, finfo->netfs_group); 51*ee4cdf7bSDavid Howells else 52*ee4cdf7bSDavid Howells folio_detach_private(folio); 53*ee4cdf7bSDavid Howells kfree(finfo); 54*ee4cdf7bSDavid Howells } 55*ee4cdf7bSDavid Howells 56*ee4cdf7bSDavid Howells if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags)) { 57*ee4cdf7bSDavid Howells if (!WARN_ON_ONCE(folio_get_private(folio) != NULL)) { 58*ee4cdf7bSDavid Howells trace_netfs_folio(folio, netfs_folio_trace_copy_to_cache); 59*ee4cdf7bSDavid Howells folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE); 60*ee4cdf7bSDavid Howells folio_mark_dirty(folio); 61*ee4cdf7bSDavid Howells } 62*ee4cdf7bSDavid Howells } else { 63*ee4cdf7bSDavid Howells trace_netfs_folio(folio, netfs_folio_trace_read_done); 64*ee4cdf7bSDavid Howells } 65*ee4cdf7bSDavid Howells } else { 66*ee4cdf7bSDavid Howells // TODO: Use of PG_private_2 is deprecated. 67*ee4cdf7bSDavid Howells if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags)) 68*ee4cdf7bSDavid Howells netfs_pgpriv2_mark_copy_to_cache(subreq, rreq, folioq, slot); 69*ee4cdf7bSDavid Howells } 70*ee4cdf7bSDavid Howells 71*ee4cdf7bSDavid Howells if (!test_bit(NETFS_RREQ_DONT_UNLOCK_FOLIOS, &rreq->flags)) { 72*ee4cdf7bSDavid Howells if (folio->index == rreq->no_unlock_folio && 73*ee4cdf7bSDavid Howells test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { 74*ee4cdf7bSDavid Howells _debug("no unlock"); 75*ee4cdf7bSDavid Howells } else { 76*ee4cdf7bSDavid Howells trace_netfs_folio(folio, netfs_folio_trace_read_unlock); 77*ee4cdf7bSDavid Howells folio_unlock(folio); 78*ee4cdf7bSDavid Howells } 79*ee4cdf7bSDavid Howells } 80*ee4cdf7bSDavid Howells } 81*ee4cdf7bSDavid Howells 82*ee4cdf7bSDavid Howells /* 83*ee4cdf7bSDavid Howells * Unlock any folios that are now completely read. Returns true if the 84*ee4cdf7bSDavid Howells * subrequest is removed from the list. 85*ee4cdf7bSDavid Howells */ 86*ee4cdf7bSDavid Howells static bool netfs_consume_read_data(struct netfs_io_subrequest *subreq, bool was_async) 87*ee4cdf7bSDavid Howells { 88*ee4cdf7bSDavid Howells struct netfs_io_subrequest *prev, *next; 89*ee4cdf7bSDavid Howells struct netfs_io_request *rreq = subreq->rreq; 90*ee4cdf7bSDavid Howells struct folio_queue *folioq = subreq->curr_folioq; 91*ee4cdf7bSDavid Howells size_t avail, prev_donated, next_donated, fsize, part, excess; 92*ee4cdf7bSDavid Howells loff_t fpos, start; 93*ee4cdf7bSDavid Howells loff_t fend; 94*ee4cdf7bSDavid Howells int slot = subreq->curr_folioq_slot; 95*ee4cdf7bSDavid Howells 96*ee4cdf7bSDavid Howells if (WARN(subreq->transferred > subreq->len, 97*ee4cdf7bSDavid Howells "Subreq overread: R%x[%x] %zu > %zu", 98*ee4cdf7bSDavid Howells rreq->debug_id, subreq->debug_index, 99*ee4cdf7bSDavid Howells subreq->transferred, subreq->len)) 100*ee4cdf7bSDavid Howells subreq->transferred = subreq->len; 101*ee4cdf7bSDavid Howells 102*ee4cdf7bSDavid Howells next_folio: 103*ee4cdf7bSDavid Howells fsize = PAGE_SIZE << subreq->curr_folio_order; 104*ee4cdf7bSDavid Howells fpos = round_down(subreq->start + subreq->consumed, fsize); 105*ee4cdf7bSDavid Howells fend = fpos + fsize; 106*ee4cdf7bSDavid Howells 107*ee4cdf7bSDavid Howells if (WARN_ON_ONCE(!folioq) || 108*ee4cdf7bSDavid Howells WARN_ON_ONCE(!folioq_folio(folioq, slot)) || 109*ee4cdf7bSDavid Howells WARN_ON_ONCE(folioq_folio(folioq, slot)->index != fpos / PAGE_SIZE)) { 110*ee4cdf7bSDavid Howells pr_err("R=%08x[%x] s=%llx-%llx ctl=%zx/%zx/%zx sl=%u\n", 111*ee4cdf7bSDavid Howells rreq->debug_id, subreq->debug_index, 112*ee4cdf7bSDavid Howells subreq->start, subreq->start + subreq->transferred - 1, 113*ee4cdf7bSDavid Howells subreq->consumed, subreq->transferred, subreq->len, 114*ee4cdf7bSDavid Howells slot); 115*ee4cdf7bSDavid Howells if (folioq) { 116*ee4cdf7bSDavid Howells struct folio *folio = folioq_folio(folioq, slot); 117*ee4cdf7bSDavid Howells 118*ee4cdf7bSDavid Howells pr_err("folioq: orders=%02x%02x%02x%02x\n", 119*ee4cdf7bSDavid Howells folioq->orders[0], folioq->orders[1], 120*ee4cdf7bSDavid Howells folioq->orders[2], folioq->orders[3]); 121*ee4cdf7bSDavid Howells if (folio) 122*ee4cdf7bSDavid Howells pr_err("folio: %llx-%llx ix=%llx o=%u qo=%u\n", 123*ee4cdf7bSDavid Howells fpos, fend - 1, folio_pos(folio), folio_order(folio), 124*ee4cdf7bSDavid Howells folioq_folio_order(folioq, slot)); 125*ee4cdf7bSDavid Howells } 126*ee4cdf7bSDavid Howells } 127*ee4cdf7bSDavid Howells 128*ee4cdf7bSDavid Howells donation_changed: 129*ee4cdf7bSDavid Howells /* Try to consume the current folio if we've hit or passed the end of 130*ee4cdf7bSDavid Howells * it. There's a possibility that this subreq doesn't start at the 131*ee4cdf7bSDavid Howells * beginning of the folio, in which case we need to donate to/from the 132*ee4cdf7bSDavid Howells * preceding subreq. 133*ee4cdf7bSDavid Howells * 134*ee4cdf7bSDavid Howells * We also need to include any potential donation back from the 135*ee4cdf7bSDavid Howells * following subreq. 136*ee4cdf7bSDavid Howells */ 137*ee4cdf7bSDavid Howells prev_donated = READ_ONCE(subreq->prev_donated); 138*ee4cdf7bSDavid Howells next_donated = READ_ONCE(subreq->next_donated); 139*ee4cdf7bSDavid Howells if (prev_donated || next_donated) { 140*ee4cdf7bSDavid Howells spin_lock_bh(&rreq->lock); 141*ee4cdf7bSDavid Howells prev_donated = subreq->prev_donated; 142*ee4cdf7bSDavid Howells next_donated = subreq->next_donated; 143*ee4cdf7bSDavid Howells subreq->start -= prev_donated; 144*ee4cdf7bSDavid Howells subreq->len += prev_donated; 145*ee4cdf7bSDavid Howells subreq->transferred += prev_donated; 146*ee4cdf7bSDavid Howells prev_donated = subreq->prev_donated = 0; 147*ee4cdf7bSDavid Howells if (subreq->transferred == subreq->len) { 148*ee4cdf7bSDavid Howells subreq->len += next_donated; 149*ee4cdf7bSDavid Howells subreq->transferred += next_donated; 150*ee4cdf7bSDavid Howells next_donated = subreq->next_donated = 0; 151*ee4cdf7bSDavid Howells } 152*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_add_donations); 153*ee4cdf7bSDavid Howells spin_unlock_bh(&rreq->lock); 154*ee4cdf7bSDavid Howells } 155*ee4cdf7bSDavid Howells 156*ee4cdf7bSDavid Howells avail = subreq->transferred; 157*ee4cdf7bSDavid Howells if (avail == subreq->len) 158*ee4cdf7bSDavid Howells avail += next_donated; 159*ee4cdf7bSDavid Howells start = subreq->start; 160*ee4cdf7bSDavid Howells if (subreq->consumed == 0) { 161*ee4cdf7bSDavid Howells start -= prev_donated; 162*ee4cdf7bSDavid Howells avail += prev_donated; 163*ee4cdf7bSDavid Howells } else { 164*ee4cdf7bSDavid Howells start += subreq->consumed; 165*ee4cdf7bSDavid Howells avail -= subreq->consumed; 166*ee4cdf7bSDavid Howells } 167*ee4cdf7bSDavid Howells part = umin(avail, fsize); 168*ee4cdf7bSDavid Howells 169*ee4cdf7bSDavid Howells trace_netfs_progress(subreq, start, avail, part); 170*ee4cdf7bSDavid Howells 171*ee4cdf7bSDavid Howells if (start + avail >= fend) { 172*ee4cdf7bSDavid Howells if (fpos == start) { 173*ee4cdf7bSDavid Howells /* Flush, unlock and mark for caching any folio we've just read. */ 174*ee4cdf7bSDavid Howells subreq->consumed = fend - subreq->start; 175*ee4cdf7bSDavid Howells netfs_unlock_read_folio(subreq, rreq, folioq, slot); 176*ee4cdf7bSDavid Howells folioq_mark2(folioq, slot); 177*ee4cdf7bSDavid Howells if (subreq->consumed >= subreq->len) 178*ee4cdf7bSDavid Howells goto remove_subreq; 179*ee4cdf7bSDavid Howells } else if (fpos < start) { 180*ee4cdf7bSDavid Howells excess = fend - subreq->start; 181*ee4cdf7bSDavid Howells 182*ee4cdf7bSDavid Howells spin_lock_bh(&rreq->lock); 183*ee4cdf7bSDavid Howells /* If we complete first on a folio split with the 184*ee4cdf7bSDavid Howells * preceding subreq, donate to that subreq - otherwise 185*ee4cdf7bSDavid Howells * we get the responsibility. 186*ee4cdf7bSDavid Howells */ 187*ee4cdf7bSDavid Howells if (subreq->prev_donated != prev_donated) { 188*ee4cdf7bSDavid Howells spin_unlock_bh(&rreq->lock); 189*ee4cdf7bSDavid Howells goto donation_changed; 190*ee4cdf7bSDavid Howells } 191*ee4cdf7bSDavid Howells 192*ee4cdf7bSDavid Howells if (list_is_first(&subreq->rreq_link, &rreq->subrequests)) { 193*ee4cdf7bSDavid Howells spin_unlock_bh(&rreq->lock); 194*ee4cdf7bSDavid Howells pr_err("Can't donate prior to front\n"); 195*ee4cdf7bSDavid Howells goto bad; 196*ee4cdf7bSDavid Howells } 197*ee4cdf7bSDavid Howells 198*ee4cdf7bSDavid Howells prev = list_prev_entry(subreq, rreq_link); 199*ee4cdf7bSDavid Howells WRITE_ONCE(prev->next_donated, prev->next_donated + excess); 200*ee4cdf7bSDavid Howells subreq->start += excess; 201*ee4cdf7bSDavid Howells subreq->len -= excess; 202*ee4cdf7bSDavid Howells subreq->transferred -= excess; 203*ee4cdf7bSDavid Howells trace_netfs_donate(rreq, subreq, prev, excess, 204*ee4cdf7bSDavid Howells netfs_trace_donate_tail_to_prev); 205*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_donate_to_prev); 206*ee4cdf7bSDavid Howells 207*ee4cdf7bSDavid Howells if (subreq->consumed >= subreq->len) 208*ee4cdf7bSDavid Howells goto remove_subreq_locked; 209*ee4cdf7bSDavid Howells spin_unlock_bh(&rreq->lock); 210*ee4cdf7bSDavid Howells } else { 211*ee4cdf7bSDavid Howells pr_err("fpos > start\n"); 212*ee4cdf7bSDavid Howells goto bad; 213*ee4cdf7bSDavid Howells } 214*ee4cdf7bSDavid Howells 215*ee4cdf7bSDavid Howells /* Advance the rolling buffer to the next folio. */ 216*ee4cdf7bSDavid Howells slot++; 217*ee4cdf7bSDavid Howells if (slot >= folioq_nr_slots(folioq)) { 218*ee4cdf7bSDavid Howells slot = 0; 219*ee4cdf7bSDavid Howells folioq = folioq->next; 220*ee4cdf7bSDavid Howells subreq->curr_folioq = folioq; 221*ee4cdf7bSDavid Howells } 222*ee4cdf7bSDavid Howells subreq->curr_folioq_slot = slot; 223*ee4cdf7bSDavid Howells if (folioq && folioq_folio(folioq, slot)) 224*ee4cdf7bSDavid Howells subreq->curr_folio_order = folioq->orders[slot]; 225*ee4cdf7bSDavid Howells if (!was_async) 226*ee4cdf7bSDavid Howells cond_resched(); 227*ee4cdf7bSDavid Howells goto next_folio; 228*ee4cdf7bSDavid Howells } 229*ee4cdf7bSDavid Howells 230*ee4cdf7bSDavid Howells /* Deal with partial progress. */ 231*ee4cdf7bSDavid Howells if (subreq->transferred < subreq->len) 232*ee4cdf7bSDavid Howells return false; 233*ee4cdf7bSDavid Howells 234*ee4cdf7bSDavid Howells /* Donate the remaining downloaded data to one of the neighbouring 235*ee4cdf7bSDavid Howells * subrequests. Note that we may race with them doing the same thing. 236*ee4cdf7bSDavid Howells */ 237*ee4cdf7bSDavid Howells spin_lock_bh(&rreq->lock); 238*ee4cdf7bSDavid Howells 239*ee4cdf7bSDavid Howells if (subreq->prev_donated != prev_donated || 240*ee4cdf7bSDavid Howells subreq->next_donated != next_donated) { 241*ee4cdf7bSDavid Howells spin_unlock_bh(&rreq->lock); 242*ee4cdf7bSDavid Howells cond_resched(); 243*ee4cdf7bSDavid Howells goto donation_changed; 244*ee4cdf7bSDavid Howells } 245*ee4cdf7bSDavid Howells 246*ee4cdf7bSDavid Howells /* Deal with the trickiest case: that this subreq is in the middle of a 247*ee4cdf7bSDavid Howells * folio, not touching either edge, but finishes first. In such a 248*ee4cdf7bSDavid Howells * case, we donate to the previous subreq, if there is one, so that the 249*ee4cdf7bSDavid Howells * donation is only handled when that completes - and remove this 250*ee4cdf7bSDavid Howells * subreq from the list. 251*ee4cdf7bSDavid Howells * 252*ee4cdf7bSDavid Howells * If the previous subreq finished first, we will have acquired their 253*ee4cdf7bSDavid Howells * donation and should be able to unlock folios and/or donate nextwards. 254*ee4cdf7bSDavid Howells */ 255*ee4cdf7bSDavid Howells if (!subreq->consumed && 256*ee4cdf7bSDavid Howells !prev_donated && 257*ee4cdf7bSDavid Howells !list_is_first(&subreq->rreq_link, &rreq->subrequests)) { 258*ee4cdf7bSDavid Howells prev = list_prev_entry(subreq, rreq_link); 259*ee4cdf7bSDavid Howells WRITE_ONCE(prev->next_donated, prev->next_donated + subreq->len); 260*ee4cdf7bSDavid Howells subreq->start += subreq->len; 261*ee4cdf7bSDavid Howells subreq->len = 0; 262*ee4cdf7bSDavid Howells subreq->transferred = 0; 263*ee4cdf7bSDavid Howells trace_netfs_donate(rreq, subreq, prev, subreq->len, 264*ee4cdf7bSDavid Howells netfs_trace_donate_to_prev); 265*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_donate_to_prev); 266*ee4cdf7bSDavid Howells goto remove_subreq_locked; 267*ee4cdf7bSDavid Howells } 268*ee4cdf7bSDavid Howells 269*ee4cdf7bSDavid Howells /* If we can't donate down the chain, donate up the chain instead. */ 270*ee4cdf7bSDavid Howells excess = subreq->len - subreq->consumed + next_donated; 271*ee4cdf7bSDavid Howells 272*ee4cdf7bSDavid Howells if (!subreq->consumed) 273*ee4cdf7bSDavid Howells excess += prev_donated; 274*ee4cdf7bSDavid Howells 275*ee4cdf7bSDavid Howells if (list_is_last(&subreq->rreq_link, &rreq->subrequests)) { 276*ee4cdf7bSDavid Howells rreq->prev_donated = excess; 277*ee4cdf7bSDavid Howells trace_netfs_donate(rreq, subreq, NULL, excess, 278*ee4cdf7bSDavid Howells netfs_trace_donate_to_deferred_next); 279*ee4cdf7bSDavid Howells } else { 280*ee4cdf7bSDavid Howells next = list_next_entry(subreq, rreq_link); 281*ee4cdf7bSDavid Howells WRITE_ONCE(next->prev_donated, excess); 282*ee4cdf7bSDavid Howells trace_netfs_donate(rreq, subreq, next, excess, 283*ee4cdf7bSDavid Howells netfs_trace_donate_to_next); 284*ee4cdf7bSDavid Howells } 285*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_donate_to_next); 286*ee4cdf7bSDavid Howells subreq->len = subreq->consumed; 287*ee4cdf7bSDavid Howells subreq->transferred = subreq->consumed; 288*ee4cdf7bSDavid Howells goto remove_subreq_locked; 289*ee4cdf7bSDavid Howells 290*ee4cdf7bSDavid Howells remove_subreq: 291*ee4cdf7bSDavid Howells spin_lock_bh(&rreq->lock); 292*ee4cdf7bSDavid Howells remove_subreq_locked: 293*ee4cdf7bSDavid Howells subreq->consumed = subreq->len; 294*ee4cdf7bSDavid Howells list_del(&subreq->rreq_link); 295*ee4cdf7bSDavid Howells spin_unlock_bh(&rreq->lock); 296*ee4cdf7bSDavid Howells netfs_put_subrequest(subreq, false, netfs_sreq_trace_put_consumed); 297*ee4cdf7bSDavid Howells return true; 298*ee4cdf7bSDavid Howells 299*ee4cdf7bSDavid Howells bad: 300*ee4cdf7bSDavid Howells /* Errr... prev and next both donated to us, but insufficient to finish 301*ee4cdf7bSDavid Howells * the folio. 302*ee4cdf7bSDavid Howells */ 303*ee4cdf7bSDavid Howells printk("R=%08x[%x] s=%llx-%llx %zx/%zx/%zx\n", 304*ee4cdf7bSDavid Howells rreq->debug_id, subreq->debug_index, 305*ee4cdf7bSDavid Howells subreq->start, subreq->start + subreq->transferred - 1, 306*ee4cdf7bSDavid Howells subreq->consumed, subreq->transferred, subreq->len); 307*ee4cdf7bSDavid Howells printk("folio: %llx-%llx\n", fpos, fend - 1); 308*ee4cdf7bSDavid Howells printk("donated: prev=%zx next=%zx\n", prev_donated, next_donated); 309*ee4cdf7bSDavid Howells printk("s=%llx av=%zx part=%zx\n", start, avail, part); 310*ee4cdf7bSDavid Howells BUG(); 311*ee4cdf7bSDavid Howells } 312*ee4cdf7bSDavid Howells 313*ee4cdf7bSDavid Howells /* 314*ee4cdf7bSDavid Howells * Do page flushing and suchlike after DIO. 315*ee4cdf7bSDavid Howells */ 316*ee4cdf7bSDavid Howells static void netfs_rreq_assess_dio(struct netfs_io_request *rreq) 317*ee4cdf7bSDavid Howells { 318*ee4cdf7bSDavid Howells struct netfs_io_subrequest *subreq; 319*ee4cdf7bSDavid Howells unsigned int i; 320*ee4cdf7bSDavid Howells 321*ee4cdf7bSDavid Howells /* Collect unbuffered reads and direct reads, adding up the transfer 322*ee4cdf7bSDavid Howells * sizes until we find the first short or failed subrequest. 323*ee4cdf7bSDavid Howells */ 324*ee4cdf7bSDavid Howells list_for_each_entry(subreq, &rreq->subrequests, rreq_link) { 325*ee4cdf7bSDavid Howells rreq->transferred += subreq->transferred; 326*ee4cdf7bSDavid Howells 327*ee4cdf7bSDavid Howells if (subreq->transferred < subreq->len || 328*ee4cdf7bSDavid Howells test_bit(NETFS_SREQ_FAILED, &subreq->flags)) { 329*ee4cdf7bSDavid Howells rreq->error = subreq->error; 330*ee4cdf7bSDavid Howells break; 331*ee4cdf7bSDavid Howells } 332*ee4cdf7bSDavid Howells } 333*ee4cdf7bSDavid Howells 334*ee4cdf7bSDavid Howells if (rreq->origin == NETFS_DIO_READ) { 335*ee4cdf7bSDavid Howells for (i = 0; i < rreq->direct_bv_count; i++) { 336*ee4cdf7bSDavid Howells flush_dcache_page(rreq->direct_bv[i].bv_page); 337*ee4cdf7bSDavid Howells // TODO: cifs marks pages in the destination buffer 338*ee4cdf7bSDavid Howells // dirty under some circumstances after a read. Do we 339*ee4cdf7bSDavid Howells // need to do that too? 340*ee4cdf7bSDavid Howells set_page_dirty(rreq->direct_bv[i].bv_page); 341*ee4cdf7bSDavid Howells } 342*ee4cdf7bSDavid Howells } 343*ee4cdf7bSDavid Howells 344*ee4cdf7bSDavid Howells if (rreq->iocb) { 345*ee4cdf7bSDavid Howells rreq->iocb->ki_pos += rreq->transferred; 346*ee4cdf7bSDavid Howells if (rreq->iocb->ki_complete) 347*ee4cdf7bSDavid Howells rreq->iocb->ki_complete( 348*ee4cdf7bSDavid Howells rreq->iocb, rreq->error ? rreq->error : rreq->transferred); 349*ee4cdf7bSDavid Howells } 350*ee4cdf7bSDavid Howells if (rreq->netfs_ops->done) 351*ee4cdf7bSDavid Howells rreq->netfs_ops->done(rreq); 352*ee4cdf7bSDavid Howells if (rreq->origin == NETFS_DIO_READ) 353*ee4cdf7bSDavid Howells inode_dio_end(rreq->inode); 354*ee4cdf7bSDavid Howells } 355*ee4cdf7bSDavid Howells 356*ee4cdf7bSDavid Howells /* 357*ee4cdf7bSDavid Howells * Assess the state of a read request and decide what to do next. 358*ee4cdf7bSDavid Howells * 359*ee4cdf7bSDavid Howells * Note that we're in normal kernel thread context at this point, possibly 360*ee4cdf7bSDavid Howells * running on a workqueue. 361*ee4cdf7bSDavid Howells */ 362*ee4cdf7bSDavid Howells static void netfs_rreq_assess(struct netfs_io_request *rreq) 363*ee4cdf7bSDavid Howells { 364*ee4cdf7bSDavid Howells trace_netfs_rreq(rreq, netfs_rreq_trace_assess); 365*ee4cdf7bSDavid Howells 366*ee4cdf7bSDavid Howells //netfs_rreq_is_still_valid(rreq); 367*ee4cdf7bSDavid Howells 368*ee4cdf7bSDavid Howells if (test_and_clear_bit(NETFS_RREQ_NEED_RETRY, &rreq->flags)) { 369*ee4cdf7bSDavid Howells netfs_retry_reads(rreq); 370*ee4cdf7bSDavid Howells return; 371*ee4cdf7bSDavid Howells } 372*ee4cdf7bSDavid Howells 373*ee4cdf7bSDavid Howells if (rreq->origin == NETFS_DIO_READ || 374*ee4cdf7bSDavid Howells rreq->origin == NETFS_READ_GAPS) 375*ee4cdf7bSDavid Howells netfs_rreq_assess_dio(rreq); 376*ee4cdf7bSDavid Howells task_io_account_read(rreq->transferred); 377*ee4cdf7bSDavid Howells 378*ee4cdf7bSDavid Howells trace_netfs_rreq(rreq, netfs_rreq_trace_wake_ip); 379*ee4cdf7bSDavid Howells clear_bit_unlock(NETFS_RREQ_IN_PROGRESS, &rreq->flags); 380*ee4cdf7bSDavid Howells wake_up_bit(&rreq->flags, NETFS_RREQ_IN_PROGRESS); 381*ee4cdf7bSDavid Howells 382*ee4cdf7bSDavid Howells trace_netfs_rreq(rreq, netfs_rreq_trace_done); 383*ee4cdf7bSDavid Howells netfs_clear_subrequests(rreq, false); 384*ee4cdf7bSDavid Howells netfs_unlock_abandoned_read_pages(rreq); 385*ee4cdf7bSDavid Howells if (unlikely(test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags))) 386*ee4cdf7bSDavid Howells netfs_pgpriv2_write_to_the_cache(rreq); 387*ee4cdf7bSDavid Howells } 388*ee4cdf7bSDavid Howells 389*ee4cdf7bSDavid Howells void netfs_read_termination_worker(struct work_struct *work) 390*ee4cdf7bSDavid Howells { 391*ee4cdf7bSDavid Howells struct netfs_io_request *rreq = 392*ee4cdf7bSDavid Howells container_of(work, struct netfs_io_request, work); 393*ee4cdf7bSDavid Howells netfs_see_request(rreq, netfs_rreq_trace_see_work); 394*ee4cdf7bSDavid Howells netfs_rreq_assess(rreq); 395*ee4cdf7bSDavid Howells netfs_put_request(rreq, false, netfs_rreq_trace_put_work_complete); 396*ee4cdf7bSDavid Howells } 397*ee4cdf7bSDavid Howells 398*ee4cdf7bSDavid Howells /* 399*ee4cdf7bSDavid Howells * Handle the completion of all outstanding I/O operations on a read request. 400*ee4cdf7bSDavid Howells * We inherit a ref from the caller. 401*ee4cdf7bSDavid Howells */ 402*ee4cdf7bSDavid Howells void netfs_rreq_terminated(struct netfs_io_request *rreq, bool was_async) 403*ee4cdf7bSDavid Howells { 404*ee4cdf7bSDavid Howells if (!was_async) 405*ee4cdf7bSDavid Howells return netfs_rreq_assess(rreq); 406*ee4cdf7bSDavid Howells if (!work_pending(&rreq->work)) { 407*ee4cdf7bSDavid Howells netfs_get_request(rreq, netfs_rreq_trace_get_work); 408*ee4cdf7bSDavid Howells if (!queue_work(system_unbound_wq, &rreq->work)) 409*ee4cdf7bSDavid Howells netfs_put_request(rreq, was_async, netfs_rreq_trace_put_work_nq); 410*ee4cdf7bSDavid Howells } 411*ee4cdf7bSDavid Howells } 412*ee4cdf7bSDavid Howells 413*ee4cdf7bSDavid Howells /** 414*ee4cdf7bSDavid Howells * netfs_read_subreq_progress - Note progress of a read operation. 415*ee4cdf7bSDavid Howells * @subreq: The read request that has terminated. 416*ee4cdf7bSDavid Howells * @was_async: True if we're in an asynchronous context. 417*ee4cdf7bSDavid Howells * 418*ee4cdf7bSDavid Howells * This tells the read side of netfs lib that a contributory I/O operation has 419*ee4cdf7bSDavid Howells * made some progress and that it may be possible to unlock some folios. 420*ee4cdf7bSDavid Howells * 421*ee4cdf7bSDavid Howells * Before calling, the filesystem should update subreq->transferred to track 422*ee4cdf7bSDavid Howells * the amount of data copied into the output buffer. 423*ee4cdf7bSDavid Howells * 424*ee4cdf7bSDavid Howells * If @was_async is true, the caller might be running in softirq or interrupt 425*ee4cdf7bSDavid Howells * context and we can't sleep. 426*ee4cdf7bSDavid Howells */ 427*ee4cdf7bSDavid Howells void netfs_read_subreq_progress(struct netfs_io_subrequest *subreq, 428*ee4cdf7bSDavid Howells bool was_async) 429*ee4cdf7bSDavid Howells { 430*ee4cdf7bSDavid Howells struct netfs_io_request *rreq = subreq->rreq; 431*ee4cdf7bSDavid Howells 432*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_progress); 433*ee4cdf7bSDavid Howells 434*ee4cdf7bSDavid Howells if (subreq->transferred > subreq->consumed && 435*ee4cdf7bSDavid Howells (rreq->origin == NETFS_READAHEAD || 436*ee4cdf7bSDavid Howells rreq->origin == NETFS_READPAGE || 437*ee4cdf7bSDavid Howells rreq->origin == NETFS_READ_FOR_WRITE)) { 438*ee4cdf7bSDavid Howells netfs_consume_read_data(subreq, was_async); 439*ee4cdf7bSDavid Howells __clear_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags); 440*ee4cdf7bSDavid Howells } 441*ee4cdf7bSDavid Howells } 442*ee4cdf7bSDavid Howells EXPORT_SYMBOL(netfs_read_subreq_progress); 443*ee4cdf7bSDavid Howells 444*ee4cdf7bSDavid Howells /** 445*ee4cdf7bSDavid Howells * netfs_read_subreq_terminated - Note the termination of an I/O operation. 446*ee4cdf7bSDavid Howells * @subreq: The I/O request that has terminated. 447*ee4cdf7bSDavid Howells * @error: Error code indicating type of completion. 448*ee4cdf7bSDavid Howells * @was_async: The termination was asynchronous 449*ee4cdf7bSDavid Howells * 450*ee4cdf7bSDavid Howells * This tells the read helper that a contributory I/O operation has terminated, 451*ee4cdf7bSDavid Howells * one way or another, and that it should integrate the results. 452*ee4cdf7bSDavid Howells * 453*ee4cdf7bSDavid Howells * The caller indicates the outcome of the operation through @error, supplying 454*ee4cdf7bSDavid Howells * 0 to indicate a successful or retryable transfer (if NETFS_SREQ_NEED_RETRY 455*ee4cdf7bSDavid Howells * is set) or a negative error code. The helper will look after reissuing I/O 456*ee4cdf7bSDavid Howells * operations as appropriate and writing downloaded data to the cache. 457*ee4cdf7bSDavid Howells * 458*ee4cdf7bSDavid Howells * Before calling, the filesystem should update subreq->transferred to track 459*ee4cdf7bSDavid Howells * the amount of data copied into the output buffer. 460*ee4cdf7bSDavid Howells * 461*ee4cdf7bSDavid Howells * If @was_async is true, the caller might be running in softirq or interrupt 462*ee4cdf7bSDavid Howells * context and we can't sleep. 463*ee4cdf7bSDavid Howells */ 464*ee4cdf7bSDavid Howells void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq, 465*ee4cdf7bSDavid Howells int error, bool was_async) 466*ee4cdf7bSDavid Howells { 467*ee4cdf7bSDavid Howells struct netfs_io_request *rreq = subreq->rreq; 468*ee4cdf7bSDavid Howells 469*ee4cdf7bSDavid Howells switch (subreq->source) { 470*ee4cdf7bSDavid Howells case NETFS_READ_FROM_CACHE: 471*ee4cdf7bSDavid Howells netfs_stat(&netfs_n_rh_read_done); 472*ee4cdf7bSDavid Howells break; 473*ee4cdf7bSDavid Howells case NETFS_DOWNLOAD_FROM_SERVER: 474*ee4cdf7bSDavid Howells netfs_stat(&netfs_n_rh_download_done); 475*ee4cdf7bSDavid Howells break; 476*ee4cdf7bSDavid Howells default: 477*ee4cdf7bSDavid Howells break; 478*ee4cdf7bSDavid Howells } 479*ee4cdf7bSDavid Howells 480*ee4cdf7bSDavid Howells if (rreq->origin != NETFS_DIO_READ) { 481*ee4cdf7bSDavid Howells /* Collect buffered reads. 482*ee4cdf7bSDavid Howells * 483*ee4cdf7bSDavid Howells * If the read completed validly short, then we can clear the 484*ee4cdf7bSDavid Howells * tail before going on to unlock the folios. 485*ee4cdf7bSDavid Howells */ 486*ee4cdf7bSDavid Howells if (error == 0 && subreq->transferred < subreq->len && 487*ee4cdf7bSDavid Howells (test_bit(NETFS_SREQ_HIT_EOF, &subreq->flags) || 488*ee4cdf7bSDavid Howells test_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags))) { 489*ee4cdf7bSDavid Howells netfs_clear_unread(subreq); 490*ee4cdf7bSDavid Howells subreq->transferred = subreq->len; 491*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_clear); 492*ee4cdf7bSDavid Howells } 493*ee4cdf7bSDavid Howells if (subreq->transferred > subreq->consumed && 494*ee4cdf7bSDavid Howells (rreq->origin == NETFS_READAHEAD || 495*ee4cdf7bSDavid Howells rreq->origin == NETFS_READPAGE || 496*ee4cdf7bSDavid Howells rreq->origin == NETFS_READ_FOR_WRITE)) { 497*ee4cdf7bSDavid Howells netfs_consume_read_data(subreq, was_async); 498*ee4cdf7bSDavid Howells __clear_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags); 499*ee4cdf7bSDavid Howells } 500*ee4cdf7bSDavid Howells rreq->transferred += subreq->transferred; 501*ee4cdf7bSDavid Howells } 502*ee4cdf7bSDavid Howells 503*ee4cdf7bSDavid Howells /* Deal with retry requests, short reads and errors. If we retry 504*ee4cdf7bSDavid Howells * but don't make progress, we abandon the attempt. 505*ee4cdf7bSDavid Howells */ 506*ee4cdf7bSDavid Howells if (!error && subreq->transferred < subreq->len) { 507*ee4cdf7bSDavid Howells if (test_bit(NETFS_SREQ_HIT_EOF, &subreq->flags)) { 508*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_hit_eof); 509*ee4cdf7bSDavid Howells } else { 510*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_short); 511*ee4cdf7bSDavid Howells if (subreq->transferred > subreq->consumed) { 512*ee4cdf7bSDavid Howells __set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags); 513*ee4cdf7bSDavid Howells __clear_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags); 514*ee4cdf7bSDavid Howells set_bit(NETFS_RREQ_NEED_RETRY, &rreq->flags); 515*ee4cdf7bSDavid Howells } else if (!__test_and_set_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags)) { 516*ee4cdf7bSDavid Howells __set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags); 517*ee4cdf7bSDavid Howells set_bit(NETFS_RREQ_NEED_RETRY, &rreq->flags); 518*ee4cdf7bSDavid Howells } else { 519*ee4cdf7bSDavid Howells __set_bit(NETFS_SREQ_FAILED, &subreq->flags); 520*ee4cdf7bSDavid Howells error = -ENODATA; 521*ee4cdf7bSDavid Howells } 522*ee4cdf7bSDavid Howells } 523*ee4cdf7bSDavid Howells } 524*ee4cdf7bSDavid Howells 525*ee4cdf7bSDavid Howells subreq->error = error; 526*ee4cdf7bSDavid Howells trace_netfs_sreq(subreq, netfs_sreq_trace_terminated); 527*ee4cdf7bSDavid Howells 528*ee4cdf7bSDavid Howells if (unlikely(error < 0)) { 529*ee4cdf7bSDavid Howells trace_netfs_failure(rreq, subreq, error, netfs_fail_read); 530*ee4cdf7bSDavid Howells if (subreq->source == NETFS_READ_FROM_CACHE) { 531*ee4cdf7bSDavid Howells netfs_stat(&netfs_n_rh_read_failed); 532*ee4cdf7bSDavid Howells } else { 533*ee4cdf7bSDavid Howells netfs_stat(&netfs_n_rh_download_failed); 534*ee4cdf7bSDavid Howells set_bit(NETFS_RREQ_FAILED, &rreq->flags); 535*ee4cdf7bSDavid Howells rreq->error = subreq->error; 536*ee4cdf7bSDavid Howells } 537*ee4cdf7bSDavid Howells } 538*ee4cdf7bSDavid Howells 539*ee4cdf7bSDavid Howells if (atomic_dec_and_test(&rreq->nr_outstanding)) 540*ee4cdf7bSDavid Howells netfs_rreq_terminated(rreq, was_async); 541*ee4cdf7bSDavid Howells 542*ee4cdf7bSDavid Howells netfs_put_subrequest(subreq, was_async, netfs_sreq_trace_put_terminated); 543*ee4cdf7bSDavid Howells } 544*ee4cdf7bSDavid Howells EXPORT_SYMBOL(netfs_read_subreq_terminated); 545