David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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; |
David Howells | cca37d4 | 2020-04-29 17:02:04 +0100 | [diff] [blame] | 39 | op->volume = afs_get_volume(volume, afs_volume_trace_get_new_op); |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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) { |
David Howells | 811f04b | 2020-07-08 09:27:07 +0100 | [diff] [blame] | 74 | op->error = -ERESTARTSYS; |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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) { |
David Howells | 811f04b | 2020-07-08 09:27:07 +0100 | [diff] [blame] | 83 | op->error = -ERESTARTSYS; |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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; |
David Howells | 22650f1 | 2021-04-30 13:47:08 +0100 | [diff] [blame] | 121 | if (vp->modification) |
| 122 | set_bit(AFS_VNODE_MODIFYING, &vnode->flags); |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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 | |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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)) { |
David Howells | 2032596 | 2020-04-30 01:03:49 +0100 | [diff] [blame] | 182 | op->cb_s_break = op->server->cb_s_break; |
| 183 | if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) && |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 184 | op->ops->issue_yfs_rpc) |
| 185 | op->ops->issue_yfs_rpc(op); |
David Howells | 64fcbb6 | 2021-03-02 10:26:45 +0000 | [diff] [blame] | 186 | else if (op->ops->issue_afs_rpc) |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 187 | op->ops->issue_afs_rpc(op); |
David Howells | 64fcbb6 | 2021-03-02 10:26:45 +0000 | [diff] [blame] | 188 | else |
| 189 | op->ac.error = -ENOTSUPP; |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 190 | |
David Howells | 64fcbb6 | 2021-03-02 10:26:45 +0000 | [diff] [blame] | 191 | if (op->call) |
| 192 | op->error = afs_wait_for_call_to_complete(op->call, &op->ac); |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 193 | } |
| 194 | |
David Howells | 728279a | 2020-06-16 00:34:09 +0100 | [diff] [blame] | 195 | switch (op->error) { |
| 196 | case 0: |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 197 | _debug("success"); |
| 198 | op->ops->success(op); |
David Howells | 728279a | 2020-06-16 00:34:09 +0100 | [diff] [blame] | 199 | break; |
| 200 | case -ECONNABORTED: |
| 201 | if (op->ops->aborted) |
| 202 | op->ops->aborted(op); |
David Howells | dc41918 | 2020-09-18 09:11:15 +0100 | [diff] [blame] | 203 | fallthrough; |
David Howells | 728279a | 2020-06-16 00:34:09 +0100 | [diff] [blame] | 204 | default: |
David Howells | dc41918 | 2020-09-18 09:11:15 +0100 | [diff] [blame] | 205 | if (op->ops->failed) |
| 206 | op->ops->failed(op); |
David Howells | 728279a | 2020-06-16 00:34:09 +0100 | [diff] [blame] | 207 | break; |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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); |
David Howells | 22650f1 | 2021-04-30 13:47:08 +0100 | [diff] [blame] | 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); |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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); |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 247 | afs_put_serverlist(op->net, op->server_list); |
David Howells | cca37d4 | 2020-04-29 17:02:04 +0100 | [diff] [blame] | 248 | afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op); |
David Howells | ba8e420 | 2020-08-20 14:37:12 +0100 | [diff] [blame] | 249 | key_put(op->key); |
David Howells | e49c7b2 | 2020-04-10 20:51:51 +0100 | [diff] [blame] | 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 | } |