| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* Network filesystem read subrequest result collection, assessment and | 
|---|
| 3 | * retrying. | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (C) 2024 Red Hat, Inc. All Rights Reserved. | 
|---|
| 6 | * Written by David Howells (dhowells@redhat.com) | 
|---|
| 7 | */ | 
|---|
| 8 |  | 
|---|
| 9 | #include <linux/export.h> | 
|---|
| 10 | #include <linux/fs.h> | 
|---|
| 11 | #include <linux/mm.h> | 
|---|
| 12 | #include <linux/pagemap.h> | 
|---|
| 13 | #include <linux/slab.h> | 
|---|
| 14 | #include <linux/task_io_accounting_ops.h> | 
|---|
| 15 | #include "internal.h" | 
|---|
| 16 |  | 
|---|
| 17 | /* Notes made in the collector */ | 
|---|
| 18 | #define HIT_PENDING	0x01	/* A front op was still pending */ | 
|---|
| 19 | #define MADE_PROGRESS	0x04	/* Made progress cleaning up a stream or the folio set */ | 
|---|
| 20 | #define BUFFERED	0x08	/* The pagecache needs cleaning up */ | 
|---|
| 21 | #define NEED_RETRY	0x10	/* A front op requests retrying */ | 
|---|
| 22 | #define COPY_TO_CACHE	0x40	/* Need to copy subrequest to cache */ | 
|---|
| 23 | #define ABANDON_SREQ	0x80	/* Need to abandon untransferred part of subrequest */ | 
|---|
| 24 |  | 
|---|
| 25 | /* | 
|---|
| 26 | * Clear the unread part of an I/O request. | 
|---|
| 27 | */ | 
|---|
| 28 | static void netfs_clear_unread(struct netfs_io_subrequest *subreq) | 
|---|
| 29 | { | 
|---|
| 30 | netfs_reset_iter(subreq); | 
|---|
| 31 | WARN_ON_ONCE(subreq->len - subreq->transferred != iov_iter_count(&subreq->io_iter)); | 
|---|
| 32 | iov_iter_zero(bytes: iov_iter_count(i: &subreq->io_iter), &subreq->io_iter); | 
|---|
| 33 | if (subreq->start + subreq->transferred >= subreq->rreq->i_size) | 
|---|
| 34 | __set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags); | 
|---|
| 35 | } | 
|---|
| 36 |  | 
|---|
| 37 | /* | 
|---|
| 38 | * Flush, mark and unlock a folio that's now completely read.  If we want to | 
|---|
| 39 | * cache the folio, we set the group to NETFS_FOLIO_COPY_TO_CACHE, mark it | 
|---|
| 40 | * dirty and let writeback handle it. | 
|---|
| 41 | */ | 
|---|
| 42 | static void netfs_unlock_read_folio(struct netfs_io_request *rreq, | 
|---|
| 43 | struct folio_queue *folioq, | 
|---|
| 44 | int slot) | 
|---|
| 45 | { | 
|---|
| 46 | struct netfs_folio *finfo; | 
|---|
| 47 | struct folio *folio = folioq_folio(folioq, slot); | 
|---|
| 48 |  | 
|---|
| 49 | if (unlikely(folio_pos(folio) < rreq->abandon_to)) { | 
|---|
| 50 | trace_netfs_folio(folio, why: netfs_folio_trace_abandon); | 
|---|
| 51 | goto just_unlock; | 
|---|
| 52 | } | 
|---|
| 53 |  | 
|---|
| 54 | flush_dcache_folio(folio); | 
|---|
| 55 | folio_mark_uptodate(folio); | 
|---|
| 56 |  | 
|---|
| 57 | if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { | 
|---|
| 58 | finfo = netfs_folio_info(folio); | 
|---|
| 59 | if (finfo) { | 
|---|
| 60 | trace_netfs_folio(folio, why: netfs_folio_trace_filled_gaps); | 
|---|
| 61 | if (finfo->netfs_group) | 
|---|
| 62 | folio_change_private(folio, data: finfo->netfs_group); | 
|---|
| 63 | else | 
|---|
| 64 | folio_detach_private(folio); | 
|---|
| 65 | kfree(objp: finfo); | 
|---|
| 66 | } | 
|---|
| 67 |  | 
|---|
| 68 | if (test_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags)) { | 
|---|
| 69 | if (!WARN_ON_ONCE(folio_get_private(folio) != NULL)) { | 
|---|
| 70 | trace_netfs_folio(folio, why: netfs_folio_trace_copy_to_cache); | 
|---|
| 71 | folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE); | 
|---|
| 72 | folio_mark_dirty(folio); | 
|---|
| 73 | } | 
|---|
| 74 | } else { | 
|---|
| 75 | trace_netfs_folio(folio, why: netfs_folio_trace_read_done); | 
|---|
| 76 | } | 
|---|
| 77 |  | 
|---|
| 78 | folioq_clear(folioq, slot); | 
|---|
| 79 | } else { | 
|---|
| 80 | // TODO: Use of PG_private_2 is deprecated. | 
|---|
| 81 | if (test_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags)) | 
|---|
| 82 | netfs_pgpriv2_copy_to_cache(rreq, folio); | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | just_unlock: | 
|---|
| 86 | if (folio->index == rreq->no_unlock_folio && | 
|---|
| 87 | test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { | 
|---|
| 88 | _debug( "no unlock"); | 
|---|
| 89 | } else { | 
|---|
| 90 | trace_netfs_folio(folio, why: netfs_folio_trace_read_unlock); | 
|---|
| 91 | folio_unlock(folio); | 
|---|
| 92 | } | 
|---|
| 93 |  | 
|---|
| 94 | folioq_clear(folioq, slot); | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | /* | 
|---|
| 98 | * Unlock any folios we've finished with. | 
|---|
| 99 | */ | 
|---|
| 100 | static void netfs_read_unlock_folios(struct netfs_io_request *rreq, | 
|---|
| 101 | unsigned int *notes) | 
|---|
| 102 | { | 
|---|
| 103 | struct folio_queue *folioq = rreq->buffer.tail; | 
|---|
| 104 | unsigned long long collected_to = rreq->collected_to; | 
|---|
| 105 | unsigned int slot = rreq->buffer.first_tail_slot; | 
|---|
| 106 |  | 
|---|
| 107 | if (rreq->cleaned_to >= rreq->collected_to) | 
|---|
| 108 | return; | 
|---|
| 109 |  | 
|---|
| 110 | // TODO: Begin decryption | 
|---|
| 111 |  | 
|---|
| 112 | if (slot >= folioq_nr_slots(folioq)) { | 
|---|
| 113 | folioq = rolling_buffer_delete_spent(roll: &rreq->buffer); | 
|---|
| 114 | if (!folioq) { | 
|---|
| 115 | rreq->front_folio_order = 0; | 
|---|
| 116 | return; | 
|---|
| 117 | } | 
|---|
| 118 | slot = 0; | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|
| 121 | for (;;) { | 
|---|
| 122 | struct folio *folio; | 
|---|
| 123 | unsigned long long fpos, fend; | 
|---|
| 124 | unsigned int order; | 
|---|
| 125 | size_t fsize; | 
|---|
| 126 |  | 
|---|
| 127 | if (*notes & COPY_TO_CACHE) | 
|---|
| 128 | set_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, addr: &rreq->flags); | 
|---|
| 129 |  | 
|---|
| 130 | folio = folioq_folio(folioq, slot); | 
|---|
| 131 | if (WARN_ONCE(!folio_test_locked(folio), | 
|---|
| 132 | "R=%08x: folio %lx is not locked\n", | 
|---|
| 133 | rreq->debug_id, folio->index)) | 
|---|
| 134 | trace_netfs_folio(folio, why: netfs_folio_trace_not_locked); | 
|---|
| 135 |  | 
|---|
| 136 | order = folioq_folio_order(folioq, slot); | 
|---|
| 137 | rreq->front_folio_order = order; | 
|---|
| 138 | fsize = PAGE_SIZE << order; | 
|---|
| 139 | fpos = folio_pos(folio); | 
|---|
| 140 | fend = umin(fpos + fsize, rreq->i_size); | 
|---|
| 141 |  | 
|---|
| 142 | trace_netfs_collect_folio(wreq: rreq, folio, fend, collected_to); | 
|---|
| 143 |  | 
|---|
| 144 | /* Unlock any folio we've transferred all of. */ | 
|---|
| 145 | if (collected_to < fend) | 
|---|
| 146 | break; | 
|---|
| 147 |  | 
|---|
| 148 | netfs_unlock_read_folio(rreq, folioq, slot); | 
|---|
| 149 | WRITE_ONCE(rreq->cleaned_to, fpos + fsize); | 
|---|
| 150 | *notes |= MADE_PROGRESS; | 
|---|
| 151 |  | 
|---|
| 152 | clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, addr: &rreq->flags); | 
|---|
| 153 |  | 
|---|
| 154 | /* Clean up the head folioq.  If we clear an entire folioq, then | 
|---|
| 155 | * we can get rid of it provided it's not also the tail folioq | 
|---|
| 156 | * being filled by the issuer. | 
|---|
| 157 | */ | 
|---|
| 158 | folioq_clear(folioq, slot); | 
|---|
| 159 | slot++; | 
|---|
| 160 | if (slot >= folioq_nr_slots(folioq)) { | 
|---|
| 161 | folioq = rolling_buffer_delete_spent(roll: &rreq->buffer); | 
|---|
| 162 | if (!folioq) | 
|---|
| 163 | goto done; | 
|---|
| 164 | slot = 0; | 
|---|
| 165 | trace_netfs_folioq(fq: folioq, trace: netfs_trace_folioq_read_progress); | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | if (fpos + fsize >= collected_to) | 
|---|
| 169 | break; | 
|---|
| 170 | } | 
|---|
| 171 |  | 
|---|
| 172 | rreq->buffer.tail = folioq; | 
|---|
| 173 | done: | 
|---|
| 174 | rreq->buffer.first_tail_slot = slot; | 
|---|
| 175 | } | 
|---|
| 176 |  | 
|---|
| 177 | /* | 
|---|
| 178 | * Collect and assess the results of various read subrequests.  We may need to | 
|---|
| 179 | * retry some of the results. | 
|---|
| 180 | * | 
|---|
| 181 | * Note that we have a sequence of subrequests, which may be drawing on | 
|---|
| 182 | * different sources and may or may not be the same size or starting position | 
|---|
| 183 | * and may not even correspond in boundary alignment. | 
|---|
| 184 | */ | 
|---|
| 185 | static void netfs_collect_read_results(struct netfs_io_request *rreq) | 
|---|
| 186 | { | 
|---|
| 187 | struct netfs_io_subrequest *front, *remove; | 
|---|
| 188 | struct netfs_io_stream *stream = &rreq->io_streams[0]; | 
|---|
| 189 | unsigned int notes; | 
|---|
| 190 |  | 
|---|
| 191 | _enter( "%llx-%llx", rreq->start, rreq->start + rreq->len); | 
|---|
| 192 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_collect); | 
|---|
| 193 | trace_netfs_collect(wreq: rreq); | 
|---|
| 194 |  | 
|---|
| 195 | reassess: | 
|---|
| 196 | if (rreq->origin == NETFS_READAHEAD || | 
|---|
| 197 | rreq->origin == NETFS_READPAGE || | 
|---|
| 198 | rreq->origin == NETFS_READ_FOR_WRITE) | 
|---|
| 199 | notes = BUFFERED; | 
|---|
| 200 | else | 
|---|
| 201 | notes = 0; | 
|---|
| 202 |  | 
|---|
| 203 | /* Remove completed subrequests from the front of the stream and | 
|---|
| 204 | * advance the completion point.  We stop when we hit something that's | 
|---|
| 205 | * in progress.  The issuer thread may be adding stuff to the tail | 
|---|
| 206 | * whilst we're doing this. | 
|---|
| 207 | */ | 
|---|
| 208 | front = READ_ONCE(stream->front); | 
|---|
| 209 | while (front) { | 
|---|
| 210 | size_t transferred; | 
|---|
| 211 |  | 
|---|
| 212 | trace_netfs_collect_sreq(wreq: rreq, subreq: front); | 
|---|
| 213 | _debug( "sreq [%x] %llx %zx/%zx", | 
|---|
| 214 | front->debug_index, front->start, front->transferred, front->len); | 
|---|
| 215 |  | 
|---|
| 216 | if (stream->collected_to < front->start) { | 
|---|
| 217 | trace_netfs_collect_gap(wreq: rreq, stream, jump_to: front->start, type: 'F'); | 
|---|
| 218 | stream->collected_to = front->start; | 
|---|
| 219 | } | 
|---|
| 220 |  | 
|---|
| 221 | if (netfs_check_subreq_in_progress(subreq: front)) | 
|---|
| 222 | notes |= HIT_PENDING; | 
|---|
| 223 | smp_rmb(); /* Read counters after IN_PROGRESS flag. */ | 
|---|
| 224 | transferred = READ_ONCE(front->transferred); | 
|---|
| 225 |  | 
|---|
| 226 | /* If we can now collect the next folio, do so.  We don't want | 
|---|
| 227 | * to defer this as we have to decide whether we need to copy | 
|---|
| 228 | * to the cache or not, and that may differ between adjacent | 
|---|
| 229 | * subreqs. | 
|---|
| 230 | */ | 
|---|
| 231 | if (notes & BUFFERED) { | 
|---|
| 232 | size_t fsize = PAGE_SIZE << rreq->front_folio_order; | 
|---|
| 233 |  | 
|---|
| 234 | /* Clear the tail of a short read. */ | 
|---|
| 235 | if (!(notes & HIT_PENDING) && | 
|---|
| 236 | front->error == 0 && | 
|---|
| 237 | transferred < front->len && | 
|---|
| 238 | (test_bit(NETFS_SREQ_HIT_EOF, &front->flags) || | 
|---|
| 239 | test_bit(NETFS_SREQ_CLEAR_TAIL, &front->flags))) { | 
|---|
| 240 | netfs_clear_unread(subreq: front); | 
|---|
| 241 | transferred = front->transferred = front->len; | 
|---|
| 242 | trace_netfs_sreq(sreq: front, what: netfs_sreq_trace_clear); | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | stream->collected_to = front->start + transferred; | 
|---|
| 246 | rreq->collected_to = stream->collected_to; | 
|---|
| 247 |  | 
|---|
| 248 | if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &front->flags)) | 
|---|
| 249 | notes |= COPY_TO_CACHE; | 
|---|
| 250 |  | 
|---|
| 251 | if (test_bit(NETFS_SREQ_FAILED, &front->flags)) { | 
|---|
| 252 | rreq->abandon_to = front->start + front->len; | 
|---|
| 253 | front->transferred = front->len; | 
|---|
| 254 | transferred = front->len; | 
|---|
| 255 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_set_abandon); | 
|---|
| 256 | } | 
|---|
| 257 | if (front->start + transferred >= rreq->cleaned_to + fsize || | 
|---|
| 258 | test_bit(NETFS_SREQ_HIT_EOF, &front->flags)) | 
|---|
| 259 | netfs_read_unlock_folios(rreq, notes: ¬es); | 
|---|
| 260 | } else { | 
|---|
| 261 | stream->collected_to = front->start + transferred; | 
|---|
| 262 | rreq->collected_to = stream->collected_to; | 
|---|
| 263 | } | 
|---|
| 264 |  | 
|---|
| 265 | /* Stall if the front is still undergoing I/O. */ | 
|---|
| 266 | if (notes & HIT_PENDING) | 
|---|
| 267 | break; | 
|---|
| 268 |  | 
|---|
| 269 | if (test_bit(NETFS_SREQ_FAILED, &front->flags)) { | 
|---|
| 270 | if (!stream->failed) { | 
|---|
| 271 | stream->error = front->error; | 
|---|
| 272 | rreq->error = front->error; | 
|---|
| 273 | set_bit(NETFS_RREQ_FAILED, addr: &rreq->flags); | 
|---|
| 274 | stream->failed = true; | 
|---|
| 275 | } | 
|---|
| 276 | notes |= MADE_PROGRESS | ABANDON_SREQ; | 
|---|
| 277 | } else if (test_bit(NETFS_SREQ_NEED_RETRY, &front->flags)) { | 
|---|
| 278 | stream->need_retry = true; | 
|---|
| 279 | notes |= NEED_RETRY | MADE_PROGRESS; | 
|---|
| 280 | break; | 
|---|
| 281 | } else if (test_bit(NETFS_RREQ_SHORT_TRANSFER, &rreq->flags)) { | 
|---|
| 282 | notes |= MADE_PROGRESS; | 
|---|
| 283 | } else { | 
|---|
| 284 | if (!stream->failed) { | 
|---|
| 285 | stream->transferred += transferred; | 
|---|
| 286 | stream->transferred_valid = true; | 
|---|
| 287 | } | 
|---|
| 288 | if (front->transferred < front->len) | 
|---|
| 289 | set_bit(NETFS_RREQ_SHORT_TRANSFER, addr: &rreq->flags); | 
|---|
| 290 | notes |= MADE_PROGRESS; | 
|---|
| 291 | } | 
|---|
| 292 |  | 
|---|
| 293 | /* Remove if completely consumed. */ | 
|---|
| 294 | stream->source = front->source; | 
|---|
| 295 | spin_lock(lock: &rreq->lock); | 
|---|
| 296 |  | 
|---|
| 297 | remove = front; | 
|---|
| 298 | trace_netfs_sreq(sreq: front, | 
|---|
| 299 | what: notes & ABANDON_SREQ ? | 
|---|
| 300 | netfs_sreq_trace_abandoned : netfs_sreq_trace_consumed); | 
|---|
| 301 | list_del_init(entry: &front->rreq_link); | 
|---|
| 302 | front = list_first_entry_or_null(&stream->subrequests, | 
|---|
| 303 | struct netfs_io_subrequest, rreq_link); | 
|---|
| 304 | stream->front = front; | 
|---|
| 305 | spin_unlock(lock: &rreq->lock); | 
|---|
| 306 | netfs_put_subrequest(subreq: remove, | 
|---|
| 307 | what: notes & ABANDON_SREQ ? | 
|---|
| 308 | netfs_sreq_trace_put_abandon : | 
|---|
| 309 | netfs_sreq_trace_put_done); | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | trace_netfs_collect_stream(wreq: rreq, stream); | 
|---|
| 313 | trace_netfs_collect_state(wreq: rreq, collected_to: rreq->collected_to, notes); | 
|---|
| 314 |  | 
|---|
| 315 | if (!(notes & BUFFERED)) | 
|---|
| 316 | rreq->cleaned_to = rreq->collected_to; | 
|---|
| 317 |  | 
|---|
| 318 | if (notes & NEED_RETRY) | 
|---|
| 319 | goto need_retry; | 
|---|
| 320 | if (notes & MADE_PROGRESS) { | 
|---|
| 321 | netfs_wake_rreq_flag(rreq, NETFS_RREQ_PAUSE, trace: netfs_rreq_trace_unpause); | 
|---|
| 322 | //cond_resched(); | 
|---|
| 323 | goto reassess; | 
|---|
| 324 | } | 
|---|
| 325 |  | 
|---|
| 326 | out: | 
|---|
| 327 | _leave( " = %x", notes); | 
|---|
| 328 | return; | 
|---|
| 329 |  | 
|---|
| 330 | need_retry: | 
|---|
| 331 | /* Okay...  We're going to have to retry parts of the stream.  Note | 
|---|
| 332 | * that any partially completed op will have had any wholly transferred | 
|---|
| 333 | * folios removed from it. | 
|---|
| 334 | */ | 
|---|
| 335 | _debug( "retry"); | 
|---|
| 336 | netfs_retry_reads(rreq); | 
|---|
| 337 | goto out; | 
|---|
| 338 | } | 
|---|
| 339 |  | 
|---|
| 340 | /* | 
|---|
| 341 | * Do page flushing and suchlike after DIO. | 
|---|
| 342 | */ | 
|---|
| 343 | static void netfs_rreq_assess_dio(struct netfs_io_request *rreq) | 
|---|
| 344 | { | 
|---|
| 345 | unsigned int i; | 
|---|
| 346 |  | 
|---|
| 347 | if (rreq->origin == NETFS_UNBUFFERED_READ || | 
|---|
| 348 | rreq->origin == NETFS_DIO_READ) { | 
|---|
| 349 | for (i = 0; i < rreq->direct_bv_count; i++) { | 
|---|
| 350 | flush_dcache_page(page: rreq->direct_bv[i].bv_page); | 
|---|
| 351 | // TODO: cifs marks pages in the destination buffer | 
|---|
| 352 | // dirty under some circumstances after a read.  Do we | 
|---|
| 353 | // need to do that too? | 
|---|
| 354 | set_page_dirty(rreq->direct_bv[i].bv_page); | 
|---|
| 355 | } | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | if (rreq->iocb) { | 
|---|
| 359 | rreq->iocb->ki_pos += rreq->transferred; | 
|---|
| 360 | if (rreq->iocb->ki_complete) { | 
|---|
| 361 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_ki_complete); | 
|---|
| 362 | rreq->iocb->ki_complete( | 
|---|
| 363 | rreq->iocb, rreq->error ? rreq->error : rreq->transferred); | 
|---|
| 364 | } | 
|---|
| 365 | } | 
|---|
| 366 | if (rreq->netfs_ops->done) | 
|---|
| 367 | rreq->netfs_ops->done(rreq); | 
|---|
| 368 | if (rreq->origin == NETFS_UNBUFFERED_READ || | 
|---|
| 369 | rreq->origin == NETFS_DIO_READ) | 
|---|
| 370 | inode_dio_end(inode: rreq->inode); | 
|---|
| 371 | } | 
|---|
| 372 |  | 
|---|
| 373 | /* | 
|---|
| 374 | * Do processing after reading a monolithic single object. | 
|---|
| 375 | */ | 
|---|
| 376 | static void netfs_rreq_assess_single(struct netfs_io_request *rreq) | 
|---|
| 377 | { | 
|---|
| 378 | struct netfs_io_stream *stream = &rreq->io_streams[0]; | 
|---|
| 379 |  | 
|---|
| 380 | if (!rreq->error && stream->source == NETFS_DOWNLOAD_FROM_SERVER && | 
|---|
| 381 | fscache_resources_valid(&rreq->cache_resources)) { | 
|---|
| 382 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_dirty); | 
|---|
| 383 | netfs_single_mark_inode_dirty(inode: rreq->inode); | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|
| 386 | if (rreq->iocb) { | 
|---|
| 387 | rreq->iocb->ki_pos += rreq->transferred; | 
|---|
| 388 | if (rreq->iocb->ki_complete) { | 
|---|
| 389 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_ki_complete); | 
|---|
| 390 | rreq->iocb->ki_complete( | 
|---|
| 391 | rreq->iocb, rreq->error ? rreq->error : rreq->transferred); | 
|---|
| 392 | } | 
|---|
| 393 | } | 
|---|
| 394 | if (rreq->netfs_ops->done) | 
|---|
| 395 | rreq->netfs_ops->done(rreq); | 
|---|
| 396 | } | 
|---|
| 397 |  | 
|---|
| 398 | /* | 
|---|
| 399 | * Perform the collection of subrequests and folios. | 
|---|
| 400 | * | 
|---|
| 401 | * Note that we're in normal kernel thread context at this point, possibly | 
|---|
| 402 | * running on a workqueue. | 
|---|
| 403 | */ | 
|---|
| 404 | bool netfs_read_collection(struct netfs_io_request *rreq) | 
|---|
| 405 | { | 
|---|
| 406 | struct netfs_io_stream *stream = &rreq->io_streams[0]; | 
|---|
| 407 |  | 
|---|
| 408 | netfs_collect_read_results(rreq); | 
|---|
| 409 |  | 
|---|
| 410 | /* We're done when the app thread has finished posting subreqs and the | 
|---|
| 411 | * queue is empty. | 
|---|
| 412 | */ | 
|---|
| 413 | if (!test_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags)) | 
|---|
| 414 | return false; | 
|---|
| 415 | smp_rmb(); /* Read ALL_QUEUED before subreq lists. */ | 
|---|
| 416 |  | 
|---|
| 417 | if (!list_empty(head: &stream->subrequests)) | 
|---|
| 418 | return false; | 
|---|
| 419 |  | 
|---|
| 420 | /* Okay, declare that all I/O is complete. */ | 
|---|
| 421 | rreq->transferred = stream->transferred; | 
|---|
| 422 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_complete); | 
|---|
| 423 |  | 
|---|
| 424 | //netfs_rreq_is_still_valid(rreq); | 
|---|
| 425 |  | 
|---|
| 426 | switch (rreq->origin) { | 
|---|
| 427 | case NETFS_UNBUFFERED_READ: | 
|---|
| 428 | case NETFS_DIO_READ: | 
|---|
| 429 | case NETFS_READ_GAPS: | 
|---|
| 430 | netfs_rreq_assess_dio(rreq); | 
|---|
| 431 | break; | 
|---|
| 432 | case NETFS_READ_SINGLE: | 
|---|
| 433 | netfs_rreq_assess_single(rreq); | 
|---|
| 434 | break; | 
|---|
| 435 | default: | 
|---|
| 436 | break; | 
|---|
| 437 | } | 
|---|
| 438 | task_io_account_read(bytes: rreq->transferred); | 
|---|
| 439 |  | 
|---|
| 440 | netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, trace: netfs_rreq_trace_wake_ip); | 
|---|
| 441 | /* As we cleared NETFS_RREQ_IN_PROGRESS, we acquired its ref. */ | 
|---|
| 442 |  | 
|---|
| 443 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_done); | 
|---|
| 444 | netfs_clear_subrequests(rreq); | 
|---|
| 445 | netfs_unlock_abandoned_read_pages(rreq); | 
|---|
| 446 | if (unlikely(rreq->copy_to_cache)) | 
|---|
| 447 | netfs_pgpriv2_end_copy_to_cache(rreq); | 
|---|
| 448 | return true; | 
|---|
| 449 | } | 
|---|
| 450 |  | 
|---|
| 451 | void netfs_read_collection_worker(struct work_struct *work) | 
|---|
| 452 | { | 
|---|
| 453 | struct netfs_io_request *rreq = container_of(work, struct netfs_io_request, work); | 
|---|
| 454 |  | 
|---|
| 455 | netfs_see_request(rreq, what: netfs_rreq_trace_see_work); | 
|---|
| 456 | if (netfs_check_rreq_in_progress(rreq)) { | 
|---|
| 457 | if (netfs_read_collection(rreq)) | 
|---|
| 458 | /* Drop the ref from the IN_PROGRESS flag. */ | 
|---|
| 459 | netfs_put_request(rreq, what: netfs_rreq_trace_put_work_ip); | 
|---|
| 460 | else | 
|---|
| 461 | netfs_see_request(rreq, what: netfs_rreq_trace_see_work_complete); | 
|---|
| 462 | } | 
|---|
| 463 | } | 
|---|
| 464 |  | 
|---|
| 465 | /** | 
|---|
| 466 | * netfs_read_subreq_progress - Note progress of a read operation. | 
|---|
| 467 | * @subreq: The read request that has terminated. | 
|---|
| 468 | * | 
|---|
| 469 | * This tells the read side of netfs lib that a contributory I/O operation has | 
|---|
| 470 | * made some progress and that it may be possible to unlock some folios. | 
|---|
| 471 | * | 
|---|
| 472 | * Before calling, the filesystem should update subreq->transferred to track | 
|---|
| 473 | * the amount of data copied into the output buffer. | 
|---|
| 474 | */ | 
|---|
| 475 | void netfs_read_subreq_progress(struct netfs_io_subrequest *subreq) | 
|---|
| 476 | { | 
|---|
| 477 | struct netfs_io_request *rreq = subreq->rreq; | 
|---|
| 478 | struct netfs_io_stream *stream = &rreq->io_streams[0]; | 
|---|
| 479 | size_t fsize = PAGE_SIZE << rreq->front_folio_order; | 
|---|
| 480 |  | 
|---|
| 481 | trace_netfs_sreq(sreq: subreq, what: netfs_sreq_trace_progress); | 
|---|
| 482 |  | 
|---|
| 483 | /* If we are at the head of the queue, wake up the collector, | 
|---|
| 484 | * getting a ref to it if we were the ones to do so. | 
|---|
| 485 | */ | 
|---|
| 486 | if (subreq->start + subreq->transferred > rreq->cleaned_to + fsize && | 
|---|
| 487 | (rreq->origin == NETFS_READAHEAD || | 
|---|
| 488 | rreq->origin == NETFS_READPAGE || | 
|---|
| 489 | rreq->origin == NETFS_READ_FOR_WRITE) && | 
|---|
| 490 | list_is_first(list: &subreq->rreq_link, head: &stream->subrequests) | 
|---|
| 491 | ) { | 
|---|
| 492 | __set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); | 
|---|
| 493 | netfs_wake_collector(rreq); | 
|---|
| 494 | } | 
|---|
| 495 | } | 
|---|
| 496 | EXPORT_SYMBOL(netfs_read_subreq_progress); | 
|---|
| 497 |  | 
|---|
| 498 | /** | 
|---|
| 499 | * netfs_read_subreq_terminated - Note the termination of an I/O operation. | 
|---|
| 500 | * @subreq: The I/O request that has terminated. | 
|---|
| 501 | * | 
|---|
| 502 | * This tells the read helper that a contributory I/O operation has terminated, | 
|---|
| 503 | * one way or another, and that it should integrate the results. | 
|---|
| 504 | * | 
|---|
| 505 | * The caller indicates the outcome of the operation through @subreq->error, | 
|---|
| 506 | * supplying 0 to indicate a successful or retryable transfer (if | 
|---|
| 507 | * NETFS_SREQ_NEED_RETRY is set) or a negative error code.  The helper will | 
|---|
| 508 | * look after reissuing I/O operations as appropriate and writing downloaded | 
|---|
| 509 | * data to the cache. | 
|---|
| 510 | * | 
|---|
| 511 | * Before calling, the filesystem should update subreq->transferred to track | 
|---|
| 512 | * the amount of data copied into the output buffer. | 
|---|
| 513 | */ | 
|---|
| 514 | void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq) | 
|---|
| 515 | { | 
|---|
| 516 | struct netfs_io_request *rreq = subreq->rreq; | 
|---|
| 517 |  | 
|---|
| 518 | switch (subreq->source) { | 
|---|
| 519 | case NETFS_READ_FROM_CACHE: | 
|---|
| 520 | netfs_stat(&netfs_n_rh_read_done); | 
|---|
| 521 | break; | 
|---|
| 522 | case NETFS_DOWNLOAD_FROM_SERVER: | 
|---|
| 523 | netfs_stat(&netfs_n_rh_download_done); | 
|---|
| 524 | break; | 
|---|
| 525 | default: | 
|---|
| 526 | break; | 
|---|
| 527 | } | 
|---|
| 528 |  | 
|---|
| 529 | /* Deal with retry requests, short reads and errors.  If we retry | 
|---|
| 530 | * but don't make progress, we abandon the attempt. | 
|---|
| 531 | */ | 
|---|
| 532 | if (!subreq->error && subreq->transferred < subreq->len) { | 
|---|
| 533 | if (test_bit(NETFS_SREQ_HIT_EOF, &subreq->flags)) { | 
|---|
| 534 | trace_netfs_sreq(sreq: subreq, what: netfs_sreq_trace_hit_eof); | 
|---|
| 535 | } else if (test_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags)) { | 
|---|
| 536 | trace_netfs_sreq(sreq: subreq, what: netfs_sreq_trace_need_clear); | 
|---|
| 537 | } else if (test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags)) { | 
|---|
| 538 | trace_netfs_sreq(sreq: subreq, what: netfs_sreq_trace_need_retry); | 
|---|
| 539 | } else if (test_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags)) { | 
|---|
| 540 | __set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags); | 
|---|
| 541 | trace_netfs_sreq(sreq: subreq, what: netfs_sreq_trace_partial_read); | 
|---|
| 542 | } else { | 
|---|
| 543 | __set_bit(NETFS_SREQ_FAILED, &subreq->flags); | 
|---|
| 544 | subreq->error = -ENODATA; | 
|---|
| 545 | trace_netfs_sreq(sreq: subreq, what: netfs_sreq_trace_short); | 
|---|
| 546 | } | 
|---|
| 547 | } | 
|---|
| 548 |  | 
|---|
| 549 | if (unlikely(subreq->error < 0)) { | 
|---|
| 550 | trace_netfs_failure(rreq, sreq: subreq, error: subreq->error, what: netfs_fail_read); | 
|---|
| 551 | if (subreq->source == NETFS_READ_FROM_CACHE) { | 
|---|
| 552 | netfs_stat(&netfs_n_rh_read_failed); | 
|---|
| 553 | __set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags); | 
|---|
| 554 | } else { | 
|---|
| 555 | netfs_stat(&netfs_n_rh_download_failed); | 
|---|
| 556 | __set_bit(NETFS_SREQ_FAILED, &subreq->flags); | 
|---|
| 557 | } | 
|---|
| 558 | trace_netfs_rreq(rreq, what: netfs_rreq_trace_set_pause); | 
|---|
| 559 | set_bit(NETFS_RREQ_PAUSE, addr: &rreq->flags); | 
|---|
| 560 | } | 
|---|
| 561 |  | 
|---|
| 562 | trace_netfs_sreq(sreq: subreq, what: netfs_sreq_trace_terminated); | 
|---|
| 563 | netfs_subreq_clear_in_progress(subreq); | 
|---|
| 564 | netfs_put_subrequest(subreq, what: netfs_sreq_trace_put_terminated); | 
|---|
| 565 | } | 
|---|
| 566 | EXPORT_SYMBOL(netfs_read_subreq_terminated); | 
|---|
| 567 |  | 
|---|
| 568 | /* | 
|---|
| 569 | * Handle termination of a read from the cache. | 
|---|
| 570 | */ | 
|---|
| 571 | void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error) | 
|---|
| 572 | { | 
|---|
| 573 | struct netfs_io_subrequest *subreq = priv; | 
|---|
| 574 |  | 
|---|
| 575 | if (transferred_or_error > 0) { | 
|---|
| 576 | subreq->error = 0; | 
|---|
| 577 | if (transferred_or_error > 0) { | 
|---|
| 578 | subreq->transferred += transferred_or_error; | 
|---|
| 579 | __set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); | 
|---|
| 580 | } | 
|---|
| 581 | } else { | 
|---|
| 582 | subreq->error = transferred_or_error; | 
|---|
| 583 | } | 
|---|
| 584 | netfs_read_subreq_terminated(subreq); | 
|---|
| 585 | } | 
|---|
| 586 |  | 
|---|