Commit | Line | Data |
---|---|---|
e49c7b2f DH |
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(sizeof(*op), GFP_KERNEL); | |
25 | if (!op) | |
26 | return ERR_PTR(-ENOMEM); | |
27 | ||
28 | if (!key) { | |
29 | key = afs_request_key(volume->cell); | |
30 | if (IS_ERR(key)) { | |
31 | kfree(op); | |
32 | return ERR_CAST(key); | |
33 | } | |
34 | } else { | |
35 | key_get(key); | |
36 | } | |
37 | ||
16069e13 DH |
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(&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(&afs_operation_debug_counter); | |
453924de | 45 | op->nr_iterations = -1; |
2de5599f | 46 | afs_op_set_error(op, -EDESTADDRREQ); |
e49c7b2f DH |
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) { | |
2de5599f | 76 | afs_op_set_error(op, -ERESTARTSYS); |
e49c7b2f DH |
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(&vnode2->io_lock, 1) < 0) { | |
2de5599f | 85 | afs_op_set_error(op, -ERESTARTSYS); |
e49c7b2f DH |
86 | op->flags |= AFS_OPERATION_STOP; |
87 | mutex_unlock(&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(&vnode2->io_lock); | |
108 | if (op->flags & AFS_OPERATION_LOCK_0) | |
109 | mutex_unlock(&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; | |
22650f14 DH |
123 | if (vp->modification) |
124 | set_bit(AFS_VNODE_MODIFYING, &vnode->flags); | |
e49c7b2f DH |
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 | ||
e49c7b2f DH |
150 | afs_prepare_vnode(op, &op->file[0], 0); |
151 | afs_prepare_vnode(op, &op->file[1], 1); | |
16069e13 | 152 | op->cb_v_break = atomic_read(&op->volume->cb_v_break); |
e49c7b2f DH |
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 | ||
2de5599f DH |
164 | switch (afs_op_error(op)) { |
165 | case -EDESTADDRREQ: | |
166 | case -EADDRNOTAVAIL: | |
167 | case -ENETUNREACH: | |
168 | case -EHOSTUNREACH: | |
e49c7b2f | 169 | afs_dump_edestaddrreq(op); |
2de5599f DH |
170 | break; |
171 | } | |
e49c7b2f DH |
172 | |
173 | afs_drop_io_locks(op); | |
e49c7b2f DH |
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)) { | |
98f9fda2 | 184 | op->call_responded = false; |
aa453bec DH |
185 | op->call_error = 0; |
186 | op->call_abort_code = 0; | |
20325960 | 187 | if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) && |
e49c7b2f DH |
188 | op->ops->issue_yfs_rpc) |
189 | op->ops->issue_yfs_rpc(op); | |
64fcbb61 | 190 | else if (op->ops->issue_afs_rpc) |
e49c7b2f | 191 | op->ops->issue_afs_rpc(op); |
64fcbb61 | 192 | else |
aa453bec | 193 | op->call_error = -ENOTSUPP; |
e49c7b2f | 194 | |
6f2ff7e8 | 195 | if (op->call) { |
98f9fda2 | 196 | afs_wait_for_call_to_complete(op->call); |
aa453bec DH |
197 | op->call_abort_code = op->call->abort_code; |
198 | op->call_error = op->call->error; | |
199 | op->call_responded = op->call->responded; | |
6f2ff7e8 DH |
200 | afs_put_call(op->call); |
201 | } | |
e49c7b2f DH |
202 | } |
203 | ||
98f9fda2 DH |
204 | if (op->call_responded) |
205 | set_bit(AFS_SERVER_FL_RESPONDING, &op->server->flags); | |
206 | ||
aa453bec | 207 | if (!afs_op_error(op)) { |
e49c7b2f DH |
208 | _debug("success"); |
209 | op->ops->success(op); | |
aa453bec | 210 | } else if (op->cumul_error.aborted) { |
728279a5 DH |
211 | if (op->ops->aborted) |
212 | op->ops->aborted(op); | |
aa453bec | 213 | } else { |
dc419184 DH |
214 | if (op->ops->failed) |
215 | op->ops->failed(op); | |
e49c7b2f DH |
216 | } |
217 | ||
218 | afs_end_vnode_operation(op); | |
219 | ||
2de5599f | 220 | if (!afs_op_error(op) && op->ops->edit_dir) { |
e49c7b2f DH |
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 | { | |
98f9fda2 | 232 | struct afs_addr_list *alist; |
2de5599f | 233 | int i, ret = afs_op_error(op); |
e49c7b2f DH |
234 | |
235 | _enter("op=%08x,%d", op->debug_id, ret); | |
236 | ||
237 | if (op->ops && op->ops->put) | |
238 | op->ops->put(op); | |
22650f14 DH |
239 | if (op->file[0].modification) |
240 | clear_bit(AFS_VNODE_MODIFYING, &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, &op->file[1].vnode->flags); | |
e49c7b2f | 243 | if (op->file[0].put_vnode) |
874c8ca1 | 244 | iput(&op->file[0].vnode->netfs.inode); |
e49c7b2f | 245 | if (op->file[1].put_vnode) |
874c8ca1 | 246 | iput(&op->file[1].vnode->netfs.inode); |
e49c7b2f DH |
247 | |
248 | if (op->more_files) { | |
249 | for (i = 0; i < op->nr_files - 2; i++) | |
250 | if (op->more_files[i].put_vnode) | |
874c8ca1 | 251 | iput(&op->more_files[i].vnode->netfs.inode); |
e49c7b2f DH |
252 | kfree(op->more_files); |
253 | } | |
254 | ||
495f2ae9 DH |
255 | if (op->estate) { |
256 | alist = op->estate->addresses; | |
f49b594d DH |
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 | } | |
98f9fda2 DH |
263 | } |
264 | ||
495f2ae9 | 265 | afs_clear_server_states(op); |
e49c7b2f | 266 | afs_put_serverlist(op->net, op->server_list); |
445f9b69 | 267 | afs_put_volume(op->volume, afs_volume_trace_put_put_op); |
ba8e4207 | 268 | key_put(op->key); |
e49c7b2f DH |
269 | kfree(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 | } |