162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Network filesystem high-level buffered 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/export.h>
962306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h>
1062306a36Sopenharmony_ci#include "internal.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * Unlock the folios in a read operation.  We need to set PG_fscache on any
1462306a36Sopenharmony_ci * folios we're going to write back before we unlock them.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_civoid netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct netfs_io_subrequest *subreq;
1962306a36Sopenharmony_ci	struct folio *folio;
2062306a36Sopenharmony_ci	pgoff_t start_page = rreq->start / PAGE_SIZE;
2162306a36Sopenharmony_ci	pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1;
2262306a36Sopenharmony_ci	size_t account = 0;
2362306a36Sopenharmony_ci	bool subreq_failed = false;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	XA_STATE(xas, &rreq->mapping->i_pages, start_page);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (test_bit(NETFS_RREQ_FAILED, &rreq->flags)) {
2862306a36Sopenharmony_ci		__clear_bit(NETFS_RREQ_COPY_TO_CACHE, &rreq->flags);
2962306a36Sopenharmony_ci		list_for_each_entry(subreq, &rreq->subrequests, rreq_link) {
3062306a36Sopenharmony_ci			__clear_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
3162306a36Sopenharmony_ci		}
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Walk through the pagecache and the I/O request lists simultaneously.
3562306a36Sopenharmony_ci	 * We may have a mixture of cached and uncached sections and we only
3662306a36Sopenharmony_ci	 * really want to write out the uncached sections.  This is slightly
3762306a36Sopenharmony_ci	 * complicated by the possibility that we might have huge pages with a
3862306a36Sopenharmony_ci	 * mixture inside.
3962306a36Sopenharmony_ci	 */
4062306a36Sopenharmony_ci	subreq = list_first_entry(&rreq->subrequests,
4162306a36Sopenharmony_ci				  struct netfs_io_subrequest, rreq_link);
4262306a36Sopenharmony_ci	subreq_failed = (subreq->error < 0);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	trace_netfs_rreq(rreq, netfs_rreq_trace_unlock);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	rcu_read_lock();
4762306a36Sopenharmony_ci	xas_for_each(&xas, folio, last_page) {
4862306a36Sopenharmony_ci		loff_t pg_end;
4962306a36Sopenharmony_ci		bool pg_failed = false;
5062306a36Sopenharmony_ci		bool folio_started;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci		if (xas_retry(&xas, folio))
5362306a36Sopenharmony_ci			continue;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		pg_end = folio_pos(folio) + folio_size(folio) - 1;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		folio_started = false;
5862306a36Sopenharmony_ci		for (;;) {
5962306a36Sopenharmony_ci			loff_t sreq_end;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci			if (!subreq) {
6262306a36Sopenharmony_ci				pg_failed = true;
6362306a36Sopenharmony_ci				break;
6462306a36Sopenharmony_ci			}
6562306a36Sopenharmony_ci			if (!folio_started && test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags)) {
6662306a36Sopenharmony_ci				folio_start_fscache(folio);
6762306a36Sopenharmony_ci				folio_started = true;
6862306a36Sopenharmony_ci			}
6962306a36Sopenharmony_ci			pg_failed |= subreq_failed;
7062306a36Sopenharmony_ci			sreq_end = subreq->start + subreq->len - 1;
7162306a36Sopenharmony_ci			if (pg_end < sreq_end)
7262306a36Sopenharmony_ci				break;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci			account += subreq->transferred;
7562306a36Sopenharmony_ci			if (!list_is_last(&subreq->rreq_link, &rreq->subrequests)) {
7662306a36Sopenharmony_ci				subreq = list_next_entry(subreq, rreq_link);
7762306a36Sopenharmony_ci				subreq_failed = (subreq->error < 0);
7862306a36Sopenharmony_ci			} else {
7962306a36Sopenharmony_ci				subreq = NULL;
8062306a36Sopenharmony_ci				subreq_failed = false;
8162306a36Sopenharmony_ci			}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci			if (pg_end == sreq_end)
8462306a36Sopenharmony_ci				break;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		if (!pg_failed) {
8862306a36Sopenharmony_ci			flush_dcache_folio(folio);
8962306a36Sopenharmony_ci			folio_mark_uptodate(folio);
9062306a36Sopenharmony_ci		}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		if (!test_bit(NETFS_RREQ_DONT_UNLOCK_FOLIOS, &rreq->flags)) {
9362306a36Sopenharmony_ci			if (folio_index(folio) == rreq->no_unlock_folio &&
9462306a36Sopenharmony_ci			    test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags))
9562306a36Sopenharmony_ci				_debug("no unlock");
9662306a36Sopenharmony_ci			else
9762306a36Sopenharmony_ci				folio_unlock(folio);
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	rcu_read_unlock();
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	task_io_account_read(account);
10362306a36Sopenharmony_ci	if (rreq->netfs_ops->done)
10462306a36Sopenharmony_ci		rreq->netfs_ops->done(rreq);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void netfs_cache_expand_readahead(struct netfs_io_request *rreq,
10862306a36Sopenharmony_ci					 loff_t *_start, size_t *_len, loff_t i_size)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct netfs_cache_resources *cres = &rreq->cache_resources;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (cres->ops && cres->ops->expand_readahead)
11362306a36Sopenharmony_ci		cres->ops->expand_readahead(cres, _start, _len, i_size);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void netfs_rreq_expand(struct netfs_io_request *rreq,
11762306a36Sopenharmony_ci			      struct readahead_control *ractl)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	/* Give the cache a chance to change the request parameters.  The
12062306a36Sopenharmony_ci	 * resultant request must contain the original region.
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	netfs_cache_expand_readahead(rreq, &rreq->start, &rreq->len, rreq->i_size);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Give the netfs a chance to change the request parameters.  The
12562306a36Sopenharmony_ci	 * resultant request must contain the original region.
12662306a36Sopenharmony_ci	 */
12762306a36Sopenharmony_ci	if (rreq->netfs_ops->expand_readahead)
12862306a36Sopenharmony_ci		rreq->netfs_ops->expand_readahead(rreq);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Expand the request if the cache wants it to start earlier.  Note
13162306a36Sopenharmony_ci	 * that the expansion may get further extended if the VM wishes to
13262306a36Sopenharmony_ci	 * insert THPs and the preferred start and/or end wind up in the middle
13362306a36Sopenharmony_ci	 * of THPs.
13462306a36Sopenharmony_ci	 *
13562306a36Sopenharmony_ci	 * If this is the case, however, the THP size should be an integer
13662306a36Sopenharmony_ci	 * multiple of the cache granule size, so we get a whole number of
13762306a36Sopenharmony_ci	 * granules to deal with.
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	if (rreq->start  != readahead_pos(ractl) ||
14062306a36Sopenharmony_ci	    rreq->len != readahead_length(ractl)) {
14162306a36Sopenharmony_ci		readahead_expand(ractl, rreq->start, rreq->len);
14262306a36Sopenharmony_ci		rreq->start  = readahead_pos(ractl);
14362306a36Sopenharmony_ci		rreq->len = readahead_length(ractl);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		trace_netfs_read(rreq, readahead_pos(ractl), readahead_length(ractl),
14662306a36Sopenharmony_ci				 netfs_read_trace_expanded);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/**
15162306a36Sopenharmony_ci * netfs_readahead - Helper to manage a read request
15262306a36Sopenharmony_ci * @ractl: The description of the readahead request
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * Fulfil a readahead request by drawing data from the cache if possible, or
15562306a36Sopenharmony_ci * the netfs if not.  Space beyond the EOF is zero-filled.  Multiple I/O
15662306a36Sopenharmony_ci * requests from different sources will get munged together.  If necessary, the
15762306a36Sopenharmony_ci * readahead window can be expanded in either direction to a more convenient
15862306a36Sopenharmony_ci * alighment for RPC efficiency or to make storage in the cache feasible.
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * The calling netfs must initialise a netfs context contiguous to the vfs
16162306a36Sopenharmony_ci * inode before calling this.
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * This is usable whether or not caching is enabled.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_civoid netfs_readahead(struct readahead_control *ractl)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct netfs_io_request *rreq;
16862306a36Sopenharmony_ci	struct netfs_inode *ctx = netfs_inode(ractl->mapping->host);
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	_enter("%lx,%x", readahead_index(ractl), readahead_count(ractl));
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (readahead_count(ractl) == 0)
17462306a36Sopenharmony_ci		return;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	rreq = netfs_alloc_request(ractl->mapping, ractl->file,
17762306a36Sopenharmony_ci				   readahead_pos(ractl),
17862306a36Sopenharmony_ci				   readahead_length(ractl),
17962306a36Sopenharmony_ci				   NETFS_READAHEAD);
18062306a36Sopenharmony_ci	if (IS_ERR(rreq))
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (ctx->ops->begin_cache_operation) {
18462306a36Sopenharmony_ci		ret = ctx->ops->begin_cache_operation(rreq);
18562306a36Sopenharmony_ci		if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
18662306a36Sopenharmony_ci			goto cleanup_free;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	netfs_stat(&netfs_n_rh_readahead);
19062306a36Sopenharmony_ci	trace_netfs_read(rreq, readahead_pos(ractl), readahead_length(ractl),
19162306a36Sopenharmony_ci			 netfs_read_trace_readahead);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	netfs_rreq_expand(rreq, ractl);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Drop the refs on the folios here rather than in the cache or
19662306a36Sopenharmony_ci	 * filesystem.  The locks will be dropped in netfs_rreq_unlock().
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	while (readahead_folio(ractl))
19962306a36Sopenharmony_ci		;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	netfs_begin_read(rreq, false);
20262306a36Sopenharmony_ci	return;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cicleanup_free:
20562306a36Sopenharmony_ci	netfs_put_request(rreq, false, netfs_rreq_trace_put_failed);
20662306a36Sopenharmony_ci	return;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ciEXPORT_SYMBOL(netfs_readahead);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/**
21162306a36Sopenharmony_ci * netfs_read_folio - Helper to manage a read_folio request
21262306a36Sopenharmony_ci * @file: The file to read from
21362306a36Sopenharmony_ci * @folio: The folio to read
21462306a36Sopenharmony_ci *
21562306a36Sopenharmony_ci * Fulfil a read_folio request by drawing data from the cache if
21662306a36Sopenharmony_ci * possible, or the netfs if not.  Space beyond the EOF is zero-filled.
21762306a36Sopenharmony_ci * Multiple I/O requests from different sources will get munged together.
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * The calling netfs must initialise a netfs context contiguous to the vfs
22062306a36Sopenharmony_ci * inode before calling this.
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci * This is usable whether or not caching is enabled.
22362306a36Sopenharmony_ci */
22462306a36Sopenharmony_ciint netfs_read_folio(struct file *file, struct folio *folio)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct address_space *mapping = folio_file_mapping(folio);
22762306a36Sopenharmony_ci	struct netfs_io_request *rreq;
22862306a36Sopenharmony_ci	struct netfs_inode *ctx = netfs_inode(mapping->host);
22962306a36Sopenharmony_ci	int ret;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	_enter("%lx", folio_index(folio));
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	rreq = netfs_alloc_request(mapping, file,
23462306a36Sopenharmony_ci				   folio_file_pos(folio), folio_size(folio),
23562306a36Sopenharmony_ci				   NETFS_READPAGE);
23662306a36Sopenharmony_ci	if (IS_ERR(rreq)) {
23762306a36Sopenharmony_ci		ret = PTR_ERR(rreq);
23862306a36Sopenharmony_ci		goto alloc_error;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (ctx->ops->begin_cache_operation) {
24262306a36Sopenharmony_ci		ret = ctx->ops->begin_cache_operation(rreq);
24362306a36Sopenharmony_ci		if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
24462306a36Sopenharmony_ci			goto discard;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	netfs_stat(&netfs_n_rh_readpage);
24862306a36Sopenharmony_ci	trace_netfs_read(rreq, rreq->start, rreq->len, netfs_read_trace_readpage);
24962306a36Sopenharmony_ci	return netfs_begin_read(rreq, true);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cidiscard:
25262306a36Sopenharmony_ci	netfs_put_request(rreq, false, netfs_rreq_trace_put_discard);
25362306a36Sopenharmony_cialloc_error:
25462306a36Sopenharmony_ci	folio_unlock(folio);
25562306a36Sopenharmony_ci	return ret;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ciEXPORT_SYMBOL(netfs_read_folio);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * Prepare a folio for writing without reading first
26162306a36Sopenharmony_ci * @folio: The folio being prepared
26262306a36Sopenharmony_ci * @pos: starting position for the write
26362306a36Sopenharmony_ci * @len: length of write
26462306a36Sopenharmony_ci * @always_fill: T if the folio should always be completely filled/cleared
26562306a36Sopenharmony_ci *
26662306a36Sopenharmony_ci * In some cases, write_begin doesn't need to read at all:
26762306a36Sopenharmony_ci * - full folio write
26862306a36Sopenharmony_ci * - write that lies in a folio that is completely beyond EOF
26962306a36Sopenharmony_ci * - write that covers the folio from start to EOF or beyond it
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * If any of these criteria are met, then zero out the unwritten parts
27262306a36Sopenharmony_ci * of the folio and return true. Otherwise, return false.
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_cistatic bool netfs_skip_folio_read(struct folio *folio, loff_t pos, size_t len,
27562306a36Sopenharmony_ci				 bool always_fill)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct inode *inode = folio_inode(folio);
27862306a36Sopenharmony_ci	loff_t i_size = i_size_read(inode);
27962306a36Sopenharmony_ci	size_t offset = offset_in_folio(folio, pos);
28062306a36Sopenharmony_ci	size_t plen = folio_size(folio);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (unlikely(always_fill)) {
28362306a36Sopenharmony_ci		if (pos - offset + len <= i_size)
28462306a36Sopenharmony_ci			return false; /* Page entirely before EOF */
28562306a36Sopenharmony_ci		zero_user_segment(&folio->page, 0, plen);
28662306a36Sopenharmony_ci		folio_mark_uptodate(folio);
28762306a36Sopenharmony_ci		return true;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Full folio write */
29162306a36Sopenharmony_ci	if (offset == 0 && len >= plen)
29262306a36Sopenharmony_ci		return true;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* Page entirely beyond the end of the file */
29562306a36Sopenharmony_ci	if (pos - offset >= i_size)
29662306a36Sopenharmony_ci		goto zero_out;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Write that covers from the start of the folio to EOF or beyond */
29962306a36Sopenharmony_ci	if (offset == 0 && (pos + len) >= i_size)
30062306a36Sopenharmony_ci		goto zero_out;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return false;
30362306a36Sopenharmony_cizero_out:
30462306a36Sopenharmony_ci	zero_user_segments(&folio->page, 0, offset, offset + len, plen);
30562306a36Sopenharmony_ci	return true;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/**
30962306a36Sopenharmony_ci * netfs_write_begin - Helper to prepare for writing
31062306a36Sopenharmony_ci * @ctx: The netfs context
31162306a36Sopenharmony_ci * @file: The file to read from
31262306a36Sopenharmony_ci * @mapping: The mapping to read from
31362306a36Sopenharmony_ci * @pos: File position at which the write will begin
31462306a36Sopenharmony_ci * @len: The length of the write (may extend beyond the end of the folio chosen)
31562306a36Sopenharmony_ci * @_folio: Where to put the resultant folio
31662306a36Sopenharmony_ci * @_fsdata: Place for the netfs to store a cookie
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * Pre-read data for a write-begin request by drawing data from the cache if
31962306a36Sopenharmony_ci * possible, or the netfs if not.  Space beyond the EOF is zero-filled.
32062306a36Sopenharmony_ci * Multiple I/O requests from different sources will get munged together.  If
32162306a36Sopenharmony_ci * necessary, the readahead window can be expanded in either direction to a
32262306a36Sopenharmony_ci * more convenient alighment for RPC efficiency or to make storage in the cache
32362306a36Sopenharmony_ci * feasible.
32462306a36Sopenharmony_ci *
32562306a36Sopenharmony_ci * The calling netfs must provide a table of operations, only one of which,
32662306a36Sopenharmony_ci * issue_op, is mandatory.
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * The check_write_begin() operation can be provided to check for and flush
32962306a36Sopenharmony_ci * conflicting writes once the folio is grabbed and locked.  It is passed a
33062306a36Sopenharmony_ci * pointer to the fsdata cookie that gets returned to the VM to be passed to
33162306a36Sopenharmony_ci * write_end.  It is permitted to sleep.  It should return 0 if the request
33262306a36Sopenharmony_ci * should go ahead or it may return an error.  It may also unlock and put the
33362306a36Sopenharmony_ci * folio, provided it sets ``*foliop`` to NULL, in which case a return of 0
33462306a36Sopenharmony_ci * will cause the folio to be re-got and the process to be retried.
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * The calling netfs must initialise a netfs context contiguous to the vfs
33762306a36Sopenharmony_ci * inode before calling this.
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * This is usable whether or not caching is enabled.
34062306a36Sopenharmony_ci */
34162306a36Sopenharmony_ciint netfs_write_begin(struct netfs_inode *ctx,
34262306a36Sopenharmony_ci		      struct file *file, struct address_space *mapping,
34362306a36Sopenharmony_ci		      loff_t pos, unsigned int len, struct folio **_folio,
34462306a36Sopenharmony_ci		      void **_fsdata)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct netfs_io_request *rreq;
34762306a36Sopenharmony_ci	struct folio *folio;
34862306a36Sopenharmony_ci	pgoff_t index = pos >> PAGE_SHIFT;
34962306a36Sopenharmony_ci	int ret;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	DEFINE_READAHEAD(ractl, file, NULL, mapping, index);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ciretry:
35462306a36Sopenharmony_ci	folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
35562306a36Sopenharmony_ci				    mapping_gfp_mask(mapping));
35662306a36Sopenharmony_ci	if (IS_ERR(folio))
35762306a36Sopenharmony_ci		return PTR_ERR(folio);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (ctx->ops->check_write_begin) {
36062306a36Sopenharmony_ci		/* Allow the netfs (eg. ceph) to flush conflicts. */
36162306a36Sopenharmony_ci		ret = ctx->ops->check_write_begin(file, pos, len, &folio, _fsdata);
36262306a36Sopenharmony_ci		if (ret < 0) {
36362306a36Sopenharmony_ci			trace_netfs_failure(NULL, NULL, ret, netfs_fail_check_write_begin);
36462306a36Sopenharmony_ci			goto error;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci		if (!folio)
36762306a36Sopenharmony_ci			goto retry;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (folio_test_uptodate(folio))
37162306a36Sopenharmony_ci		goto have_folio;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* If the page is beyond the EOF, we want to clear it - unless it's
37462306a36Sopenharmony_ci	 * within the cache granule containing the EOF, in which case we need
37562306a36Sopenharmony_ci	 * to preload the granule.
37662306a36Sopenharmony_ci	 */
37762306a36Sopenharmony_ci	if (!netfs_is_cache_enabled(ctx) &&
37862306a36Sopenharmony_ci	    netfs_skip_folio_read(folio, pos, len, false)) {
37962306a36Sopenharmony_ci		netfs_stat(&netfs_n_rh_write_zskip);
38062306a36Sopenharmony_ci		goto have_folio_no_wait;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	rreq = netfs_alloc_request(mapping, file,
38462306a36Sopenharmony_ci				   folio_file_pos(folio), folio_size(folio),
38562306a36Sopenharmony_ci				   NETFS_READ_FOR_WRITE);
38662306a36Sopenharmony_ci	if (IS_ERR(rreq)) {
38762306a36Sopenharmony_ci		ret = PTR_ERR(rreq);
38862306a36Sopenharmony_ci		goto error;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci	rreq->no_unlock_folio	= folio_index(folio);
39162306a36Sopenharmony_ci	__set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (ctx->ops->begin_cache_operation) {
39462306a36Sopenharmony_ci		ret = ctx->ops->begin_cache_operation(rreq);
39562306a36Sopenharmony_ci		if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
39662306a36Sopenharmony_ci			goto error_put;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	netfs_stat(&netfs_n_rh_write_begin);
40062306a36Sopenharmony_ci	trace_netfs_read(rreq, pos, len, netfs_read_trace_write_begin);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* Expand the request to meet caching requirements and download
40362306a36Sopenharmony_ci	 * preferences.
40462306a36Sopenharmony_ci	 */
40562306a36Sopenharmony_ci	ractl._nr_pages = folio_nr_pages(folio);
40662306a36Sopenharmony_ci	netfs_rreq_expand(rreq, &ractl);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* We hold the folio locks, so we can drop the references */
40962306a36Sopenharmony_ci	folio_get(folio);
41062306a36Sopenharmony_ci	while (readahead_folio(&ractl))
41162306a36Sopenharmony_ci		;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	ret = netfs_begin_read(rreq, true);
41462306a36Sopenharmony_ci	if (ret < 0)
41562306a36Sopenharmony_ci		goto error;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cihave_folio:
41862306a36Sopenharmony_ci	ret = folio_wait_fscache_killable(folio);
41962306a36Sopenharmony_ci	if (ret < 0)
42062306a36Sopenharmony_ci		goto error;
42162306a36Sopenharmony_cihave_folio_no_wait:
42262306a36Sopenharmony_ci	*_folio = folio;
42362306a36Sopenharmony_ci	_leave(" = 0");
42462306a36Sopenharmony_ci	return 0;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cierror_put:
42762306a36Sopenharmony_ci	netfs_put_request(rreq, false, netfs_rreq_trace_put_failed);
42862306a36Sopenharmony_cierror:
42962306a36Sopenharmony_ci	if (folio) {
43062306a36Sopenharmony_ci		folio_unlock(folio);
43162306a36Sopenharmony_ci		folio_put(folio);
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci	_leave(" = %d", ret);
43462306a36Sopenharmony_ci	return ret;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ciEXPORT_SYMBOL(netfs_write_begin);
437