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 | ||
38 | op->key = key; | |
cca37d45 | 39 | op->volume = afs_get_volume(volume, afs_volume_trace_get_new_op); |
e49c7b2f DH |
40 | op->net = volume->cell->net; |
41 | op->cb_v_break = volume->cb_v_break; | |
42 | op->debug_id = atomic_inc_return(&afs_operation_debug_counter); | |
43 | op->error = -EDESTADDRREQ; | |
44 | op->ac.error = SHRT_MAX; | |
45 | ||
46 | _leave(" = [op=%08x]", op->debug_id); | |
47 | return op; | |
48 | } | |
49 | ||
50 | /* | |
51 | * Lock the vnode(s) being operated upon. | |
52 | */ | |
53 | static bool afs_get_io_locks(struct afs_operation *op) | |
54 | { | |
55 | struct afs_vnode *vnode = op->file[0].vnode; | |
56 | struct afs_vnode *vnode2 = op->file[1].vnode; | |
57 | ||
58 | _enter(""); | |
59 | ||
60 | if (op->flags & AFS_OPERATION_UNINTR) { | |
61 | mutex_lock(&vnode->io_lock); | |
62 | op->flags |= AFS_OPERATION_LOCK_0; | |
63 | _leave(" = t [1]"); | |
64 | return true; | |
65 | } | |
66 | ||
67 | if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2) | |
68 | vnode2 = NULL; | |
69 | ||
70 | if (vnode2 > vnode) | |
71 | swap(vnode, vnode2); | |
72 | ||
73 | if (mutex_lock_interruptible(&vnode->io_lock) < 0) { | |
811f04ba | 74 | op->error = -ERESTARTSYS; |
e49c7b2f DH |
75 | op->flags |= AFS_OPERATION_STOP; |
76 | _leave(" = f [I 0]"); | |
77 | return false; | |
78 | } | |
79 | op->flags |= AFS_OPERATION_LOCK_0; | |
80 | ||
81 | if (vnode2) { | |
82 | if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) { | |
811f04ba | 83 | op->error = -ERESTARTSYS; |
e49c7b2f DH |
84 | op->flags |= AFS_OPERATION_STOP; |
85 | mutex_unlock(&vnode->io_lock); | |
86 | op->flags &= ~AFS_OPERATION_LOCK_0; | |
87 | _leave(" = f [I 1]"); | |
88 | return false; | |
89 | } | |
90 | op->flags |= AFS_OPERATION_LOCK_1; | |
91 | } | |
92 | ||
93 | _leave(" = t [2]"); | |
94 | return true; | |
95 | } | |
96 | ||
97 | static void afs_drop_io_locks(struct afs_operation *op) | |
98 | { | |
99 | struct afs_vnode *vnode = op->file[0].vnode; | |
100 | struct afs_vnode *vnode2 = op->file[1].vnode; | |
101 | ||
102 | _enter(""); | |
103 | ||
104 | if (op->flags & AFS_OPERATION_LOCK_1) | |
105 | mutex_unlock(&vnode2->io_lock); | |
106 | if (op->flags & AFS_OPERATION_LOCK_0) | |
107 | mutex_unlock(&vnode->io_lock); | |
108 | } | |
109 | ||
110 | static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp, | |
111 | unsigned int index) | |
112 | { | |
113 | struct afs_vnode *vnode = vp->vnode; | |
114 | ||
115 | if (vnode) { | |
116 | vp->fid = vnode->fid; | |
117 | vp->dv_before = vnode->status.data_version; | |
118 | vp->cb_break_before = afs_calc_vnode_cb_break(vnode); | |
119 | if (vnode->lock_state != AFS_VNODE_LOCK_NONE) | |
120 | op->flags |= AFS_OPERATION_CUR_ONLY; | |
22650f14 DH |
121 | if (vp->modification) |
122 | set_bit(AFS_VNODE_MODIFYING, &vnode->flags); | |
e49c7b2f DH |
123 | } |
124 | ||
125 | if (vp->fid.vnode) | |
126 | _debug("PREP[%u] {%llx:%llu.%u}", | |
127 | index, vp->fid.vid, vp->fid.vnode, vp->fid.unique); | |
128 | } | |
129 | ||
130 | /* | |
131 | * Begin an operation on the fileserver. | |
132 | * | |
133 | * Fileserver operations are serialised on the server by vnode, so we serialise | |
134 | * them here also using the io_lock. | |
135 | */ | |
136 | bool afs_begin_vnode_operation(struct afs_operation *op) | |
137 | { | |
138 | struct afs_vnode *vnode = op->file[0].vnode; | |
139 | ||
140 | ASSERT(vnode); | |
141 | ||
142 | _enter(""); | |
143 | ||
144 | if (op->file[0].need_io_lock) | |
145 | if (!afs_get_io_locks(op)) | |
146 | return false; | |
147 | ||
e49c7b2f DH |
148 | afs_prepare_vnode(op, &op->file[0], 0); |
149 | afs_prepare_vnode(op, &op->file[1], 1); | |
150 | op->cb_v_break = op->volume->cb_v_break; | |
151 | _leave(" = true"); | |
152 | return true; | |
153 | } | |
154 | ||
155 | /* | |
156 | * Tidy up a filesystem cursor and unlock the vnode. | |
157 | */ | |
158 | static void afs_end_vnode_operation(struct afs_operation *op) | |
159 | { | |
160 | _enter(""); | |
161 | ||
162 | if (op->error == -EDESTADDRREQ || | |
163 | op->error == -EADDRNOTAVAIL || | |
164 | op->error == -ENETUNREACH || | |
165 | op->error == -EHOSTUNREACH) | |
166 | afs_dump_edestaddrreq(op); | |
167 | ||
168 | afs_drop_io_locks(op); | |
169 | ||
170 | if (op->error == -ECONNABORTED) | |
171 | op->error = afs_abort_to_error(op->ac.abort_code); | |
172 | } | |
173 | ||
174 | /* | |
175 | * Wait for an in-progress operation to complete. | |
176 | */ | |
177 | void afs_wait_for_operation(struct afs_operation *op) | |
178 | { | |
179 | _enter(""); | |
180 | ||
181 | while (afs_select_fileserver(op)) { | |
20325960 DH |
182 | op->cb_s_break = op->server->cb_s_break; |
183 | if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) && | |
e49c7b2f DH |
184 | op->ops->issue_yfs_rpc) |
185 | op->ops->issue_yfs_rpc(op); | |
64fcbb61 | 186 | else if (op->ops->issue_afs_rpc) |
e49c7b2f | 187 | op->ops->issue_afs_rpc(op); |
64fcbb61 DH |
188 | else |
189 | op->ac.error = -ENOTSUPP; | |
e49c7b2f | 190 | |
64fcbb61 DH |
191 | if (op->call) |
192 | op->error = afs_wait_for_call_to_complete(op->call, &op->ac); | |
e49c7b2f DH |
193 | } |
194 | ||
728279a5 DH |
195 | switch (op->error) { |
196 | case 0: | |
e49c7b2f DH |
197 | _debug("success"); |
198 | op->ops->success(op); | |
728279a5 DH |
199 | break; |
200 | case -ECONNABORTED: | |
201 | if (op->ops->aborted) | |
202 | op->ops->aborted(op); | |
dc419184 | 203 | fallthrough; |
728279a5 | 204 | default: |
dc419184 DH |
205 | if (op->ops->failed) |
206 | op->ops->failed(op); | |
728279a5 | 207 | break; |
e49c7b2f DH |
208 | } |
209 | ||
210 | afs_end_vnode_operation(op); | |
211 | ||
212 | if (op->error == 0 && op->ops->edit_dir) { | |
213 | _debug("edit_dir"); | |
214 | op->ops->edit_dir(op); | |
215 | } | |
216 | _leave(""); | |
217 | } | |
218 | ||
219 | /* | |
220 | * Dispose of an operation. | |
221 | */ | |
222 | int afs_put_operation(struct afs_operation *op) | |
223 | { | |
224 | int i, ret = op->error; | |
225 | ||
226 | _enter("op=%08x,%d", op->debug_id, ret); | |
227 | ||
228 | if (op->ops && op->ops->put) | |
229 | op->ops->put(op); | |
22650f14 DH |
230 | if (op->file[0].modification) |
231 | clear_bit(AFS_VNODE_MODIFYING, &op->file[0].vnode->flags); | |
232 | if (op->file[1].modification && op->file[1].vnode != op->file[0].vnode) | |
233 | clear_bit(AFS_VNODE_MODIFYING, &op->file[1].vnode->flags); | |
e49c7b2f DH |
234 | if (op->file[0].put_vnode) |
235 | iput(&op->file[0].vnode->vfs_inode); | |
236 | if (op->file[1].put_vnode) | |
237 | iput(&op->file[1].vnode->vfs_inode); | |
238 | ||
239 | if (op->more_files) { | |
240 | for (i = 0; i < op->nr_files - 2; i++) | |
241 | if (op->more_files[i].put_vnode) | |
242 | iput(&op->more_files[i].vnode->vfs_inode); | |
243 | kfree(op->more_files); | |
244 | } | |
245 | ||
246 | afs_end_cursor(&op->ac); | |
e49c7b2f | 247 | afs_put_serverlist(op->net, op->server_list); |
cca37d45 | 248 | afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op); |
ba8e4207 | 249 | key_put(op->key); |
e49c7b2f DH |
250 | kfree(op); |
251 | return ret; | |
252 | } | |
253 | ||
254 | int afs_do_sync_operation(struct afs_operation *op) | |
255 | { | |
256 | afs_begin_vnode_operation(op); | |
257 | afs_wait_for_operation(op); | |
258 | return afs_put_operation(op); | |
259 | } |