xref: /kernel/linux/linux-5.10/fs/afs/fs_operation.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Fileserver-directed operation handling.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/fs.h>
118c2ecf20Sopenharmony_ci#include "internal.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic atomic_t afs_operation_debug_counter;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * Create an operation against a volume.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_cistruct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *volume)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct afs_operation *op;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	_enter("");
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	op = kzalloc(sizeof(*op), GFP_KERNEL);
258c2ecf20Sopenharmony_ci	if (!op)
268c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (!key) {
298c2ecf20Sopenharmony_ci		key = afs_request_key(volume->cell);
308c2ecf20Sopenharmony_ci		if (IS_ERR(key)) {
318c2ecf20Sopenharmony_ci			kfree(op);
328c2ecf20Sopenharmony_ci			return ERR_CAST(key);
338c2ecf20Sopenharmony_ci		}
348c2ecf20Sopenharmony_ci	} else {
358c2ecf20Sopenharmony_ci		key_get(key);
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	op->key		= key;
398c2ecf20Sopenharmony_ci	op->volume	= afs_get_volume(volume, afs_volume_trace_get_new_op);
408c2ecf20Sopenharmony_ci	op->net		= volume->cell->net;
418c2ecf20Sopenharmony_ci	op->cb_v_break	= volume->cb_v_break;
428c2ecf20Sopenharmony_ci	op->debug_id	= atomic_inc_return(&afs_operation_debug_counter);
438c2ecf20Sopenharmony_ci	op->error	= -EDESTADDRREQ;
448c2ecf20Sopenharmony_ci	op->ac.error	= SHRT_MAX;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	_leave(" = [op=%08x]", op->debug_id);
478c2ecf20Sopenharmony_ci	return op;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Lock the vnode(s) being operated upon.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic bool afs_get_io_locks(struct afs_operation *op)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = op->file[0].vnode;
568c2ecf20Sopenharmony_ci	struct afs_vnode *vnode2 = op->file[1].vnode;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	_enter("");
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (op->flags & AFS_OPERATION_UNINTR) {
618c2ecf20Sopenharmony_ci		mutex_lock(&vnode->io_lock);
628c2ecf20Sopenharmony_ci		op->flags |= AFS_OPERATION_LOCK_0;
638c2ecf20Sopenharmony_ci		_leave(" = t [1]");
648c2ecf20Sopenharmony_ci		return true;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2)
688c2ecf20Sopenharmony_ci		vnode2 = NULL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (vnode2 > vnode)
718c2ecf20Sopenharmony_ci		swap(vnode, vnode2);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&vnode->io_lock) < 0) {
748c2ecf20Sopenharmony_ci		op->error = -ERESTARTSYS;
758c2ecf20Sopenharmony_ci		op->flags |= AFS_OPERATION_STOP;
768c2ecf20Sopenharmony_ci		_leave(" = f [I 0]");
778c2ecf20Sopenharmony_ci		return false;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci	op->flags |= AFS_OPERATION_LOCK_0;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (vnode2) {
828c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) {
838c2ecf20Sopenharmony_ci			op->error = -ERESTARTSYS;
848c2ecf20Sopenharmony_ci			op->flags |= AFS_OPERATION_STOP;
858c2ecf20Sopenharmony_ci			mutex_unlock(&vnode->io_lock);
868c2ecf20Sopenharmony_ci			op->flags &= ~AFS_OPERATION_LOCK_0;
878c2ecf20Sopenharmony_ci			_leave(" = f [I 1]");
888c2ecf20Sopenharmony_ci			return false;
898c2ecf20Sopenharmony_ci		}
908c2ecf20Sopenharmony_ci		op->flags |= AFS_OPERATION_LOCK_1;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	_leave(" = t [2]");
948c2ecf20Sopenharmony_ci	return true;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void afs_drop_io_locks(struct afs_operation *op)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = op->file[0].vnode;
1008c2ecf20Sopenharmony_ci	struct afs_vnode *vnode2 = op->file[1].vnode;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	_enter("");
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (op->flags & AFS_OPERATION_LOCK_1)
1058c2ecf20Sopenharmony_ci		mutex_unlock(&vnode2->io_lock);
1068c2ecf20Sopenharmony_ci	if (op->flags & AFS_OPERATION_LOCK_0)
1078c2ecf20Sopenharmony_ci		mutex_unlock(&vnode->io_lock);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp,
1118c2ecf20Sopenharmony_ci			      unsigned int index)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = vp->vnode;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (vnode) {
1168c2ecf20Sopenharmony_ci		vp->fid			= vnode->fid;
1178c2ecf20Sopenharmony_ci		vp->dv_before		= vnode->status.data_version;
1188c2ecf20Sopenharmony_ci		vp->cb_break_before	= afs_calc_vnode_cb_break(vnode);
1198c2ecf20Sopenharmony_ci		if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
1208c2ecf20Sopenharmony_ci			op->flags	|= AFS_OPERATION_CUR_ONLY;
1218c2ecf20Sopenharmony_ci		if (vp->modification)
1228c2ecf20Sopenharmony_ci			set_bit(AFS_VNODE_MODIFYING, &vnode->flags);
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (vp->fid.vnode)
1268c2ecf20Sopenharmony_ci		_debug("PREP[%u] {%llx:%llu.%u}",
1278c2ecf20Sopenharmony_ci		       index, vp->fid.vid, vp->fid.vnode, vp->fid.unique);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/*
1318c2ecf20Sopenharmony_ci * Begin an operation on the fileserver.
1328c2ecf20Sopenharmony_ci *
1338c2ecf20Sopenharmony_ci * Fileserver operations are serialised on the server by vnode, so we serialise
1348c2ecf20Sopenharmony_ci * them here also using the io_lock.
1358c2ecf20Sopenharmony_ci */
1368c2ecf20Sopenharmony_cibool afs_begin_vnode_operation(struct afs_operation *op)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct afs_vnode *vnode = op->file[0].vnode;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	ASSERT(vnode);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	_enter("");
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (op->file[0].need_io_lock)
1458c2ecf20Sopenharmony_ci		if (!afs_get_io_locks(op))
1468c2ecf20Sopenharmony_ci			return false;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	afs_prepare_vnode(op, &op->file[0], 0);
1498c2ecf20Sopenharmony_ci	afs_prepare_vnode(op, &op->file[1], 1);
1508c2ecf20Sopenharmony_ci	op->cb_v_break = op->volume->cb_v_break;
1518c2ecf20Sopenharmony_ci	_leave(" = true");
1528c2ecf20Sopenharmony_ci	return true;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci * Tidy up a filesystem cursor and unlock the vnode.
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_cistatic void afs_end_vnode_operation(struct afs_operation *op)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	_enter("");
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (op->error == -EDESTADDRREQ ||
1638c2ecf20Sopenharmony_ci	    op->error == -EADDRNOTAVAIL ||
1648c2ecf20Sopenharmony_ci	    op->error == -ENETUNREACH ||
1658c2ecf20Sopenharmony_ci	    op->error == -EHOSTUNREACH)
1668c2ecf20Sopenharmony_ci		afs_dump_edestaddrreq(op);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	afs_drop_io_locks(op);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (op->error == -ECONNABORTED)
1718c2ecf20Sopenharmony_ci		op->error = afs_abort_to_error(op->ac.abort_code);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/*
1758c2ecf20Sopenharmony_ci * Wait for an in-progress operation to complete.
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_civoid afs_wait_for_operation(struct afs_operation *op)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	_enter("");
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	while (afs_select_fileserver(op)) {
1828c2ecf20Sopenharmony_ci		op->cb_s_break = op->server->cb_s_break;
1838c2ecf20Sopenharmony_ci		if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) &&
1848c2ecf20Sopenharmony_ci		    op->ops->issue_yfs_rpc)
1858c2ecf20Sopenharmony_ci			op->ops->issue_yfs_rpc(op);
1868c2ecf20Sopenharmony_ci		else if (op->ops->issue_afs_rpc)
1878c2ecf20Sopenharmony_ci			op->ops->issue_afs_rpc(op);
1888c2ecf20Sopenharmony_ci		else
1898c2ecf20Sopenharmony_ci			op->ac.error = -ENOTSUPP;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		if (op->call)
1928c2ecf20Sopenharmony_ci			op->error = afs_wait_for_call_to_complete(op->call, &op->ac);
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	switch (op->error) {
1968c2ecf20Sopenharmony_ci	case 0:
1978c2ecf20Sopenharmony_ci		_debug("success");
1988c2ecf20Sopenharmony_ci		op->ops->success(op);
1998c2ecf20Sopenharmony_ci		break;
2008c2ecf20Sopenharmony_ci	case -ECONNABORTED:
2018c2ecf20Sopenharmony_ci		if (op->ops->aborted)
2028c2ecf20Sopenharmony_ci			op->ops->aborted(op);
2038c2ecf20Sopenharmony_ci		break;
2048c2ecf20Sopenharmony_ci	default:
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	afs_end_vnode_operation(op);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (op->error == 0 && op->ops->edit_dir) {
2118c2ecf20Sopenharmony_ci		_debug("edit_dir");
2128c2ecf20Sopenharmony_ci		op->ops->edit_dir(op);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci	_leave("");
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/*
2188c2ecf20Sopenharmony_ci * Dispose of an operation.
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_ciint afs_put_operation(struct afs_operation *op)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	int i, ret = op->error;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	_enter("op=%08x,%d", op->debug_id, ret);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (op->ops && op->ops->put)
2278c2ecf20Sopenharmony_ci		op->ops->put(op);
2288c2ecf20Sopenharmony_ci	if (op->file[0].modification)
2298c2ecf20Sopenharmony_ci		clear_bit(AFS_VNODE_MODIFYING, &op->file[0].vnode->flags);
2308c2ecf20Sopenharmony_ci	if (op->file[1].modification && op->file[1].vnode != op->file[0].vnode)
2318c2ecf20Sopenharmony_ci		clear_bit(AFS_VNODE_MODIFYING, &op->file[1].vnode->flags);
2328c2ecf20Sopenharmony_ci	if (op->file[0].put_vnode)
2338c2ecf20Sopenharmony_ci		iput(&op->file[0].vnode->vfs_inode);
2348c2ecf20Sopenharmony_ci	if (op->file[1].put_vnode)
2358c2ecf20Sopenharmony_ci		iput(&op->file[1].vnode->vfs_inode);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (op->more_files) {
2388c2ecf20Sopenharmony_ci		for (i = 0; i < op->nr_files - 2; i++)
2398c2ecf20Sopenharmony_ci			if (op->more_files[i].put_vnode)
2408c2ecf20Sopenharmony_ci				iput(&op->more_files[i].vnode->vfs_inode);
2418c2ecf20Sopenharmony_ci		kfree(op->more_files);
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	afs_end_cursor(&op->ac);
2458c2ecf20Sopenharmony_ci	afs_put_serverlist(op->net, op->server_list);
2468c2ecf20Sopenharmony_ci	afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op);
2478c2ecf20Sopenharmony_ci	key_put(op->key);
2488c2ecf20Sopenharmony_ci	kfree(op);
2498c2ecf20Sopenharmony_ci	return ret;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ciint afs_do_sync_operation(struct afs_operation *op)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	afs_begin_vnode_operation(op);
2558c2ecf20Sopenharmony_ci	afs_wait_for_operation(op);
2568c2ecf20Sopenharmony_ci	return afs_put_operation(op);
2578c2ecf20Sopenharmony_ci}
258