162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* AFS filesystem file handling
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/pagemap.h>
1362306a36Sopenharmony_ci#include <linux/writeback.h>
1462306a36Sopenharmony_ci#include <linux/gfp.h>
1562306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h>
1662306a36Sopenharmony_ci#include <linux/mm.h>
1762306a36Sopenharmony_ci#include <linux/swap.h>
1862306a36Sopenharmony_ci#include <linux/netfs.h>
1962306a36Sopenharmony_ci#include "internal.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
2262306a36Sopenharmony_cistatic int afs_symlink_read_folio(struct file *file, struct folio *folio);
2362306a36Sopenharmony_cistatic void afs_invalidate_folio(struct folio *folio, size_t offset,
2462306a36Sopenharmony_ci			       size_t length);
2562306a36Sopenharmony_cistatic bool afs_release_folio(struct folio *folio, gfp_t gfp_flags);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic ssize_t afs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter);
2862306a36Sopenharmony_cistatic ssize_t afs_file_splice_read(struct file *in, loff_t *ppos,
2962306a36Sopenharmony_ci				    struct pipe_inode_info *pipe,
3062306a36Sopenharmony_ci				    size_t len, unsigned int flags);
3162306a36Sopenharmony_cistatic void afs_vm_open(struct vm_area_struct *area);
3262306a36Sopenharmony_cistatic void afs_vm_close(struct vm_area_struct *area);
3362306a36Sopenharmony_cistatic vm_fault_t afs_vm_map_pages(struct vm_fault *vmf, pgoff_t start_pgoff, pgoff_t end_pgoff);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ciconst struct file_operations afs_file_operations = {
3662306a36Sopenharmony_ci	.open		= afs_open,
3762306a36Sopenharmony_ci	.release	= afs_release,
3862306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
3962306a36Sopenharmony_ci	.read_iter	= afs_file_read_iter,
4062306a36Sopenharmony_ci	.write_iter	= afs_file_write,
4162306a36Sopenharmony_ci	.mmap		= afs_file_mmap,
4262306a36Sopenharmony_ci	.splice_read	= afs_file_splice_read,
4362306a36Sopenharmony_ci	.splice_write	= iter_file_splice_write,
4462306a36Sopenharmony_ci	.fsync		= afs_fsync,
4562306a36Sopenharmony_ci	.lock		= afs_lock,
4662306a36Sopenharmony_ci	.flock		= afs_flock,
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ciconst struct inode_operations afs_file_inode_operations = {
5062306a36Sopenharmony_ci	.getattr	= afs_getattr,
5162306a36Sopenharmony_ci	.setattr	= afs_setattr,
5262306a36Sopenharmony_ci	.permission	= afs_permission,
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciconst struct address_space_operations afs_file_aops = {
5662306a36Sopenharmony_ci	.read_folio	= netfs_read_folio,
5762306a36Sopenharmony_ci	.readahead	= netfs_readahead,
5862306a36Sopenharmony_ci	.dirty_folio	= afs_dirty_folio,
5962306a36Sopenharmony_ci	.launder_folio	= afs_launder_folio,
6062306a36Sopenharmony_ci	.release_folio	= afs_release_folio,
6162306a36Sopenharmony_ci	.invalidate_folio = afs_invalidate_folio,
6262306a36Sopenharmony_ci	.write_begin	= afs_write_begin,
6362306a36Sopenharmony_ci	.write_end	= afs_write_end,
6462306a36Sopenharmony_ci	.writepages	= afs_writepages,
6562306a36Sopenharmony_ci	.migrate_folio	= filemap_migrate_folio,
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciconst struct address_space_operations afs_symlink_aops = {
6962306a36Sopenharmony_ci	.read_folio	= afs_symlink_read_folio,
7062306a36Sopenharmony_ci	.release_folio	= afs_release_folio,
7162306a36Sopenharmony_ci	.invalidate_folio = afs_invalidate_folio,
7262306a36Sopenharmony_ci	.migrate_folio	= filemap_migrate_folio,
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic const struct vm_operations_struct afs_vm_ops = {
7662306a36Sopenharmony_ci	.open		= afs_vm_open,
7762306a36Sopenharmony_ci	.close		= afs_vm_close,
7862306a36Sopenharmony_ci	.fault		= filemap_fault,
7962306a36Sopenharmony_ci	.map_pages	= afs_vm_map_pages,
8062306a36Sopenharmony_ci	.page_mkwrite	= afs_page_mkwrite,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Discard a pin on a writeback key.
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_civoid afs_put_wb_key(struct afs_wb_key *wbk)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	if (wbk && refcount_dec_and_test(&wbk->usage)) {
8962306a36Sopenharmony_ci		key_put(wbk->key);
9062306a36Sopenharmony_ci		kfree(wbk);
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * Cache key for writeback.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ciint afs_cache_wb_key(struct afs_vnode *vnode, struct afs_file *af)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct afs_wb_key *wbk, *p;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	wbk = kzalloc(sizeof(struct afs_wb_key), GFP_KERNEL);
10262306a36Sopenharmony_ci	if (!wbk)
10362306a36Sopenharmony_ci		return -ENOMEM;
10462306a36Sopenharmony_ci	refcount_set(&wbk->usage, 2);
10562306a36Sopenharmony_ci	wbk->key = af->key;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	spin_lock(&vnode->wb_lock);
10862306a36Sopenharmony_ci	list_for_each_entry(p, &vnode->wb_keys, vnode_link) {
10962306a36Sopenharmony_ci		if (p->key == wbk->key)
11062306a36Sopenharmony_ci			goto found;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	key_get(wbk->key);
11462306a36Sopenharmony_ci	list_add_tail(&wbk->vnode_link, &vnode->wb_keys);
11562306a36Sopenharmony_ci	spin_unlock(&vnode->wb_lock);
11662306a36Sopenharmony_ci	af->wb = wbk;
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cifound:
12062306a36Sopenharmony_ci	refcount_inc(&p->usage);
12162306a36Sopenharmony_ci	spin_unlock(&vnode->wb_lock);
12262306a36Sopenharmony_ci	af->wb = p;
12362306a36Sopenharmony_ci	kfree(wbk);
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/*
12862306a36Sopenharmony_ci * open an AFS file or directory and attach a key to it
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_ciint afs_open(struct inode *inode, struct file *file)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
13362306a36Sopenharmony_ci	struct afs_file *af;
13462306a36Sopenharmony_ci	struct key *key;
13562306a36Sopenharmony_ci	int ret;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	_enter("{%llx:%llu},", vnode->fid.vid, vnode->fid.vnode);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	key = afs_request_key(vnode->volume->cell);
14062306a36Sopenharmony_ci	if (IS_ERR(key)) {
14162306a36Sopenharmony_ci		ret = PTR_ERR(key);
14262306a36Sopenharmony_ci		goto error;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	af = kzalloc(sizeof(*af), GFP_KERNEL);
14662306a36Sopenharmony_ci	if (!af) {
14762306a36Sopenharmony_ci		ret = -ENOMEM;
14862306a36Sopenharmony_ci		goto error_key;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	af->key = key;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = afs_validate(vnode, key);
15362306a36Sopenharmony_ci	if (ret < 0)
15462306a36Sopenharmony_ci		goto error_af;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (file->f_mode & FMODE_WRITE) {
15762306a36Sopenharmony_ci		ret = afs_cache_wb_key(vnode, af);
15862306a36Sopenharmony_ci		if (ret < 0)
15962306a36Sopenharmony_ci			goto error_af;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (file->f_flags & O_TRUNC)
16362306a36Sopenharmony_ci		set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	fscache_use_cookie(afs_vnode_cache(vnode), file->f_mode & FMODE_WRITE);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	file->private_data = af;
16862306a36Sopenharmony_ci	_leave(" = 0");
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cierror_af:
17262306a36Sopenharmony_ci	kfree(af);
17362306a36Sopenharmony_cierror_key:
17462306a36Sopenharmony_ci	key_put(key);
17562306a36Sopenharmony_cierror:
17662306a36Sopenharmony_ci	_leave(" = %d", ret);
17762306a36Sopenharmony_ci	return ret;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/*
18162306a36Sopenharmony_ci * release an AFS file or directory and discard its key
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_ciint afs_release(struct inode *inode, struct file *file)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct afs_vnode_cache_aux aux;
18662306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
18762306a36Sopenharmony_ci	struct afs_file *af = file->private_data;
18862306a36Sopenharmony_ci	loff_t i_size;
18962306a36Sopenharmony_ci	int ret = 0;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	_enter("{%llx:%llu},", vnode->fid.vid, vnode->fid.vnode);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if ((file->f_mode & FMODE_WRITE))
19462306a36Sopenharmony_ci		ret = vfs_fsync(file, 0);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	file->private_data = NULL;
19762306a36Sopenharmony_ci	if (af->wb)
19862306a36Sopenharmony_ci		afs_put_wb_key(af->wb);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if ((file->f_mode & FMODE_WRITE)) {
20162306a36Sopenharmony_ci		i_size = i_size_read(&vnode->netfs.inode);
20262306a36Sopenharmony_ci		afs_set_cache_aux(vnode, &aux);
20362306a36Sopenharmony_ci		fscache_unuse_cookie(afs_vnode_cache(vnode), &aux, &i_size);
20462306a36Sopenharmony_ci	} else {
20562306a36Sopenharmony_ci		fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	key_put(af->key);
20962306a36Sopenharmony_ci	kfree(af);
21062306a36Sopenharmony_ci	afs_prune_wb_keys(vnode);
21162306a36Sopenharmony_ci	_leave(" = %d", ret);
21262306a36Sopenharmony_ci	return ret;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci * Allocate a new read record.
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_cistruct afs_read *afs_alloc_read(gfp_t gfp)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct afs_read *req;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	req = kzalloc(sizeof(struct afs_read), gfp);
22362306a36Sopenharmony_ci	if (req)
22462306a36Sopenharmony_ci		refcount_set(&req->usage, 1);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return req;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/*
23062306a36Sopenharmony_ci * Dispose of a ref to a read record.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_civoid afs_put_read(struct afs_read *req)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	if (refcount_dec_and_test(&req->usage)) {
23562306a36Sopenharmony_ci		if (req->cleanup)
23662306a36Sopenharmony_ci			req->cleanup(req);
23762306a36Sopenharmony_ci		key_put(req->key);
23862306a36Sopenharmony_ci		kfree(req);
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void afs_fetch_data_notify(struct afs_operation *op)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct afs_read *req = op->fetch.req;
24562306a36Sopenharmony_ci	struct netfs_io_subrequest *subreq = req->subreq;
24662306a36Sopenharmony_ci	int error = op->error;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (error == -ECONNABORTED)
24962306a36Sopenharmony_ci		error = afs_abort_to_error(op->ac.abort_code);
25062306a36Sopenharmony_ci	req->error = error;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (subreq) {
25362306a36Sopenharmony_ci		__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
25462306a36Sopenharmony_ci		netfs_subreq_terminated(subreq, error ?: req->actual_len, false);
25562306a36Sopenharmony_ci		req->subreq = NULL;
25662306a36Sopenharmony_ci	} else if (req->done) {
25762306a36Sopenharmony_ci		req->done(req);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic void afs_fetch_data_success(struct afs_operation *op)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct afs_vnode *vnode = op->file[0].vnode;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
26662306a36Sopenharmony_ci	afs_vnode_commit_status(op, &op->file[0]);
26762306a36Sopenharmony_ci	afs_stat_v(vnode, n_fetches);
26862306a36Sopenharmony_ci	atomic_long_add(op->fetch.req->actual_len, &op->net->n_fetch_bytes);
26962306a36Sopenharmony_ci	afs_fetch_data_notify(op);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void afs_fetch_data_put(struct afs_operation *op)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	op->fetch.req->error = op->error;
27562306a36Sopenharmony_ci	afs_put_read(op->fetch.req);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic const struct afs_operation_ops afs_fetch_data_operation = {
27962306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_fetch_data,
28062306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_fetch_data,
28162306a36Sopenharmony_ci	.success	= afs_fetch_data_success,
28262306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
28362306a36Sopenharmony_ci	.failed		= afs_fetch_data_notify,
28462306a36Sopenharmony_ci	.put		= afs_fetch_data_put,
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci * Fetch file data from the volume.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_ciint afs_fetch_data(struct afs_vnode *vnode, struct afs_read *req)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct afs_operation *op;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	_enter("%s{%llx:%llu.%u},%x,,,",
29562306a36Sopenharmony_ci	       vnode->volume->name,
29662306a36Sopenharmony_ci	       vnode->fid.vid,
29762306a36Sopenharmony_ci	       vnode->fid.vnode,
29862306a36Sopenharmony_ci	       vnode->fid.unique,
29962306a36Sopenharmony_ci	       key_serial(req->key));
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	op = afs_alloc_operation(req->key, vnode->volume);
30262306a36Sopenharmony_ci	if (IS_ERR(op)) {
30362306a36Sopenharmony_ci		if (req->subreq)
30462306a36Sopenharmony_ci			netfs_subreq_terminated(req->subreq, PTR_ERR(op), false);
30562306a36Sopenharmony_ci		return PTR_ERR(op);
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, vnode);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	op->fetch.req	= afs_get_read(req);
31162306a36Sopenharmony_ci	op->ops		= &afs_fetch_data_operation;
31262306a36Sopenharmony_ci	return afs_do_sync_operation(op);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void afs_issue_read(struct netfs_io_subrequest *subreq)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(subreq->rreq->inode);
31862306a36Sopenharmony_ci	struct afs_read *fsreq;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	fsreq = afs_alloc_read(GFP_NOFS);
32162306a36Sopenharmony_ci	if (!fsreq)
32262306a36Sopenharmony_ci		return netfs_subreq_terminated(subreq, -ENOMEM, false);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	fsreq->subreq	= subreq;
32562306a36Sopenharmony_ci	fsreq->pos	= subreq->start + subreq->transferred;
32662306a36Sopenharmony_ci	fsreq->len	= subreq->len   - subreq->transferred;
32762306a36Sopenharmony_ci	fsreq->key	= key_get(subreq->rreq->netfs_priv);
32862306a36Sopenharmony_ci	fsreq->vnode	= vnode;
32962306a36Sopenharmony_ci	fsreq->iter	= &fsreq->def_iter;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	iov_iter_xarray(&fsreq->def_iter, ITER_DEST,
33262306a36Sopenharmony_ci			&fsreq->vnode->netfs.inode.i_mapping->i_pages,
33362306a36Sopenharmony_ci			fsreq->pos, fsreq->len);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	afs_fetch_data(fsreq->vnode, fsreq);
33662306a36Sopenharmony_ci	afs_put_read(fsreq);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int afs_symlink_read_folio(struct file *file, struct folio *folio)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(folio->mapping->host);
34262306a36Sopenharmony_ci	struct afs_read *fsreq;
34362306a36Sopenharmony_ci	int ret;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	fsreq = afs_alloc_read(GFP_NOFS);
34662306a36Sopenharmony_ci	if (!fsreq)
34762306a36Sopenharmony_ci		return -ENOMEM;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	fsreq->pos	= folio_pos(folio);
35062306a36Sopenharmony_ci	fsreq->len	= folio_size(folio);
35162306a36Sopenharmony_ci	fsreq->vnode	= vnode;
35262306a36Sopenharmony_ci	fsreq->iter	= &fsreq->def_iter;
35362306a36Sopenharmony_ci	iov_iter_xarray(&fsreq->def_iter, ITER_DEST, &folio->mapping->i_pages,
35462306a36Sopenharmony_ci			fsreq->pos, fsreq->len);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = afs_fetch_data(fsreq->vnode, fsreq);
35762306a36Sopenharmony_ci	if (ret == 0)
35862306a36Sopenharmony_ci		folio_mark_uptodate(folio);
35962306a36Sopenharmony_ci	folio_unlock(folio);
36062306a36Sopenharmony_ci	return ret;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int afs_init_request(struct netfs_io_request *rreq, struct file *file)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	rreq->netfs_priv = key_get(afs_file_key(file));
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int afs_begin_cache_operation(struct netfs_io_request *rreq)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
37262306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(rreq->inode);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return fscache_begin_read_operation(&rreq->cache_resources,
37562306a36Sopenharmony_ci					    afs_vnode_cache(vnode));
37662306a36Sopenharmony_ci#else
37762306a36Sopenharmony_ci	return -ENOBUFS;
37862306a36Sopenharmony_ci#endif
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int afs_check_write_begin(struct file *file, loff_t pos, unsigned len,
38262306a36Sopenharmony_ci				 struct folio **foliop, void **_fsdata)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return test_bit(AFS_VNODE_DELETED, &vnode->flags) ? -ESTALE : 0;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void afs_free_request(struct netfs_io_request *rreq)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	key_put(rreq->netfs_priv);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciconst struct netfs_request_ops afs_req_ops = {
39562306a36Sopenharmony_ci	.init_request		= afs_init_request,
39662306a36Sopenharmony_ci	.free_request		= afs_free_request,
39762306a36Sopenharmony_ci	.begin_cache_operation	= afs_begin_cache_operation,
39862306a36Sopenharmony_ci	.check_write_begin	= afs_check_write_begin,
39962306a36Sopenharmony_ci	.issue_read		= afs_issue_read,
40062306a36Sopenharmony_ci};
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ciint afs_write_inode(struct inode *inode, struct writeback_control *wbc)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	fscache_unpin_writeback(wbc, afs_vnode_cache(AFS_FS_I(inode)));
40562306a36Sopenharmony_ci	return 0;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/*
40962306a36Sopenharmony_ci * Adjust the dirty region of the page on truncation or full invalidation,
41062306a36Sopenharmony_ci * getting rid of the markers altogether if the region is entirely invalidated.
41162306a36Sopenharmony_ci */
41262306a36Sopenharmony_cistatic void afs_invalidate_dirty(struct folio *folio, size_t offset,
41362306a36Sopenharmony_ci				 size_t length)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio));
41662306a36Sopenharmony_ci	unsigned long priv;
41762306a36Sopenharmony_ci	unsigned int f, t, end = offset + length;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	priv = (unsigned long)folio_get_private(folio);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* we clean up only if the entire page is being invalidated */
42262306a36Sopenharmony_ci	if (offset == 0 && length == folio_size(folio))
42362306a36Sopenharmony_ci		goto full_invalidate;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	 /* If the page was dirtied by page_mkwrite(), the PTE stays writable
42662306a36Sopenharmony_ci	  * and we don't get another notification to tell us to expand it
42762306a36Sopenharmony_ci	  * again.
42862306a36Sopenharmony_ci	  */
42962306a36Sopenharmony_ci	if (afs_is_folio_dirty_mmapped(priv))
43062306a36Sopenharmony_ci		return;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* We may need to shorten the dirty region */
43362306a36Sopenharmony_ci	f = afs_folio_dirty_from(folio, priv);
43462306a36Sopenharmony_ci	t = afs_folio_dirty_to(folio, priv);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (t <= offset || f >= end)
43762306a36Sopenharmony_ci		return; /* Doesn't overlap */
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (f < offset && t > end)
44062306a36Sopenharmony_ci		return; /* Splits the dirty region - just absorb it */
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (f >= offset && t <= end)
44362306a36Sopenharmony_ci		goto undirty;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (f < offset)
44662306a36Sopenharmony_ci		t = offset;
44762306a36Sopenharmony_ci	else
44862306a36Sopenharmony_ci		f = end;
44962306a36Sopenharmony_ci	if (f == t)
45062306a36Sopenharmony_ci		goto undirty;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	priv = afs_folio_dirty(folio, f, t);
45362306a36Sopenharmony_ci	folio_change_private(folio, (void *)priv);
45462306a36Sopenharmony_ci	trace_afs_folio_dirty(vnode, tracepoint_string("trunc"), folio);
45562306a36Sopenharmony_ci	return;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ciundirty:
45862306a36Sopenharmony_ci	trace_afs_folio_dirty(vnode, tracepoint_string("undirty"), folio);
45962306a36Sopenharmony_ci	folio_clear_dirty_for_io(folio);
46062306a36Sopenharmony_cifull_invalidate:
46162306a36Sopenharmony_ci	trace_afs_folio_dirty(vnode, tracepoint_string("inval"), folio);
46262306a36Sopenharmony_ci	folio_detach_private(folio);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci/*
46662306a36Sopenharmony_ci * invalidate part or all of a page
46762306a36Sopenharmony_ci * - release a page and clean up its private data if offset is 0 (indicating
46862306a36Sopenharmony_ci *   the entire page)
46962306a36Sopenharmony_ci */
47062306a36Sopenharmony_cistatic void afs_invalidate_folio(struct folio *folio, size_t offset,
47162306a36Sopenharmony_ci			       size_t length)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	_enter("{%lu},%zu,%zu", folio->index, offset, length);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	BUG_ON(!folio_test_locked(folio));
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (folio_get_private(folio))
47862306a36Sopenharmony_ci		afs_invalidate_dirty(folio, offset, length);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	folio_wait_fscache(folio);
48162306a36Sopenharmony_ci	_leave("");
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci/*
48562306a36Sopenharmony_ci * release a page and clean up its private state if it's not busy
48662306a36Sopenharmony_ci * - return true if the page can now be released, false if not
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_cistatic bool afs_release_folio(struct folio *folio, gfp_t gfp)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio));
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	_enter("{{%llx:%llu}[%lu],%lx},%x",
49362306a36Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, folio_index(folio), folio->flags,
49462306a36Sopenharmony_ci	       gfp);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* deny if folio is being written to the cache and the caller hasn't
49762306a36Sopenharmony_ci	 * elected to wait */
49862306a36Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
49962306a36Sopenharmony_ci	if (folio_test_fscache(folio)) {
50062306a36Sopenharmony_ci		if (current_is_kswapd() || !(gfp & __GFP_FS))
50162306a36Sopenharmony_ci			return false;
50262306a36Sopenharmony_ci		folio_wait_fscache(folio);
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci	fscache_note_page_release(afs_vnode_cache(vnode));
50562306a36Sopenharmony_ci#endif
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (folio_test_private(folio)) {
50862306a36Sopenharmony_ci		trace_afs_folio_dirty(vnode, tracepoint_string("rel"), folio);
50962306a36Sopenharmony_ci		folio_detach_private(folio);
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* Indicate that the folio can be released */
51362306a36Sopenharmony_ci	_leave(" = T");
51462306a36Sopenharmony_ci	return true;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic void afs_add_open_mmap(struct afs_vnode *vnode)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	if (atomic_inc_return(&vnode->cb_nr_mmap) == 1) {
52062306a36Sopenharmony_ci		down_write(&vnode->volume->cell->fs_open_mmaps_lock);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		if (list_empty(&vnode->cb_mmap_link))
52362306a36Sopenharmony_ci			list_add_tail(&vnode->cb_mmap_link,
52462306a36Sopenharmony_ci				      &vnode->volume->cell->fs_open_mmaps);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		up_write(&vnode->volume->cell->fs_open_mmaps_lock);
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void afs_drop_open_mmap(struct afs_vnode *vnode)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	if (!atomic_dec_and_test(&vnode->cb_nr_mmap))
53362306a36Sopenharmony_ci		return;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	down_write(&vnode->volume->cell->fs_open_mmaps_lock);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (atomic_read(&vnode->cb_nr_mmap) == 0)
53862306a36Sopenharmony_ci		list_del_init(&vnode->cb_mmap_link);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	up_write(&vnode->volume->cell->fs_open_mmaps_lock);
54162306a36Sopenharmony_ci	flush_work(&vnode->cb_work);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/*
54562306a36Sopenharmony_ci * Handle setting up a memory mapping on an AFS file.
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_cistatic int afs_file_mmap(struct file *file, struct vm_area_struct *vma)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
55062306a36Sopenharmony_ci	int ret;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	afs_add_open_mmap(vnode);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ret = generic_file_mmap(file, vma);
55562306a36Sopenharmony_ci	if (ret == 0)
55662306a36Sopenharmony_ci		vma->vm_ops = &afs_vm_ops;
55762306a36Sopenharmony_ci	else
55862306a36Sopenharmony_ci		afs_drop_open_mmap(vnode);
55962306a36Sopenharmony_ci	return ret;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void afs_vm_open(struct vm_area_struct *vma)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	afs_add_open_mmap(AFS_FS_I(file_inode(vma->vm_file)));
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic void afs_vm_close(struct vm_area_struct *vma)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	afs_drop_open_mmap(AFS_FS_I(file_inode(vma->vm_file)));
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic vm_fault_t afs_vm_map_pages(struct vm_fault *vmf, pgoff_t start_pgoff, pgoff_t end_pgoff)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(vmf->vma->vm_file));
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if (afs_pagecache_valid(vnode))
57762306a36Sopenharmony_ci		return filemap_map_pages(vmf, start_pgoff, end_pgoff);
57862306a36Sopenharmony_ci	return 0;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic ssize_t afs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(iocb->ki_filp));
58462306a36Sopenharmony_ci	struct afs_file *af = iocb->ki_filp->private_data;
58562306a36Sopenharmony_ci	int ret;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ret = afs_validate(vnode, af->key);
58862306a36Sopenharmony_ci	if (ret < 0)
58962306a36Sopenharmony_ci		return ret;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return generic_file_read_iter(iocb, iter);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic ssize_t afs_file_splice_read(struct file *in, loff_t *ppos,
59562306a36Sopenharmony_ci				    struct pipe_inode_info *pipe,
59662306a36Sopenharmony_ci				    size_t len, unsigned int flags)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(file_inode(in));
59962306a36Sopenharmony_ci	struct afs_file *af = in->private_data;
60062306a36Sopenharmony_ci	int ret;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	ret = afs_validate(vnode, af->key);
60362306a36Sopenharmony_ci	if (ret < 0)
60462306a36Sopenharmony_ci		return ret;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return filemap_splice_read(in, ppos, pipe, len, flags);
60762306a36Sopenharmony_ci}
608