]>
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; | |
121 | } | |
122 | ||
123 | if (vp->fid.vnode) | |
124 | _debug("PREP[%u] {%llx:%llu.%u}", | |
125 | index, vp->fid.vid, vp->fid.vnode, vp->fid.unique); | |
126 | } | |
127 | ||
128 | /* | |
129 | * Begin an operation on the fileserver. | |
130 | * | |
131 | * Fileserver operations are serialised on the server by vnode, so we serialise | |
132 | * them here also using the io_lock. | |
133 | */ | |
134 | bool afs_begin_vnode_operation(struct afs_operation *op) | |
135 | { | |
136 | struct afs_vnode *vnode = op->file[0].vnode; | |
137 | ||
138 | ASSERT(vnode); | |
139 | ||
140 | _enter(""); | |
141 | ||
142 | if (op->file[0].need_io_lock) | |
143 | if (!afs_get_io_locks(op)) | |
144 | return false; | |
145 | ||
e49c7b2f DH |
146 | afs_prepare_vnode(op, &op->file[0], 0); |
147 | afs_prepare_vnode(op, &op->file[1], 1); | |
148 | op->cb_v_break = op->volume->cb_v_break; | |
149 | _leave(" = true"); | |
150 | return true; | |
151 | } | |
152 | ||
153 | /* | |
154 | * Tidy up a filesystem cursor and unlock the vnode. | |
155 | */ | |
156 | static void afs_end_vnode_operation(struct afs_operation *op) | |
157 | { | |
158 | _enter(""); | |
159 | ||
160 | if (op->error == -EDESTADDRREQ || | |
161 | op->error == -EADDRNOTAVAIL || | |
162 | op->error == -ENETUNREACH || | |
163 | op->error == -EHOSTUNREACH) | |
164 | afs_dump_edestaddrreq(op); | |
165 | ||
166 | afs_drop_io_locks(op); | |
167 | ||
168 | if (op->error == -ECONNABORTED) | |
169 | op->error = afs_abort_to_error(op->ac.abort_code); | |
170 | } | |
171 | ||
172 | /* | |
173 | * Wait for an in-progress operation to complete. | |
174 | */ | |
175 | void afs_wait_for_operation(struct afs_operation *op) | |
176 | { | |
177 | _enter(""); | |
178 | ||
179 | while (afs_select_fileserver(op)) { | |
20325960 DH |
180 | op->cb_s_break = op->server->cb_s_break; |
181 | if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) && | |
e49c7b2f DH |
182 | op->ops->issue_yfs_rpc) |
183 | op->ops->issue_yfs_rpc(op); | |
184 | else | |
185 | op->ops->issue_afs_rpc(op); | |
186 | ||
187 | op->error = afs_wait_for_call_to_complete(op->call, &op->ac); | |
188 | } | |
189 | ||
728279a5 DH |
190 | switch (op->error) { |
191 | case 0: | |
e49c7b2f DH |
192 | _debug("success"); |
193 | op->ops->success(op); | |
728279a5 DH |
194 | break; |
195 | case -ECONNABORTED: | |
196 | if (op->ops->aborted) | |
197 | op->ops->aborted(op); | |
198 | break; | |
199 | default: | |
200 | break; | |
e49c7b2f DH |
201 | } |
202 | ||
203 | afs_end_vnode_operation(op); | |
204 | ||
205 | if (op->error == 0 && op->ops->edit_dir) { | |
206 | _debug("edit_dir"); | |
207 | op->ops->edit_dir(op); | |
208 | } | |
209 | _leave(""); | |
210 | } | |
211 | ||
212 | /* | |
213 | * Dispose of an operation. | |
214 | */ | |
215 | int afs_put_operation(struct afs_operation *op) | |
216 | { | |
217 | int i, ret = op->error; | |
218 | ||
219 | _enter("op=%08x,%d", op->debug_id, ret); | |
220 | ||
221 | if (op->ops && op->ops->put) | |
222 | op->ops->put(op); | |
223 | if (op->file[0].put_vnode) | |
224 | iput(&op->file[0].vnode->vfs_inode); | |
225 | if (op->file[1].put_vnode) | |
226 | iput(&op->file[1].vnode->vfs_inode); | |
227 | ||
228 | if (op->more_files) { | |
229 | for (i = 0; i < op->nr_files - 2; i++) | |
230 | if (op->more_files[i].put_vnode) | |
231 | iput(&op->more_files[i].vnode->vfs_inode); | |
232 | kfree(op->more_files); | |
233 | } | |
234 | ||
235 | afs_end_cursor(&op->ac); | |
e49c7b2f | 236 | afs_put_serverlist(op->net, op->server_list); |
cca37d45 | 237 | afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op); |
e49c7b2f DH |
238 | kfree(op); |
239 | return ret; | |
240 | } | |
241 | ||
242 | int afs_do_sync_operation(struct afs_operation *op) | |
243 | { | |
244 | afs_begin_vnode_operation(op); | |
245 | afs_wait_for_operation(op); | |
246 | return afs_put_operation(op); | |
247 | } |