1e49c7b2fSDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
2e49c7b2fSDavid Howells /* Fileserver-directed operation handling.
3e49c7b2fSDavid Howells *
4e49c7b2fSDavid Howells * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
5e49c7b2fSDavid Howells * Written by David Howells (dhowells@redhat.com)
6e49c7b2fSDavid Howells */
7e49c7b2fSDavid Howells
8e49c7b2fSDavid Howells #include <linux/kernel.h>
9e49c7b2fSDavid Howells #include <linux/slab.h>
10e49c7b2fSDavid Howells #include <linux/fs.h>
11e49c7b2fSDavid Howells #include "internal.h"
12e49c7b2fSDavid Howells
13e49c7b2fSDavid Howells static atomic_t afs_operation_debug_counter;
14e49c7b2fSDavid Howells
15e49c7b2fSDavid Howells /*
16e49c7b2fSDavid Howells * Create an operation against a volume.
17e49c7b2fSDavid Howells */
afs_alloc_operation(struct key * key,struct afs_volume * volume)18e49c7b2fSDavid Howells struct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *volume)
19e49c7b2fSDavid Howells {
20e49c7b2fSDavid Howells struct afs_operation *op;
21e49c7b2fSDavid Howells
22e49c7b2fSDavid Howells _enter("");
23e49c7b2fSDavid Howells
24e49c7b2fSDavid Howells op = kzalloc(sizeof(*op), GFP_KERNEL);
25e49c7b2fSDavid Howells if (!op)
26e49c7b2fSDavid Howells return ERR_PTR(-ENOMEM);
27e49c7b2fSDavid Howells
28e49c7b2fSDavid Howells if (!key) {
29e49c7b2fSDavid Howells key = afs_request_key(volume->cell);
30e49c7b2fSDavid Howells if (IS_ERR(key)) {
31e49c7b2fSDavid Howells kfree(op);
32e49c7b2fSDavid Howells return ERR_CAST(key);
33e49c7b2fSDavid Howells }
34e49c7b2fSDavid Howells } else {
35e49c7b2fSDavid Howells key_get(key);
36e49c7b2fSDavid Howells }
37e49c7b2fSDavid Howells
38e49c7b2fSDavid Howells op->key = key;
39cca37d45SDavid Howells op->volume = afs_get_volume(volume, afs_volume_trace_get_new_op);
40e49c7b2fSDavid Howells op->net = volume->cell->net;
41e49c7b2fSDavid Howells op->cb_v_break = volume->cb_v_break;
42e49c7b2fSDavid Howells op->debug_id = atomic_inc_return(&afs_operation_debug_counter);
43e49c7b2fSDavid Howells op->error = -EDESTADDRREQ;
44e49c7b2fSDavid Howells op->ac.error = SHRT_MAX;
45e49c7b2fSDavid Howells
46e49c7b2fSDavid Howells _leave(" = [op=%08x]", op->debug_id);
47e49c7b2fSDavid Howells return op;
48e49c7b2fSDavid Howells }
49e49c7b2fSDavid Howells
50e49c7b2fSDavid Howells /*
51e49c7b2fSDavid Howells * Lock the vnode(s) being operated upon.
52e49c7b2fSDavid Howells */
afs_get_io_locks(struct afs_operation * op)53e49c7b2fSDavid Howells static bool afs_get_io_locks(struct afs_operation *op)
54e49c7b2fSDavid Howells {
55e49c7b2fSDavid Howells struct afs_vnode *vnode = op->file[0].vnode;
56e49c7b2fSDavid Howells struct afs_vnode *vnode2 = op->file[1].vnode;
57e49c7b2fSDavid Howells
58e49c7b2fSDavid Howells _enter("");
59e49c7b2fSDavid Howells
60e49c7b2fSDavid Howells if (op->flags & AFS_OPERATION_UNINTR) {
61e49c7b2fSDavid Howells mutex_lock(&vnode->io_lock);
62e49c7b2fSDavid Howells op->flags |= AFS_OPERATION_LOCK_0;
63e49c7b2fSDavid Howells _leave(" = t [1]");
64e49c7b2fSDavid Howells return true;
65e49c7b2fSDavid Howells }
66e49c7b2fSDavid Howells
67e49c7b2fSDavid Howells if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2)
68e49c7b2fSDavid Howells vnode2 = NULL;
69e49c7b2fSDavid Howells
70e49c7b2fSDavid Howells if (vnode2 > vnode)
71e49c7b2fSDavid Howells swap(vnode, vnode2);
72e49c7b2fSDavid Howells
73e49c7b2fSDavid Howells if (mutex_lock_interruptible(&vnode->io_lock) < 0) {
74811f04baSDavid Howells op->error = -ERESTARTSYS;
75e49c7b2fSDavid Howells op->flags |= AFS_OPERATION_STOP;
76e49c7b2fSDavid Howells _leave(" = f [I 0]");
77e49c7b2fSDavid Howells return false;
78e49c7b2fSDavid Howells }
79e49c7b2fSDavid Howells op->flags |= AFS_OPERATION_LOCK_0;
80e49c7b2fSDavid Howells
81e49c7b2fSDavid Howells if (vnode2) {
82e49c7b2fSDavid Howells if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) {
83811f04baSDavid Howells op->error = -ERESTARTSYS;
84e49c7b2fSDavid Howells op->flags |= AFS_OPERATION_STOP;
85e49c7b2fSDavid Howells mutex_unlock(&vnode->io_lock);
86e49c7b2fSDavid Howells op->flags &= ~AFS_OPERATION_LOCK_0;
87e49c7b2fSDavid Howells _leave(" = f [I 1]");
88e49c7b2fSDavid Howells return false;
89e49c7b2fSDavid Howells }
90e49c7b2fSDavid Howells op->flags |= AFS_OPERATION_LOCK_1;
91e49c7b2fSDavid Howells }
92e49c7b2fSDavid Howells
93e49c7b2fSDavid Howells _leave(" = t [2]");
94e49c7b2fSDavid Howells return true;
95e49c7b2fSDavid Howells }
96e49c7b2fSDavid Howells
afs_drop_io_locks(struct afs_operation * op)97e49c7b2fSDavid Howells static void afs_drop_io_locks(struct afs_operation *op)
98e49c7b2fSDavid Howells {
99e49c7b2fSDavid Howells struct afs_vnode *vnode = op->file[0].vnode;
100e49c7b2fSDavid Howells struct afs_vnode *vnode2 = op->file[1].vnode;
101e49c7b2fSDavid Howells
102e49c7b2fSDavid Howells _enter("");
103e49c7b2fSDavid Howells
104e49c7b2fSDavid Howells if (op->flags & AFS_OPERATION_LOCK_1)
105e49c7b2fSDavid Howells mutex_unlock(&vnode2->io_lock);
106e49c7b2fSDavid Howells if (op->flags & AFS_OPERATION_LOCK_0)
107e49c7b2fSDavid Howells mutex_unlock(&vnode->io_lock);
108e49c7b2fSDavid Howells }
109e49c7b2fSDavid Howells
afs_prepare_vnode(struct afs_operation * op,struct afs_vnode_param * vp,unsigned int index)110e49c7b2fSDavid Howells static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp,
111e49c7b2fSDavid Howells unsigned int index)
112e49c7b2fSDavid Howells {
113e49c7b2fSDavid Howells struct afs_vnode *vnode = vp->vnode;
114e49c7b2fSDavid Howells
115e49c7b2fSDavid Howells if (vnode) {
116e49c7b2fSDavid Howells vp->fid = vnode->fid;
117e49c7b2fSDavid Howells vp->dv_before = vnode->status.data_version;
118e49c7b2fSDavid Howells vp->cb_break_before = afs_calc_vnode_cb_break(vnode);
119e49c7b2fSDavid Howells if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
120e49c7b2fSDavid Howells op->flags |= AFS_OPERATION_CUR_ONLY;
12122650f14SDavid Howells if (vp->modification)
12222650f14SDavid Howells set_bit(AFS_VNODE_MODIFYING, &vnode->flags);
123e49c7b2fSDavid Howells }
124e49c7b2fSDavid Howells
125e49c7b2fSDavid Howells if (vp->fid.vnode)
126e49c7b2fSDavid Howells _debug("PREP[%u] {%llx:%llu.%u}",
127e49c7b2fSDavid Howells index, vp->fid.vid, vp->fid.vnode, vp->fid.unique);
128e49c7b2fSDavid Howells }
129e49c7b2fSDavid Howells
130e49c7b2fSDavid Howells /*
131e49c7b2fSDavid Howells * Begin an operation on the fileserver.
132e49c7b2fSDavid Howells *
133e49c7b2fSDavid Howells * Fileserver operations are serialised on the server by vnode, so we serialise
134e49c7b2fSDavid Howells * them here also using the io_lock.
135e49c7b2fSDavid Howells */
afs_begin_vnode_operation(struct afs_operation * op)136e49c7b2fSDavid Howells bool afs_begin_vnode_operation(struct afs_operation *op)
137e49c7b2fSDavid Howells {
138e49c7b2fSDavid Howells struct afs_vnode *vnode = op->file[0].vnode;
139e49c7b2fSDavid Howells
140e49c7b2fSDavid Howells ASSERT(vnode);
141e49c7b2fSDavid Howells
142e49c7b2fSDavid Howells _enter("");
143e49c7b2fSDavid Howells
144e49c7b2fSDavid Howells if (op->file[0].need_io_lock)
145e49c7b2fSDavid Howells if (!afs_get_io_locks(op))
146e49c7b2fSDavid Howells return false;
147e49c7b2fSDavid Howells
148e49c7b2fSDavid Howells afs_prepare_vnode(op, &op->file[0], 0);
149e49c7b2fSDavid Howells afs_prepare_vnode(op, &op->file[1], 1);
150e49c7b2fSDavid Howells op->cb_v_break = op->volume->cb_v_break;
151e49c7b2fSDavid Howells _leave(" = true");
152e49c7b2fSDavid Howells return true;
153e49c7b2fSDavid Howells }
154e49c7b2fSDavid Howells
155e49c7b2fSDavid Howells /*
156e49c7b2fSDavid Howells * Tidy up a filesystem cursor and unlock the vnode.
157e49c7b2fSDavid Howells */
afs_end_vnode_operation(struct afs_operation * op)158e49c7b2fSDavid Howells static void afs_end_vnode_operation(struct afs_operation *op)
159e49c7b2fSDavid Howells {
160e49c7b2fSDavid Howells _enter("");
161e49c7b2fSDavid Howells
162e49c7b2fSDavid Howells if (op->error == -EDESTADDRREQ ||
163e49c7b2fSDavid Howells op->error == -EADDRNOTAVAIL ||
164e49c7b2fSDavid Howells op->error == -ENETUNREACH ||
165e49c7b2fSDavid Howells op->error == -EHOSTUNREACH)
166e49c7b2fSDavid Howells afs_dump_edestaddrreq(op);
167e49c7b2fSDavid Howells
168e49c7b2fSDavid Howells afs_drop_io_locks(op);
169e49c7b2fSDavid Howells
170e49c7b2fSDavid Howells if (op->error == -ECONNABORTED)
171e49c7b2fSDavid Howells op->error = afs_abort_to_error(op->ac.abort_code);
172e49c7b2fSDavid Howells }
173e49c7b2fSDavid Howells
174e49c7b2fSDavid Howells /*
175e49c7b2fSDavid Howells * Wait for an in-progress operation to complete.
176e49c7b2fSDavid Howells */
afs_wait_for_operation(struct afs_operation * op)177e49c7b2fSDavid Howells void afs_wait_for_operation(struct afs_operation *op)
178e49c7b2fSDavid Howells {
179e49c7b2fSDavid Howells _enter("");
180e49c7b2fSDavid Howells
181e49c7b2fSDavid Howells while (afs_select_fileserver(op)) {
18220325960SDavid Howells op->cb_s_break = op->server->cb_s_break;
18320325960SDavid Howells if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) &&
184e49c7b2fSDavid Howells op->ops->issue_yfs_rpc)
185e49c7b2fSDavid Howells op->ops->issue_yfs_rpc(op);
18664fcbb61SDavid Howells else if (op->ops->issue_afs_rpc)
187e49c7b2fSDavid Howells op->ops->issue_afs_rpc(op);
18864fcbb61SDavid Howells else
18964fcbb61SDavid Howells op->ac.error = -ENOTSUPP;
190e49c7b2fSDavid Howells
19164fcbb61SDavid Howells if (op->call)
192e49c7b2fSDavid Howells op->error = afs_wait_for_call_to_complete(op->call, &op->ac);
193e49c7b2fSDavid Howells }
194e49c7b2fSDavid Howells
195728279a5SDavid Howells switch (op->error) {
196728279a5SDavid Howells case 0:
197e49c7b2fSDavid Howells _debug("success");
198e49c7b2fSDavid Howells op->ops->success(op);
199728279a5SDavid Howells break;
200728279a5SDavid Howells case -ECONNABORTED:
201728279a5SDavid Howells if (op->ops->aborted)
202728279a5SDavid Howells op->ops->aborted(op);
203dc419184SDavid Howells fallthrough;
204728279a5SDavid Howells default:
205dc419184SDavid Howells if (op->ops->failed)
206dc419184SDavid Howells op->ops->failed(op);
207728279a5SDavid Howells break;
208e49c7b2fSDavid Howells }
209e49c7b2fSDavid Howells
210e49c7b2fSDavid Howells afs_end_vnode_operation(op);
211e49c7b2fSDavid Howells
212e49c7b2fSDavid Howells if (op->error == 0 && op->ops->edit_dir) {
213e49c7b2fSDavid Howells _debug("edit_dir");
214e49c7b2fSDavid Howells op->ops->edit_dir(op);
215e49c7b2fSDavid Howells }
216e49c7b2fSDavid Howells _leave("");
217e49c7b2fSDavid Howells }
218e49c7b2fSDavid Howells
219e49c7b2fSDavid Howells /*
220e49c7b2fSDavid Howells * Dispose of an operation.
221e49c7b2fSDavid Howells */
afs_put_operation(struct afs_operation * op)222e49c7b2fSDavid Howells int afs_put_operation(struct afs_operation *op)
223e49c7b2fSDavid Howells {
224e49c7b2fSDavid Howells int i, ret = op->error;
225e49c7b2fSDavid Howells
226e49c7b2fSDavid Howells _enter("op=%08x,%d", op->debug_id, ret);
227e49c7b2fSDavid Howells
228e49c7b2fSDavid Howells if (op->ops && op->ops->put)
229e49c7b2fSDavid Howells op->ops->put(op);
23022650f14SDavid Howells if (op->file[0].modification)
23122650f14SDavid Howells clear_bit(AFS_VNODE_MODIFYING, &op->file[0].vnode->flags);
23222650f14SDavid Howells if (op->file[1].modification && op->file[1].vnode != op->file[0].vnode)
23322650f14SDavid Howells clear_bit(AFS_VNODE_MODIFYING, &op->file[1].vnode->flags);
234e49c7b2fSDavid Howells if (op->file[0].put_vnode)
235*874c8ca1SDavid Howells iput(&op->file[0].vnode->netfs.inode);
236e49c7b2fSDavid Howells if (op->file[1].put_vnode)
237*874c8ca1SDavid Howells iput(&op->file[1].vnode->netfs.inode);
238e49c7b2fSDavid Howells
239e49c7b2fSDavid Howells if (op->more_files) {
240e49c7b2fSDavid Howells for (i = 0; i < op->nr_files - 2; i++)
241e49c7b2fSDavid Howells if (op->more_files[i].put_vnode)
242*874c8ca1SDavid Howells iput(&op->more_files[i].vnode->netfs.inode);
243e49c7b2fSDavid Howells kfree(op->more_files);
244e49c7b2fSDavid Howells }
245e49c7b2fSDavid Howells
246e49c7b2fSDavid Howells afs_end_cursor(&op->ac);
247e49c7b2fSDavid Howells afs_put_serverlist(op->net, op->server_list);
248cca37d45SDavid Howells afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op);
249ba8e4207SDavid Howells key_put(op->key);
250e49c7b2fSDavid Howells kfree(op);
251e49c7b2fSDavid Howells return ret;
252e49c7b2fSDavid Howells }
253e49c7b2fSDavid Howells
afs_do_sync_operation(struct afs_operation * op)254e49c7b2fSDavid Howells int afs_do_sync_operation(struct afs_operation *op)
255e49c7b2fSDavid Howells {
256e49c7b2fSDavid Howells afs_begin_vnode_operation(op);
257e49c7b2fSDavid Howells afs_wait_for_operation(op);
258e49c7b2fSDavid Howells return afs_put_operation(op);
259e49c7b2fSDavid Howells }
260