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