162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Network filesystem high-level read support. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/pagemap.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/uio.h> 1562306a36Sopenharmony_ci#include <linux/sched/mm.h> 1662306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h> 1762306a36Sopenharmony_ci#include "internal.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Clear the unread part of an I/O request. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic void netfs_clear_unread(struct netfs_io_subrequest *subreq) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct iov_iter iter; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci iov_iter_xarray(&iter, ITER_DEST, &subreq->rreq->mapping->i_pages, 2762306a36Sopenharmony_ci subreq->start + subreq->transferred, 2862306a36Sopenharmony_ci subreq->len - subreq->transferred); 2962306a36Sopenharmony_ci iov_iter_zero(iov_iter_count(&iter), &iter); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error, 3362306a36Sopenharmony_ci bool was_async) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct netfs_io_subrequest *subreq = priv; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci netfs_subreq_terminated(subreq, transferred_or_error, was_async); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Issue a read against the cache. 4262306a36Sopenharmony_ci * - Eats the caller's ref on subreq. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistatic void netfs_read_from_cache(struct netfs_io_request *rreq, 4562306a36Sopenharmony_ci struct netfs_io_subrequest *subreq, 4662306a36Sopenharmony_ci enum netfs_read_from_hole read_hole) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct netfs_cache_resources *cres = &rreq->cache_resources; 4962306a36Sopenharmony_ci struct iov_iter iter; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_read); 5262306a36Sopenharmony_ci iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages, 5362306a36Sopenharmony_ci subreq->start + subreq->transferred, 5462306a36Sopenharmony_ci subreq->len - subreq->transferred); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci cres->ops->read(cres, subreq->start, &iter, read_hole, 5762306a36Sopenharmony_ci netfs_cache_read_terminated, subreq); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Fill a subrequest region with zeroes. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic void netfs_fill_with_zeroes(struct netfs_io_request *rreq, 6462306a36Sopenharmony_ci struct netfs_io_subrequest *subreq) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_zero); 6762306a36Sopenharmony_ci __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags); 6862306a36Sopenharmony_ci netfs_subreq_terminated(subreq, 0, false); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * Ask the netfs to issue a read request to the server for us. 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * The netfs is expected to read from subreq->pos + subreq->transferred to 7562306a36Sopenharmony_ci * subreq->pos + subreq->len - 1. It may not backtrack and write data into the 7662306a36Sopenharmony_ci * buffer prior to the transferred point as it might clobber dirty data 7762306a36Sopenharmony_ci * obtained from the cache. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Alternatively, the netfs is allowed to indicate one of two things: 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * - NETFS_SREQ_SHORT_READ: A short read - it will get called again to try and 8262306a36Sopenharmony_ci * make progress. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * - NETFS_SREQ_CLEAR_TAIL: A short read - the rest of the buffer will be 8562306a36Sopenharmony_ci * cleared. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistatic void netfs_read_from_server(struct netfs_io_request *rreq, 8862306a36Sopenharmony_ci struct netfs_io_subrequest *subreq) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_download); 9162306a36Sopenharmony_ci rreq->netfs_ops->issue_read(subreq); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Release those waiting. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic void netfs_rreq_completed(struct netfs_io_request *rreq, bool was_async) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci trace_netfs_rreq(rreq, netfs_rreq_trace_done); 10062306a36Sopenharmony_ci netfs_clear_subrequests(rreq, was_async); 10162306a36Sopenharmony_ci netfs_put_request(rreq, was_async, netfs_rreq_trace_put_complete); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * Deal with the completion of writing the data to the cache. We have to clear 10662306a36Sopenharmony_ci * the PG_fscache bits on the folios involved and release the caller's ref. 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * May be called in softirq mode and we inherit a ref from the caller. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic void netfs_rreq_unmark_after_write(struct netfs_io_request *rreq, 11162306a36Sopenharmony_ci bool was_async) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct netfs_io_subrequest *subreq; 11462306a36Sopenharmony_ci struct folio *folio; 11562306a36Sopenharmony_ci pgoff_t unlocked = 0; 11662306a36Sopenharmony_ci bool have_unlocked = false; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci rcu_read_lock(); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci list_for_each_entry(subreq, &rreq->subrequests, rreq_link) { 12162306a36Sopenharmony_ci XA_STATE(xas, &rreq->mapping->i_pages, subreq->start / PAGE_SIZE); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci xas_for_each(&xas, folio, (subreq->start + subreq->len - 1) / PAGE_SIZE) { 12462306a36Sopenharmony_ci if (xas_retry(&xas, folio)) 12562306a36Sopenharmony_ci continue; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* We might have multiple writes from the same huge 12862306a36Sopenharmony_ci * folio, but we mustn't unlock a folio more than once. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (have_unlocked && folio_index(folio) <= unlocked) 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci unlocked = folio_index(folio); 13362306a36Sopenharmony_ci folio_end_fscache(folio); 13462306a36Sopenharmony_ci have_unlocked = true; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci rcu_read_unlock(); 13962306a36Sopenharmony_ci netfs_rreq_completed(rreq, was_async); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void netfs_rreq_copy_terminated(void *priv, ssize_t transferred_or_error, 14362306a36Sopenharmony_ci bool was_async) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct netfs_io_subrequest *subreq = priv; 14662306a36Sopenharmony_ci struct netfs_io_request *rreq = subreq->rreq; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (IS_ERR_VALUE(transferred_or_error)) { 14962306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_write_failed); 15062306a36Sopenharmony_ci trace_netfs_failure(rreq, subreq, transferred_or_error, 15162306a36Sopenharmony_ci netfs_fail_copy_to_cache); 15262306a36Sopenharmony_ci } else { 15362306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_write_done); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_write_term); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* If we decrement nr_copy_ops to 0, the ref belongs to us. */ 15962306a36Sopenharmony_ci if (atomic_dec_and_test(&rreq->nr_copy_ops)) 16062306a36Sopenharmony_ci netfs_rreq_unmark_after_write(rreq, was_async); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci netfs_put_subrequest(subreq, was_async, netfs_sreq_trace_put_terminated); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * Perform any outstanding writes to the cache. We inherit a ref from the 16762306a36Sopenharmony_ci * caller. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistatic void netfs_rreq_do_write_to_cache(struct netfs_io_request *rreq) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct netfs_cache_resources *cres = &rreq->cache_resources; 17262306a36Sopenharmony_ci struct netfs_io_subrequest *subreq, *next, *p; 17362306a36Sopenharmony_ci struct iov_iter iter; 17462306a36Sopenharmony_ci int ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci trace_netfs_rreq(rreq, netfs_rreq_trace_copy); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* We don't want terminating writes trying to wake us up whilst we're 17962306a36Sopenharmony_ci * still going through the list. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci atomic_inc(&rreq->nr_copy_ops); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci list_for_each_entry_safe(subreq, p, &rreq->subrequests, rreq_link) { 18462306a36Sopenharmony_ci if (!test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags)) { 18562306a36Sopenharmony_ci list_del_init(&subreq->rreq_link); 18662306a36Sopenharmony_ci netfs_put_subrequest(subreq, false, 18762306a36Sopenharmony_ci netfs_sreq_trace_put_no_copy); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci list_for_each_entry(subreq, &rreq->subrequests, rreq_link) { 19262306a36Sopenharmony_ci /* Amalgamate adjacent writes */ 19362306a36Sopenharmony_ci while (!list_is_last(&subreq->rreq_link, &rreq->subrequests)) { 19462306a36Sopenharmony_ci next = list_next_entry(subreq, rreq_link); 19562306a36Sopenharmony_ci if (next->start != subreq->start + subreq->len) 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci subreq->len += next->len; 19862306a36Sopenharmony_ci list_del_init(&next->rreq_link); 19962306a36Sopenharmony_ci netfs_put_subrequest(next, false, 20062306a36Sopenharmony_ci netfs_sreq_trace_put_merged); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ret = cres->ops->prepare_write(cres, &subreq->start, &subreq->len, 20462306a36Sopenharmony_ci rreq->i_size, true); 20562306a36Sopenharmony_ci if (ret < 0) { 20662306a36Sopenharmony_ci trace_netfs_failure(rreq, subreq, ret, netfs_fail_prepare_write); 20762306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_write_skip); 20862306a36Sopenharmony_ci continue; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci iov_iter_xarray(&iter, ITER_SOURCE, &rreq->mapping->i_pages, 21262306a36Sopenharmony_ci subreq->start, subreq->len); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci atomic_inc(&rreq->nr_copy_ops); 21562306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_write); 21662306a36Sopenharmony_ci netfs_get_subrequest(subreq, netfs_sreq_trace_get_copy_to_cache); 21762306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_write); 21862306a36Sopenharmony_ci cres->ops->write(cres, subreq->start, &iter, 21962306a36Sopenharmony_ci netfs_rreq_copy_terminated, subreq); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* If we decrement nr_copy_ops to 0, the usage ref belongs to us. */ 22362306a36Sopenharmony_ci if (atomic_dec_and_test(&rreq->nr_copy_ops)) 22462306a36Sopenharmony_ci netfs_rreq_unmark_after_write(rreq, false); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void netfs_rreq_write_to_cache_work(struct work_struct *work) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct netfs_io_request *rreq = 23062306a36Sopenharmony_ci container_of(work, struct netfs_io_request, work); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci netfs_rreq_do_write_to_cache(rreq); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void netfs_rreq_write_to_cache(struct netfs_io_request *rreq) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci rreq->work.func = netfs_rreq_write_to_cache_work; 23862306a36Sopenharmony_ci if (!queue_work(system_unbound_wq, &rreq->work)) 23962306a36Sopenharmony_ci BUG(); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* 24362306a36Sopenharmony_ci * Handle a short read. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_cistatic void netfs_rreq_short_read(struct netfs_io_request *rreq, 24662306a36Sopenharmony_ci struct netfs_io_subrequest *subreq) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci __clear_bit(NETFS_SREQ_SHORT_IO, &subreq->flags); 24962306a36Sopenharmony_ci __set_bit(NETFS_SREQ_SEEK_DATA_READ, &subreq->flags); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_short_read); 25262306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_resubmit_short); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci netfs_get_subrequest(subreq, netfs_sreq_trace_get_short_read); 25562306a36Sopenharmony_ci atomic_inc(&rreq->nr_outstanding); 25662306a36Sopenharmony_ci if (subreq->source == NETFS_READ_FROM_CACHE) 25762306a36Sopenharmony_ci netfs_read_from_cache(rreq, subreq, NETFS_READ_HOLE_CLEAR); 25862306a36Sopenharmony_ci else 25962306a36Sopenharmony_ci netfs_read_from_server(rreq, subreq); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Resubmit any short or failed operations. Returns true if we got the rreq 26462306a36Sopenharmony_ci * ref back. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_cistatic bool netfs_rreq_perform_resubmissions(struct netfs_io_request *rreq) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct netfs_io_subrequest *subreq; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci WARN_ON(in_interrupt()); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci trace_netfs_rreq(rreq, netfs_rreq_trace_resubmit); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* We don't want terminating submissions trying to wake us up whilst 27562306a36Sopenharmony_ci * we're still going through the list. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci atomic_inc(&rreq->nr_outstanding); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci __clear_bit(NETFS_RREQ_INCOMPLETE_IO, &rreq->flags); 28062306a36Sopenharmony_ci list_for_each_entry(subreq, &rreq->subrequests, rreq_link) { 28162306a36Sopenharmony_ci if (subreq->error) { 28262306a36Sopenharmony_ci if (subreq->source != NETFS_READ_FROM_CACHE) 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci subreq->source = NETFS_DOWNLOAD_FROM_SERVER; 28562306a36Sopenharmony_ci subreq->error = 0; 28662306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_download_instead); 28762306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_download_instead); 28862306a36Sopenharmony_ci netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit); 28962306a36Sopenharmony_ci atomic_inc(&rreq->nr_outstanding); 29062306a36Sopenharmony_ci netfs_read_from_server(rreq, subreq); 29162306a36Sopenharmony_ci } else if (test_bit(NETFS_SREQ_SHORT_IO, &subreq->flags)) { 29262306a36Sopenharmony_ci netfs_rreq_short_read(rreq, subreq); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* If we decrement nr_outstanding to 0, the usage ref belongs to us. */ 29762306a36Sopenharmony_ci if (atomic_dec_and_test(&rreq->nr_outstanding)) 29862306a36Sopenharmony_ci return true; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci wake_up_var(&rreq->nr_outstanding); 30162306a36Sopenharmony_ci return false; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/* 30562306a36Sopenharmony_ci * Check to see if the data read is still valid. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_cistatic void netfs_rreq_is_still_valid(struct netfs_io_request *rreq) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct netfs_io_subrequest *subreq; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!rreq->netfs_ops->is_still_valid || 31262306a36Sopenharmony_ci rreq->netfs_ops->is_still_valid(rreq)) 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci list_for_each_entry(subreq, &rreq->subrequests, rreq_link) { 31662306a36Sopenharmony_ci if (subreq->source == NETFS_READ_FROM_CACHE) { 31762306a36Sopenharmony_ci subreq->error = -ESTALE; 31862306a36Sopenharmony_ci __set_bit(NETFS_RREQ_INCOMPLETE_IO, &rreq->flags); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/* 32462306a36Sopenharmony_ci * Assess the state of a read request and decide what to do next. 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * Note that we could be in an ordinary kernel thread, on a workqueue or in 32762306a36Sopenharmony_ci * softirq context at this point. We inherit a ref from the caller. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_cistatic void netfs_rreq_assess(struct netfs_io_request *rreq, bool was_async) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci trace_netfs_rreq(rreq, netfs_rreq_trace_assess); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciagain: 33462306a36Sopenharmony_ci netfs_rreq_is_still_valid(rreq); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!test_bit(NETFS_RREQ_FAILED, &rreq->flags) && 33762306a36Sopenharmony_ci test_bit(NETFS_RREQ_INCOMPLETE_IO, &rreq->flags)) { 33862306a36Sopenharmony_ci if (netfs_rreq_perform_resubmissions(rreq)) 33962306a36Sopenharmony_ci goto again; 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci netfs_rreq_unlock_folios(rreq); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci clear_bit_unlock(NETFS_RREQ_IN_PROGRESS, &rreq->flags); 34662306a36Sopenharmony_ci wake_up_bit(&rreq->flags, NETFS_RREQ_IN_PROGRESS); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (test_bit(NETFS_RREQ_COPY_TO_CACHE, &rreq->flags)) 34962306a36Sopenharmony_ci return netfs_rreq_write_to_cache(rreq); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci netfs_rreq_completed(rreq, was_async); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void netfs_rreq_work(struct work_struct *work) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct netfs_io_request *rreq = 35762306a36Sopenharmony_ci container_of(work, struct netfs_io_request, work); 35862306a36Sopenharmony_ci netfs_rreq_assess(rreq, false); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* 36262306a36Sopenharmony_ci * Handle the completion of all outstanding I/O operations on a read request. 36362306a36Sopenharmony_ci * We inherit a ref from the caller. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_cistatic void netfs_rreq_terminated(struct netfs_io_request *rreq, 36662306a36Sopenharmony_ci bool was_async) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci if (test_bit(NETFS_RREQ_INCOMPLETE_IO, &rreq->flags) && 36962306a36Sopenharmony_ci was_async) { 37062306a36Sopenharmony_ci if (!queue_work(system_unbound_wq, &rreq->work)) 37162306a36Sopenharmony_ci BUG(); 37262306a36Sopenharmony_ci } else { 37362306a36Sopenharmony_ci netfs_rreq_assess(rreq, was_async); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/** 37862306a36Sopenharmony_ci * netfs_subreq_terminated - Note the termination of an I/O operation. 37962306a36Sopenharmony_ci * @subreq: The I/O request that has terminated. 38062306a36Sopenharmony_ci * @transferred_or_error: The amount of data transferred or an error code. 38162306a36Sopenharmony_ci * @was_async: The termination was asynchronous 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * This tells the read helper that a contributory I/O operation has terminated, 38462306a36Sopenharmony_ci * one way or another, and that it should integrate the results. 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * The caller indicates in @transferred_or_error the outcome of the operation, 38762306a36Sopenharmony_ci * supplying a positive value to indicate the number of bytes transferred, 0 to 38862306a36Sopenharmony_ci * indicate a failure to transfer anything that should be retried or a negative 38962306a36Sopenharmony_ci * error code. The helper will look after reissuing I/O operations as 39062306a36Sopenharmony_ci * appropriate and writing downloaded data to the cache. 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * If @was_async is true, the caller might be running in softirq or interrupt 39362306a36Sopenharmony_ci * context and we can't sleep. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_civoid netfs_subreq_terminated(struct netfs_io_subrequest *subreq, 39662306a36Sopenharmony_ci ssize_t transferred_or_error, 39762306a36Sopenharmony_ci bool was_async) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct netfs_io_request *rreq = subreq->rreq; 40062306a36Sopenharmony_ci int u; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci _enter("[%u]{%llx,%lx},%zd", 40362306a36Sopenharmony_ci subreq->debug_index, subreq->start, subreq->flags, 40462306a36Sopenharmony_ci transferred_or_error); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci switch (subreq->source) { 40762306a36Sopenharmony_ci case NETFS_READ_FROM_CACHE: 40862306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_read_done); 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case NETFS_DOWNLOAD_FROM_SERVER: 41162306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_download_done); 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci default: 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (IS_ERR_VALUE(transferred_or_error)) { 41862306a36Sopenharmony_ci subreq->error = transferred_or_error; 41962306a36Sopenharmony_ci trace_netfs_failure(rreq, subreq, transferred_or_error, 42062306a36Sopenharmony_ci netfs_fail_read); 42162306a36Sopenharmony_ci goto failed; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (WARN(transferred_or_error > subreq->len - subreq->transferred, 42562306a36Sopenharmony_ci "Subreq overread: R%x[%x] %zd > %zu - %zu", 42662306a36Sopenharmony_ci rreq->debug_id, subreq->debug_index, 42762306a36Sopenharmony_ci transferred_or_error, subreq->len, subreq->transferred)) 42862306a36Sopenharmony_ci transferred_or_error = subreq->len - subreq->transferred; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci subreq->error = 0; 43162306a36Sopenharmony_ci subreq->transferred += transferred_or_error; 43262306a36Sopenharmony_ci if (subreq->transferred < subreq->len) 43362306a36Sopenharmony_ci goto incomplete; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cicomplete: 43662306a36Sopenharmony_ci __clear_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags); 43762306a36Sopenharmony_ci if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags)) 43862306a36Sopenharmony_ci set_bit(NETFS_RREQ_COPY_TO_CACHE, &rreq->flags); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ciout: 44162306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_terminated); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* If we decrement nr_outstanding to 0, the ref belongs to us. */ 44462306a36Sopenharmony_ci u = atomic_dec_return(&rreq->nr_outstanding); 44562306a36Sopenharmony_ci if (u == 0) 44662306a36Sopenharmony_ci netfs_rreq_terminated(rreq, was_async); 44762306a36Sopenharmony_ci else if (u == 1) 44862306a36Sopenharmony_ci wake_up_var(&rreq->nr_outstanding); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci netfs_put_subrequest(subreq, was_async, netfs_sreq_trace_put_terminated); 45162306a36Sopenharmony_ci return; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciincomplete: 45462306a36Sopenharmony_ci if (test_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags)) { 45562306a36Sopenharmony_ci netfs_clear_unread(subreq); 45662306a36Sopenharmony_ci subreq->transferred = subreq->len; 45762306a36Sopenharmony_ci goto complete; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (transferred_or_error == 0) { 46162306a36Sopenharmony_ci if (__test_and_set_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags)) { 46262306a36Sopenharmony_ci subreq->error = -ENODATA; 46362306a36Sopenharmony_ci goto failed; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci __clear_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci __set_bit(NETFS_SREQ_SHORT_IO, &subreq->flags); 47062306a36Sopenharmony_ci set_bit(NETFS_RREQ_INCOMPLETE_IO, &rreq->flags); 47162306a36Sopenharmony_ci goto out; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cifailed: 47462306a36Sopenharmony_ci if (subreq->source == NETFS_READ_FROM_CACHE) { 47562306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_read_failed); 47662306a36Sopenharmony_ci set_bit(NETFS_RREQ_INCOMPLETE_IO, &rreq->flags); 47762306a36Sopenharmony_ci } else { 47862306a36Sopenharmony_ci netfs_stat(&netfs_n_rh_download_failed); 47962306a36Sopenharmony_ci set_bit(NETFS_RREQ_FAILED, &rreq->flags); 48062306a36Sopenharmony_ci rreq->error = subreq->error; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci goto out; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ciEXPORT_SYMBOL(netfs_subreq_terminated); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic enum netfs_io_source netfs_cache_prepare_read(struct netfs_io_subrequest *subreq, 48762306a36Sopenharmony_ci loff_t i_size) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct netfs_io_request *rreq = subreq->rreq; 49062306a36Sopenharmony_ci struct netfs_cache_resources *cres = &rreq->cache_resources; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (cres->ops) 49362306a36Sopenharmony_ci return cres->ops->prepare_read(subreq, i_size); 49462306a36Sopenharmony_ci if (subreq->start >= rreq->i_size) 49562306a36Sopenharmony_ci return NETFS_FILL_WITH_ZEROES; 49662306a36Sopenharmony_ci return NETFS_DOWNLOAD_FROM_SERVER; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* 50062306a36Sopenharmony_ci * Work out what sort of subrequest the next one will be. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_cistatic enum netfs_io_source 50362306a36Sopenharmony_cinetfs_rreq_prepare_read(struct netfs_io_request *rreq, 50462306a36Sopenharmony_ci struct netfs_io_subrequest *subreq) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci enum netfs_io_source source; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci _enter("%llx-%llx,%llx", subreq->start, subreq->start + subreq->len, rreq->i_size); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci source = netfs_cache_prepare_read(subreq, rreq->i_size); 51162306a36Sopenharmony_ci if (source == NETFS_INVALID_READ) 51262306a36Sopenharmony_ci goto out; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (source == NETFS_DOWNLOAD_FROM_SERVER) { 51562306a36Sopenharmony_ci /* Call out to the netfs to let it shrink the request to fit 51662306a36Sopenharmony_ci * its own I/O sizes and boundaries. If it shinks it here, it 51762306a36Sopenharmony_ci * will be called again to make simultaneous calls; if it wants 51862306a36Sopenharmony_ci * to make serial calls, it can indicate a short read and then 51962306a36Sopenharmony_ci * we will call it again. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci if (subreq->len > rreq->i_size - subreq->start) 52262306a36Sopenharmony_ci subreq->len = rreq->i_size - subreq->start; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (rreq->netfs_ops->clamp_length && 52562306a36Sopenharmony_ci !rreq->netfs_ops->clamp_length(subreq)) { 52662306a36Sopenharmony_ci source = NETFS_INVALID_READ; 52762306a36Sopenharmony_ci goto out; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (WARN_ON(subreq->len == 0)) 53262306a36Sopenharmony_ci source = NETFS_INVALID_READ; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciout: 53562306a36Sopenharmony_ci subreq->source = source; 53662306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_prepare); 53762306a36Sopenharmony_ci return source; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* 54162306a36Sopenharmony_ci * Slice off a piece of a read request and submit an I/O request for it. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_cistatic bool netfs_rreq_submit_slice(struct netfs_io_request *rreq, 54462306a36Sopenharmony_ci unsigned int *_debug_index) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct netfs_io_subrequest *subreq; 54762306a36Sopenharmony_ci enum netfs_io_source source; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci subreq = netfs_alloc_subrequest(rreq); 55062306a36Sopenharmony_ci if (!subreq) 55162306a36Sopenharmony_ci return false; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci subreq->debug_index = (*_debug_index)++; 55462306a36Sopenharmony_ci subreq->start = rreq->start + rreq->submitted; 55562306a36Sopenharmony_ci subreq->len = rreq->len - rreq->submitted; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci _debug("slice %llx,%zx,%zx", subreq->start, subreq->len, rreq->submitted); 55862306a36Sopenharmony_ci list_add_tail(&subreq->rreq_link, &rreq->subrequests); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Call out to the cache to find out what it can do with the remaining 56162306a36Sopenharmony_ci * subset. It tells us in subreq->flags what it decided should be done 56262306a36Sopenharmony_ci * and adjusts subreq->len down if the subset crosses a cache boundary. 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * Then when we hand the subset, it can choose to take a subset of that 56562306a36Sopenharmony_ci * (the starts must coincide), in which case, we go around the loop 56662306a36Sopenharmony_ci * again and ask it to download the next piece. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci source = netfs_rreq_prepare_read(rreq, subreq); 56962306a36Sopenharmony_ci if (source == NETFS_INVALID_READ) 57062306a36Sopenharmony_ci goto subreq_failed; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci atomic_inc(&rreq->nr_outstanding); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci rreq->submitted += subreq->len; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci trace_netfs_sreq(subreq, netfs_sreq_trace_submit); 57762306a36Sopenharmony_ci switch (source) { 57862306a36Sopenharmony_ci case NETFS_FILL_WITH_ZEROES: 57962306a36Sopenharmony_ci netfs_fill_with_zeroes(rreq, subreq); 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci case NETFS_DOWNLOAD_FROM_SERVER: 58262306a36Sopenharmony_ci netfs_read_from_server(rreq, subreq); 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci case NETFS_READ_FROM_CACHE: 58562306a36Sopenharmony_ci netfs_read_from_cache(rreq, subreq, NETFS_READ_HOLE_IGNORE); 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci default: 58862306a36Sopenharmony_ci BUG(); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return true; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cisubreq_failed: 59462306a36Sopenharmony_ci rreq->error = subreq->error; 59562306a36Sopenharmony_ci netfs_put_subrequest(subreq, false, netfs_sreq_trace_put_failed); 59662306a36Sopenharmony_ci return false; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* 60062306a36Sopenharmony_ci * Begin the process of reading in a chunk of data, where that data may be 60162306a36Sopenharmony_ci * stitched together from multiple sources, including multiple servers and the 60262306a36Sopenharmony_ci * local cache. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ciint netfs_begin_read(struct netfs_io_request *rreq, bool sync) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci unsigned int debug_index = 0; 60762306a36Sopenharmony_ci int ret; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci _enter("R=%x %llx-%llx", 61062306a36Sopenharmony_ci rreq->debug_id, rreq->start, rreq->start + rreq->len - 1); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (rreq->len == 0) { 61362306a36Sopenharmony_ci pr_err("Zero-sized read [R=%x]\n", rreq->debug_id); 61462306a36Sopenharmony_ci netfs_put_request(rreq, false, netfs_rreq_trace_put_zero_len); 61562306a36Sopenharmony_ci return -EIO; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci INIT_WORK(&rreq->work, netfs_rreq_work); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (sync) 62162306a36Sopenharmony_ci netfs_get_request(rreq, netfs_rreq_trace_get_hold); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* Chop the read into slices according to what the cache and the netfs 62462306a36Sopenharmony_ci * want and submit each one. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ci atomic_set(&rreq->nr_outstanding, 1); 62762306a36Sopenharmony_ci do { 62862306a36Sopenharmony_ci if (!netfs_rreq_submit_slice(rreq, &debug_index)) 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci } while (rreq->submitted < rreq->len); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (sync) { 63462306a36Sopenharmony_ci /* Keep nr_outstanding incremented so that the ref always belongs to 63562306a36Sopenharmony_ci * us, and the service code isn't punted off to a random thread pool to 63662306a36Sopenharmony_ci * process. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci for (;;) { 63962306a36Sopenharmony_ci wait_var_event(&rreq->nr_outstanding, 64062306a36Sopenharmony_ci atomic_read(&rreq->nr_outstanding) == 1); 64162306a36Sopenharmony_ci netfs_rreq_assess(rreq, false); 64262306a36Sopenharmony_ci if (!test_bit(NETFS_RREQ_IN_PROGRESS, &rreq->flags)) 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci cond_resched(); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ret = rreq->error; 64862306a36Sopenharmony_ci if (ret == 0 && rreq->submitted < rreq->len) { 64962306a36Sopenharmony_ci trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read); 65062306a36Sopenharmony_ci ret = -EIO; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci netfs_put_request(rreq, false, netfs_rreq_trace_put_hold); 65362306a36Sopenharmony_ci } else { 65462306a36Sopenharmony_ci /* If we decrement nr_outstanding to 0, the ref belongs to us. */ 65562306a36Sopenharmony_ci if (atomic_dec_and_test(&rreq->nr_outstanding)) 65662306a36Sopenharmony_ci netfs_rreq_assess(rreq, false); 65762306a36Sopenharmony_ci ret = 0; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci return ret; 66062306a36Sopenharmony_ci} 661