| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* Handle fileserver selection and rotation. |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. |
|---|
| 4 | 5 | * Written by David Howells (dhowells@redhat.com) |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public Licence |
|---|
| 8 | | - * as published by the Free Software Foundation; either version |
|---|
| 9 | | - * 2 of the Licence, or (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 19 | 15 | #include "afs_fs.h" |
|---|
| 20 | 16 | |
|---|
| 21 | 17 | /* |
|---|
| 22 | | - * Initialise a filesystem server cursor for iterating over FS servers. |
|---|
| 23 | | - */ |
|---|
| 24 | | -static void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode) |
|---|
| 25 | | -{ |
|---|
| 26 | | - memset(fc, 0, sizeof(*fc)); |
|---|
| 27 | | -} |
|---|
| 28 | | - |
|---|
| 29 | | -/* |
|---|
| 30 | | - * Begin an operation on the fileserver. |
|---|
| 31 | | - * |
|---|
| 32 | | - * Fileserver operations are serialised on the server by vnode, so we serialise |
|---|
| 33 | | - * them here also using the io_lock. |
|---|
| 34 | | - */ |
|---|
| 35 | | -bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode, |
|---|
| 36 | | - struct key *key) |
|---|
| 37 | | -{ |
|---|
| 38 | | - afs_init_fs_cursor(fc, vnode); |
|---|
| 39 | | - fc->vnode = vnode; |
|---|
| 40 | | - fc->key = key; |
|---|
| 41 | | - fc->ac.error = SHRT_MAX; |
|---|
| 42 | | - |
|---|
| 43 | | - if (mutex_lock_interruptible(&vnode->io_lock) < 0) { |
|---|
| 44 | | - fc->ac.error = -EINTR; |
|---|
| 45 | | - fc->flags |= AFS_FS_CURSOR_STOP; |
|---|
| 46 | | - return false; |
|---|
| 47 | | - } |
|---|
| 48 | | - |
|---|
| 49 | | - if (vnode->lock_state != AFS_VNODE_LOCK_NONE) |
|---|
| 50 | | - fc->flags |= AFS_FS_CURSOR_CUR_ONLY; |
|---|
| 51 | | - return true; |
|---|
| 52 | | -} |
|---|
| 53 | | - |
|---|
| 54 | | -/* |
|---|
| 55 | 18 | * Begin iteration through a server list, starting with the vnode's last used |
|---|
| 56 | 19 | * server if possible, or the last recorded good server if not. |
|---|
| 57 | 20 | */ |
|---|
| 58 | | -static bool afs_start_fs_iteration(struct afs_fs_cursor *fc, |
|---|
| 21 | +static bool afs_start_fs_iteration(struct afs_operation *op, |
|---|
| 59 | 22 | struct afs_vnode *vnode) |
|---|
| 60 | 23 | { |
|---|
| 61 | | - struct afs_cb_interest *cbi; |
|---|
| 24 | + struct afs_server *server; |
|---|
| 25 | + void *cb_server; |
|---|
| 62 | 26 | int i; |
|---|
| 63 | 27 | |
|---|
| 64 | | - read_lock(&vnode->volume->servers_lock); |
|---|
| 65 | | - fc->server_list = afs_get_serverlist(vnode->volume->servers); |
|---|
| 66 | | - read_unlock(&vnode->volume->servers_lock); |
|---|
| 28 | + read_lock(&op->volume->servers_lock); |
|---|
| 29 | + op->server_list = afs_get_serverlist( |
|---|
| 30 | + rcu_dereference_protected(op->volume->servers, |
|---|
| 31 | + lockdep_is_held(&op->volume->servers_lock))); |
|---|
| 32 | + read_unlock(&op->volume->servers_lock); |
|---|
| 67 | 33 | |
|---|
| 68 | | - cbi = vnode->cb_interest; |
|---|
| 69 | | - if (cbi) { |
|---|
| 34 | + op->untried = (1UL << op->server_list->nr_servers) - 1; |
|---|
| 35 | + op->index = READ_ONCE(op->server_list->preferred); |
|---|
| 36 | + |
|---|
| 37 | + cb_server = vnode->cb_server; |
|---|
| 38 | + if (cb_server) { |
|---|
| 70 | 39 | /* See if the vnode's preferred record is still available */ |
|---|
| 71 | | - for (i = 0; i < fc->server_list->nr_servers; i++) { |
|---|
| 72 | | - if (fc->server_list->servers[i].cb_interest == cbi) { |
|---|
| 73 | | - fc->start = i; |
|---|
| 40 | + for (i = 0; i < op->server_list->nr_servers; i++) { |
|---|
| 41 | + server = op->server_list->servers[i].server; |
|---|
| 42 | + if (server == cb_server) { |
|---|
| 43 | + op->index = i; |
|---|
| 74 | 44 | goto found_interest; |
|---|
| 75 | 45 | } |
|---|
| 76 | 46 | } |
|---|
| .. | .. |
|---|
| 79 | 49 | * serving this vnode, then we can't switch to another server |
|---|
| 80 | 50 | * and have to return an error. |
|---|
| 81 | 51 | */ |
|---|
| 82 | | - if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) { |
|---|
| 83 | | - fc->ac.error = -ESTALE; |
|---|
| 52 | + if (op->flags & AFS_OPERATION_CUR_ONLY) { |
|---|
| 53 | + op->error = -ESTALE; |
|---|
| 84 | 54 | return false; |
|---|
| 85 | 55 | } |
|---|
| 86 | 56 | |
|---|
| 87 | 57 | /* Note that the callback promise is effectively broken */ |
|---|
| 88 | 58 | write_seqlock(&vnode->cb_lock); |
|---|
| 89 | | - ASSERTCMP(cbi, ==, vnode->cb_interest); |
|---|
| 90 | | - vnode->cb_interest = NULL; |
|---|
| 59 | + ASSERTCMP(cb_server, ==, vnode->cb_server); |
|---|
| 60 | + vnode->cb_server = NULL; |
|---|
| 91 | 61 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) |
|---|
| 92 | 62 | vnode->cb_break++; |
|---|
| 93 | 63 | write_sequnlock(&vnode->cb_lock); |
|---|
| 94 | | - |
|---|
| 95 | | - afs_put_cb_interest(afs_v2net(vnode), cbi); |
|---|
| 96 | | - cbi = NULL; |
|---|
| 97 | | - } else { |
|---|
| 98 | | - fc->start = READ_ONCE(fc->server_list->index); |
|---|
| 99 | 64 | } |
|---|
| 100 | 65 | |
|---|
| 101 | 66 | found_interest: |
|---|
| 102 | | - fc->index = fc->start; |
|---|
| 103 | 67 | return true; |
|---|
| 104 | 68 | } |
|---|
| 105 | 69 | |
|---|
| .. | .. |
|---|
| 117 | 81 | default: m = "busy"; break; |
|---|
| 118 | 82 | } |
|---|
| 119 | 83 | |
|---|
| 120 | | - pr_notice("kAFS: Volume %u '%s' is %s\n", volume->vid, volume->name, m); |
|---|
| 84 | + pr_notice("kAFS: Volume %llu '%s' is %s\n", volume->vid, volume->name, m); |
|---|
| 121 | 85 | } |
|---|
| 122 | 86 | |
|---|
| 123 | 87 | /* |
|---|
| 124 | 88 | * Sleep and retry the operation to the same fileserver. |
|---|
| 125 | 89 | */ |
|---|
| 126 | | -static bool afs_sleep_and_retry(struct afs_fs_cursor *fc) |
|---|
| 90 | +static bool afs_sleep_and_retry(struct afs_operation *op) |
|---|
| 127 | 91 | { |
|---|
| 128 | | - msleep_interruptible(1000); |
|---|
| 129 | | - if (signal_pending(current)) { |
|---|
| 130 | | - fc->ac.error = -ERESTARTSYS; |
|---|
| 131 | | - return false; |
|---|
| 92 | + if (!(op->flags & AFS_OPERATION_UNINTR)) { |
|---|
| 93 | + msleep_interruptible(1000); |
|---|
| 94 | + if (signal_pending(current)) { |
|---|
| 95 | + op->error = -ERESTARTSYS; |
|---|
| 96 | + return false; |
|---|
| 97 | + } |
|---|
| 98 | + } else { |
|---|
| 99 | + msleep(1000); |
|---|
| 132 | 100 | } |
|---|
| 133 | 101 | |
|---|
| 134 | 102 | return true; |
|---|
| .. | .. |
|---|
| 138 | 106 | * Select the fileserver to use. May be called multiple times to rotate |
|---|
| 139 | 107 | * through the fileservers. |
|---|
| 140 | 108 | */ |
|---|
| 141 | | -bool afs_select_fileserver(struct afs_fs_cursor *fc) |
|---|
| 109 | +bool afs_select_fileserver(struct afs_operation *op) |
|---|
| 142 | 110 | { |
|---|
| 143 | 111 | struct afs_addr_list *alist; |
|---|
| 144 | 112 | struct afs_server *server; |
|---|
| 145 | | - struct afs_vnode *vnode = fc->vnode; |
|---|
| 113 | + struct afs_vnode *vnode = op->file[0].vnode; |
|---|
| 114 | + struct afs_error e; |
|---|
| 115 | + u32 rtt; |
|---|
| 116 | + int error = op->ac.error, i; |
|---|
| 146 | 117 | |
|---|
| 147 | | - _enter("%u/%u,%u/%u,%d,%d", |
|---|
| 148 | | - fc->index, fc->start, |
|---|
| 149 | | - fc->ac.index, fc->ac.start, |
|---|
| 150 | | - fc->ac.error, fc->ac.abort_code); |
|---|
| 118 | + _enter("%lx[%d],%lx[%d],%d,%d", |
|---|
| 119 | + op->untried, op->index, |
|---|
| 120 | + op->ac.tried, op->ac.index, |
|---|
| 121 | + error, op->ac.abort_code); |
|---|
| 151 | 122 | |
|---|
| 152 | | - if (fc->flags & AFS_FS_CURSOR_STOP) { |
|---|
| 123 | + if (op->flags & AFS_OPERATION_STOP) { |
|---|
| 153 | 124 | _leave(" = f [stopped]"); |
|---|
| 154 | 125 | return false; |
|---|
| 155 | 126 | } |
|---|
| 156 | 127 | |
|---|
| 128 | + op->nr_iterations++; |
|---|
| 129 | + |
|---|
| 157 | 130 | /* Evaluate the result of the previous operation, if there was one. */ |
|---|
| 158 | | - switch (fc->ac.error) { |
|---|
| 131 | + switch (error) { |
|---|
| 159 | 132 | case SHRT_MAX: |
|---|
| 160 | 133 | goto start; |
|---|
| 161 | 134 | |
|---|
| 162 | 135 | case 0: |
|---|
| 163 | 136 | default: |
|---|
| 164 | 137 | /* Success or local failure. Stop. */ |
|---|
| 165 | | - fc->flags |= AFS_FS_CURSOR_STOP; |
|---|
| 166 | | - _leave(" = f [okay/local %d]", fc->ac.error); |
|---|
| 138 | + op->error = error; |
|---|
| 139 | + op->flags |= AFS_OPERATION_STOP; |
|---|
| 140 | + _leave(" = f [okay/local %d]", error); |
|---|
| 167 | 141 | return false; |
|---|
| 168 | 142 | |
|---|
| 169 | 143 | case -ECONNABORTED: |
|---|
| 170 | 144 | /* The far side rejected the operation on some grounds. This |
|---|
| 171 | 145 | * might involve the server being busy or the volume having been moved. |
|---|
| 172 | 146 | */ |
|---|
| 173 | | - switch (fc->ac.abort_code) { |
|---|
| 147 | + switch (op->ac.abort_code) { |
|---|
| 174 | 148 | case VNOVOL: |
|---|
| 175 | 149 | /* This fileserver doesn't know about the volume. |
|---|
| 176 | 150 | * - May indicate that the VL is wrong - retry once and compare |
|---|
| 177 | 151 | * the results. |
|---|
| 178 | 152 | * - May indicate that the fileserver couldn't attach to the vol. |
|---|
| 179 | 153 | */ |
|---|
| 180 | | - if (fc->flags & AFS_FS_CURSOR_VNOVOL) { |
|---|
| 181 | | - fc->ac.error = -EREMOTEIO; |
|---|
| 154 | + if (op->flags & AFS_OPERATION_VNOVOL) { |
|---|
| 155 | + op->error = -EREMOTEIO; |
|---|
| 182 | 156 | goto next_server; |
|---|
| 183 | 157 | } |
|---|
| 184 | 158 | |
|---|
| 185 | | - write_lock(&vnode->volume->servers_lock); |
|---|
| 186 | | - fc->server_list->vnovol_mask |= 1 << fc->index; |
|---|
| 187 | | - write_unlock(&vnode->volume->servers_lock); |
|---|
| 159 | + write_lock(&op->volume->servers_lock); |
|---|
| 160 | + op->server_list->vnovol_mask |= 1 << op->index; |
|---|
| 161 | + write_unlock(&op->volume->servers_lock); |
|---|
| 188 | 162 | |
|---|
| 189 | | - set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags); |
|---|
| 190 | | - fc->ac.error = afs_check_volume_status(vnode->volume, fc->key); |
|---|
| 191 | | - if (fc->ac.error < 0) |
|---|
| 192 | | - goto failed; |
|---|
| 163 | + set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags); |
|---|
| 164 | + error = afs_check_volume_status(op->volume, op); |
|---|
| 165 | + if (error < 0) |
|---|
| 166 | + goto failed_set_error; |
|---|
| 193 | 167 | |
|---|
| 194 | | - if (test_bit(AFS_VOLUME_DELETED, &vnode->volume->flags)) { |
|---|
| 195 | | - fc->ac.error = -ENOMEDIUM; |
|---|
| 168 | + if (test_bit(AFS_VOLUME_DELETED, &op->volume->flags)) { |
|---|
| 169 | + op->error = -ENOMEDIUM; |
|---|
| 196 | 170 | goto failed; |
|---|
| 197 | 171 | } |
|---|
| 198 | 172 | |
|---|
| 199 | 173 | /* If the server list didn't change, then assume that |
|---|
| 200 | 174 | * it's the fileserver having trouble. |
|---|
| 201 | 175 | */ |
|---|
| 202 | | - if (vnode->volume->servers == fc->server_list) { |
|---|
| 203 | | - fc->ac.error = -EREMOTEIO; |
|---|
| 176 | + if (rcu_access_pointer(op->volume->servers) == op->server_list) { |
|---|
| 177 | + op->error = -EREMOTEIO; |
|---|
| 204 | 178 | goto next_server; |
|---|
| 205 | 179 | } |
|---|
| 206 | 180 | |
|---|
| 207 | 181 | /* Try again */ |
|---|
| 208 | | - fc->flags |= AFS_FS_CURSOR_VNOVOL; |
|---|
| 182 | + op->flags |= AFS_OPERATION_VNOVOL; |
|---|
| 209 | 183 | _leave(" = t [vnovol]"); |
|---|
| 210 | 184 | return true; |
|---|
| 211 | 185 | |
|---|
| .. | .. |
|---|
| 215 | 189 | case VONLINE: |
|---|
| 216 | 190 | case VDISKFULL: |
|---|
| 217 | 191 | case VOVERQUOTA: |
|---|
| 218 | | - fc->ac.error = afs_abort_to_error(fc->ac.abort_code); |
|---|
| 192 | + op->error = afs_abort_to_error(op->ac.abort_code); |
|---|
| 219 | 193 | goto next_server; |
|---|
| 220 | 194 | |
|---|
| 221 | 195 | case VOFFLINE: |
|---|
| 222 | | - if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags)) { |
|---|
| 223 | | - afs_busy(vnode->volume, fc->ac.abort_code); |
|---|
| 224 | | - clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags); |
|---|
| 196 | + if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &op->volume->flags)) { |
|---|
| 197 | + afs_busy(op->volume, op->ac.abort_code); |
|---|
| 198 | + clear_bit(AFS_VOLUME_BUSY, &op->volume->flags); |
|---|
| 225 | 199 | } |
|---|
| 226 | | - if (fc->flags & AFS_FS_CURSOR_NO_VSLEEP) { |
|---|
| 227 | | - fc->ac.error = -EADV; |
|---|
| 200 | + if (op->flags & AFS_OPERATION_NO_VSLEEP) { |
|---|
| 201 | + op->error = -EADV; |
|---|
| 228 | 202 | goto failed; |
|---|
| 229 | 203 | } |
|---|
| 230 | | - if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) { |
|---|
| 231 | | - fc->ac.error = -ESTALE; |
|---|
| 204 | + if (op->flags & AFS_OPERATION_CUR_ONLY) { |
|---|
| 205 | + op->error = -ESTALE; |
|---|
| 232 | 206 | goto failed; |
|---|
| 233 | 207 | } |
|---|
| 234 | 208 | goto busy; |
|---|
| .. | .. |
|---|
| 239 | 213 | /* Retry after going round all the servers unless we |
|---|
| 240 | 214 | * have a file lock we need to maintain. |
|---|
| 241 | 215 | */ |
|---|
| 242 | | - if (fc->flags & AFS_FS_CURSOR_NO_VSLEEP) { |
|---|
| 243 | | - fc->ac.error = -EBUSY; |
|---|
| 216 | + if (op->flags & AFS_OPERATION_NO_VSLEEP) { |
|---|
| 217 | + op->error = -EBUSY; |
|---|
| 244 | 218 | goto failed; |
|---|
| 245 | 219 | } |
|---|
| 246 | | - if (!test_and_set_bit(AFS_VOLUME_BUSY, &vnode->volume->flags)) { |
|---|
| 247 | | - afs_busy(vnode->volume, fc->ac.abort_code); |
|---|
| 248 | | - clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags); |
|---|
| 220 | + if (!test_and_set_bit(AFS_VOLUME_BUSY, &op->volume->flags)) { |
|---|
| 221 | + afs_busy(op->volume, op->ac.abort_code); |
|---|
| 222 | + clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags); |
|---|
| 249 | 223 | } |
|---|
| 250 | 224 | busy: |
|---|
| 251 | | - if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) { |
|---|
| 252 | | - if (!afs_sleep_and_retry(fc)) |
|---|
| 225 | + if (op->flags & AFS_OPERATION_CUR_ONLY) { |
|---|
| 226 | + if (!afs_sleep_and_retry(op)) |
|---|
| 253 | 227 | goto failed; |
|---|
| 254 | 228 | |
|---|
| 255 | 229 | /* Retry with same server & address */ |
|---|
| .. | .. |
|---|
| 257 | 231 | return true; |
|---|
| 258 | 232 | } |
|---|
| 259 | 233 | |
|---|
| 260 | | - fc->flags |= AFS_FS_CURSOR_VBUSY; |
|---|
| 234 | + op->flags |= AFS_OPERATION_VBUSY; |
|---|
| 261 | 235 | goto next_server; |
|---|
| 262 | 236 | |
|---|
| 263 | 237 | case VMOVED: |
|---|
| .. | .. |
|---|
| 268 | 242 | * We also limit the number of VMOVED hops we will |
|---|
| 269 | 243 | * honour, just in case someone sets up a loop. |
|---|
| 270 | 244 | */ |
|---|
| 271 | | - if (fc->flags & AFS_FS_CURSOR_VMOVED) { |
|---|
| 272 | | - fc->ac.error = -EREMOTEIO; |
|---|
| 245 | + if (op->flags & AFS_OPERATION_VMOVED) { |
|---|
| 246 | + op->error = -EREMOTEIO; |
|---|
| 273 | 247 | goto failed; |
|---|
| 274 | 248 | } |
|---|
| 275 | | - fc->flags |= AFS_FS_CURSOR_VMOVED; |
|---|
| 249 | + op->flags |= AFS_OPERATION_VMOVED; |
|---|
| 276 | 250 | |
|---|
| 277 | | - set_bit(AFS_VOLUME_WAIT, &vnode->volume->flags); |
|---|
| 278 | | - set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags); |
|---|
| 279 | | - fc->ac.error = afs_check_volume_status(vnode->volume, fc->key); |
|---|
| 280 | | - if (fc->ac.error < 0) |
|---|
| 281 | | - goto failed; |
|---|
| 251 | + set_bit(AFS_VOLUME_WAIT, &op->volume->flags); |
|---|
| 252 | + set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags); |
|---|
| 253 | + error = afs_check_volume_status(op->volume, op); |
|---|
| 254 | + if (error < 0) |
|---|
| 255 | + goto failed_set_error; |
|---|
| 282 | 256 | |
|---|
| 283 | 257 | /* If the server list didn't change, then the VLDB is |
|---|
| 284 | 258 | * out of sync with the fileservers. This is hopefully |
|---|
| .. | .. |
|---|
| 289 | 263 | * |
|---|
| 290 | 264 | * TODO: Retry a few times with sleeps. |
|---|
| 291 | 265 | */ |
|---|
| 292 | | - if (vnode->volume->servers == fc->server_list) { |
|---|
| 293 | | - fc->ac.error = -ENOMEDIUM; |
|---|
| 266 | + if (rcu_access_pointer(op->volume->servers) == op->server_list) { |
|---|
| 267 | + op->error = -ENOMEDIUM; |
|---|
| 294 | 268 | goto failed; |
|---|
| 295 | 269 | } |
|---|
| 296 | 270 | |
|---|
| 297 | 271 | goto restart_from_beginning; |
|---|
| 298 | 272 | |
|---|
| 299 | 273 | default: |
|---|
| 300 | | - clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags); |
|---|
| 301 | | - clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags); |
|---|
| 302 | | - fc->ac.error = afs_abort_to_error(fc->ac.abort_code); |
|---|
| 274 | + clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags); |
|---|
| 275 | + clear_bit(AFS_VOLUME_BUSY, &op->volume->flags); |
|---|
| 276 | + op->error = afs_abort_to_error(op->ac.abort_code); |
|---|
| 303 | 277 | goto failed; |
|---|
| 304 | 278 | } |
|---|
| 305 | 279 | |
|---|
| 306 | | - case -ENETUNREACH: |
|---|
| 307 | | - case -EHOSTUNREACH: |
|---|
| 308 | | - case -ECONNREFUSED: |
|---|
| 309 | 280 | case -ETIMEDOUT: |
|---|
| 310 | 281 | case -ETIME: |
|---|
| 282 | + if (op->error != -EDESTADDRREQ) |
|---|
| 283 | + goto iterate_address; |
|---|
| 284 | + fallthrough; |
|---|
| 285 | + case -ERFKILL: |
|---|
| 286 | + case -EADDRNOTAVAIL: |
|---|
| 287 | + case -ENETUNREACH: |
|---|
| 288 | + case -EHOSTUNREACH: |
|---|
| 289 | + case -EHOSTDOWN: |
|---|
| 290 | + case -ECONNREFUSED: |
|---|
| 311 | 291 | _debug("no conn"); |
|---|
| 292 | + op->error = error; |
|---|
| 312 | 293 | goto iterate_address; |
|---|
| 313 | 294 | |
|---|
| 314 | 295 | case -ECONNRESET: |
|---|
| 315 | 296 | _debug("call reset"); |
|---|
| 297 | + op->error = error; |
|---|
| 316 | 298 | goto failed; |
|---|
| 317 | 299 | } |
|---|
| 318 | 300 | |
|---|
| 319 | 301 | restart_from_beginning: |
|---|
| 320 | 302 | _debug("restart"); |
|---|
| 321 | | - afs_end_cursor(&fc->ac); |
|---|
| 322 | | - afs_put_cb_interest(afs_v2net(vnode), fc->cbi); |
|---|
| 323 | | - fc->cbi = NULL; |
|---|
| 324 | | - afs_put_serverlist(afs_v2net(vnode), fc->server_list); |
|---|
| 325 | | - fc->server_list = NULL; |
|---|
| 303 | + afs_end_cursor(&op->ac); |
|---|
| 304 | + op->server = NULL; |
|---|
| 305 | + afs_put_serverlist(op->net, op->server_list); |
|---|
| 306 | + op->server_list = NULL; |
|---|
| 326 | 307 | start: |
|---|
| 327 | 308 | _debug("start"); |
|---|
| 328 | 309 | /* See if we need to do an update of the volume record. Note that the |
|---|
| 329 | 310 | * volume may have moved or even have been deleted. |
|---|
| 330 | 311 | */ |
|---|
| 331 | | - fc->ac.error = afs_check_volume_status(vnode->volume, fc->key); |
|---|
| 332 | | - if (fc->ac.error < 0) |
|---|
| 312 | + error = afs_check_volume_status(op->volume, op); |
|---|
| 313 | + if (error < 0) |
|---|
| 314 | + goto failed_set_error; |
|---|
| 315 | + |
|---|
| 316 | + if (!afs_start_fs_iteration(op, vnode)) |
|---|
| 333 | 317 | goto failed; |
|---|
| 334 | 318 | |
|---|
| 335 | | - if (!afs_start_fs_iteration(fc, vnode)) |
|---|
| 336 | | - goto failed; |
|---|
| 319 | + _debug("__ VOL %llx __", op->volume->vid); |
|---|
| 337 | 320 | |
|---|
| 338 | | -use_server: |
|---|
| 339 | | - _debug("use"); |
|---|
| 321 | +pick_server: |
|---|
| 322 | + _debug("pick [%lx]", op->untried); |
|---|
| 323 | + |
|---|
| 324 | + error = afs_wait_for_fs_probes(op->server_list, op->untried); |
|---|
| 325 | + if (error < 0) |
|---|
| 326 | + goto failed_set_error; |
|---|
| 327 | + |
|---|
| 328 | + /* Pick the untried server with the lowest RTT. If we have outstanding |
|---|
| 329 | + * callbacks, we stick with the server we're already using if we can. |
|---|
| 330 | + */ |
|---|
| 331 | + if (op->server) { |
|---|
| 332 | + _debug("server %u", op->index); |
|---|
| 333 | + if (test_bit(op->index, &op->untried)) |
|---|
| 334 | + goto selected_server; |
|---|
| 335 | + op->server = NULL; |
|---|
| 336 | + _debug("no server"); |
|---|
| 337 | + } |
|---|
| 338 | + |
|---|
| 339 | + op->index = -1; |
|---|
| 340 | + rtt = U32_MAX; |
|---|
| 341 | + for (i = 0; i < op->server_list->nr_servers; i++) { |
|---|
| 342 | + struct afs_server *s = op->server_list->servers[i].server; |
|---|
| 343 | + |
|---|
| 344 | + if (!test_bit(i, &op->untried) || |
|---|
| 345 | + !test_bit(AFS_SERVER_FL_RESPONDING, &s->flags)) |
|---|
| 346 | + continue; |
|---|
| 347 | + if (s->probe.rtt < rtt) { |
|---|
| 348 | + op->index = i; |
|---|
| 349 | + rtt = s->probe.rtt; |
|---|
| 350 | + } |
|---|
| 351 | + } |
|---|
| 352 | + |
|---|
| 353 | + if (op->index == -1) |
|---|
| 354 | + goto no_more_servers; |
|---|
| 355 | + |
|---|
| 356 | +selected_server: |
|---|
| 357 | + _debug("use %d", op->index); |
|---|
| 358 | + __clear_bit(op->index, &op->untried); |
|---|
| 359 | + |
|---|
| 340 | 360 | /* We're starting on a different fileserver from the list. We need to |
|---|
| 341 | 361 | * check it, create a callback intercept, find its address list and |
|---|
| 342 | 362 | * probe its capabilities before we use it. |
|---|
| 343 | 363 | */ |
|---|
| 344 | | - ASSERTCMP(fc->ac.alist, ==, NULL); |
|---|
| 345 | | - server = fc->server_list->servers[fc->index].server; |
|---|
| 364 | + ASSERTCMP(op->ac.alist, ==, NULL); |
|---|
| 365 | + server = op->server_list->servers[op->index].server; |
|---|
| 346 | 366 | |
|---|
| 347 | | - if (!afs_check_server_record(fc, server)) |
|---|
| 367 | + if (!afs_check_server_record(op, server)) |
|---|
| 348 | 368 | goto failed; |
|---|
| 349 | 369 | |
|---|
| 350 | 370 | _debug("USING SERVER: %pU", &server->uuid); |
|---|
| 351 | 371 | |
|---|
| 352 | | - /* Make sure we've got a callback interest record for this server. We |
|---|
| 353 | | - * have to link it in before we send the request as we can be sent a |
|---|
| 354 | | - * break request before we've finished decoding the reply and |
|---|
| 355 | | - * installing the vnode. |
|---|
| 356 | | - */ |
|---|
| 357 | | - fc->ac.error = afs_register_server_cb_interest(vnode, fc->server_list, |
|---|
| 358 | | - fc->index); |
|---|
| 359 | | - if (fc->ac.error < 0) |
|---|
| 360 | | - goto failed; |
|---|
| 361 | | - |
|---|
| 362 | | - fc->cbi = afs_get_cb_interest(vnode->cb_interest); |
|---|
| 372 | + op->flags |= AFS_OPERATION_RETRY_SERVER; |
|---|
| 373 | + op->server = server; |
|---|
| 374 | + if (vnode->cb_server != server) { |
|---|
| 375 | + vnode->cb_server = server; |
|---|
| 376 | + vnode->cb_s_break = server->cb_s_break; |
|---|
| 377 | + vnode->cb_v_break = vnode->volume->cb_v_break; |
|---|
| 378 | + clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); |
|---|
| 379 | + } |
|---|
| 363 | 380 | |
|---|
| 364 | 381 | read_lock(&server->fs_lock); |
|---|
| 365 | 382 | alist = rcu_dereference_protected(server->addresses, |
|---|
| .. | .. |
|---|
| 367 | 384 | afs_get_addrlist(alist); |
|---|
| 368 | 385 | read_unlock(&server->fs_lock); |
|---|
| 369 | 386 | |
|---|
| 370 | | - memset(&fc->ac, 0, sizeof(fc->ac)); |
|---|
| 387 | +retry_server: |
|---|
| 388 | + memset(&op->ac, 0, sizeof(op->ac)); |
|---|
| 371 | 389 | |
|---|
| 372 | | - /* Probe the current fileserver if we haven't done so yet. */ |
|---|
| 373 | | - if (!test_bit(AFS_SERVER_FL_PROBED, &server->flags)) { |
|---|
| 374 | | - fc->ac.alist = afs_get_addrlist(alist); |
|---|
| 375 | | - |
|---|
| 376 | | - if (!afs_probe_fileserver(fc)) { |
|---|
| 377 | | - switch (fc->ac.error) { |
|---|
| 378 | | - case -ENOMEM: |
|---|
| 379 | | - case -ERESTARTSYS: |
|---|
| 380 | | - case -EINTR: |
|---|
| 381 | | - goto failed; |
|---|
| 382 | | - default: |
|---|
| 383 | | - goto next_server; |
|---|
| 384 | | - } |
|---|
| 385 | | - } |
|---|
| 386 | | - } |
|---|
| 387 | | - |
|---|
| 388 | | - if (!fc->ac.alist) |
|---|
| 389 | | - fc->ac.alist = alist; |
|---|
| 390 | + if (!op->ac.alist) |
|---|
| 391 | + op->ac.alist = alist; |
|---|
| 390 | 392 | else |
|---|
| 391 | 393 | afs_put_addrlist(alist); |
|---|
| 392 | 394 | |
|---|
| 393 | | - fc->ac.start = READ_ONCE(alist->index); |
|---|
| 394 | | - fc->ac.index = fc->ac.start; |
|---|
| 395 | + op->ac.index = -1; |
|---|
| 395 | 396 | |
|---|
| 396 | 397 | iterate_address: |
|---|
| 397 | | - ASSERT(fc->ac.alist); |
|---|
| 398 | | - _debug("iterate %d/%d", fc->ac.index, fc->ac.alist->nr_addrs); |
|---|
| 398 | + ASSERT(op->ac.alist); |
|---|
| 399 | 399 | /* Iterate over the current server's address list to try and find an |
|---|
| 400 | 400 | * address on which it will respond to us. |
|---|
| 401 | 401 | */ |
|---|
| 402 | | - if (!afs_iterate_addresses(&fc->ac)) |
|---|
| 403 | | - goto next_server; |
|---|
| 402 | + if (!afs_iterate_addresses(&op->ac)) |
|---|
| 403 | + goto out_of_addresses; |
|---|
| 404 | + |
|---|
| 405 | + _debug("address [%u] %u/%u %pISp", |
|---|
| 406 | + op->index, op->ac.index, op->ac.alist->nr_addrs, |
|---|
| 407 | + &op->ac.alist->addrs[op->ac.index].transport); |
|---|
| 404 | 408 | |
|---|
| 405 | 409 | _leave(" = t"); |
|---|
| 406 | 410 | return true; |
|---|
| 407 | 411 | |
|---|
| 412 | +out_of_addresses: |
|---|
| 413 | + /* We've now had a failure to respond on all of a server's addresses - |
|---|
| 414 | + * immediately probe them again and consider retrying the server. |
|---|
| 415 | + */ |
|---|
| 416 | + afs_probe_fileserver(op->net, op->server); |
|---|
| 417 | + if (op->flags & AFS_OPERATION_RETRY_SERVER) { |
|---|
| 418 | + alist = op->ac.alist; |
|---|
| 419 | + error = afs_wait_for_one_fs_probe( |
|---|
| 420 | + op->server, !(op->flags & AFS_OPERATION_UNINTR)); |
|---|
| 421 | + switch (error) { |
|---|
| 422 | + case 0: |
|---|
| 423 | + op->flags &= ~AFS_OPERATION_RETRY_SERVER; |
|---|
| 424 | + goto retry_server; |
|---|
| 425 | + case -ERESTARTSYS: |
|---|
| 426 | + goto failed_set_error; |
|---|
| 427 | + case -ETIME: |
|---|
| 428 | + case -EDESTADDRREQ: |
|---|
| 429 | + goto next_server; |
|---|
| 430 | + } |
|---|
| 431 | + } |
|---|
| 432 | + |
|---|
| 408 | 433 | next_server: |
|---|
| 409 | 434 | _debug("next"); |
|---|
| 410 | | - afs_end_cursor(&fc->ac); |
|---|
| 411 | | - afs_put_cb_interest(afs_v2net(vnode), fc->cbi); |
|---|
| 412 | | - fc->cbi = NULL; |
|---|
| 413 | | - fc->index++; |
|---|
| 414 | | - if (fc->index >= fc->server_list->nr_servers) |
|---|
| 415 | | - fc->index = 0; |
|---|
| 416 | | - if (fc->index != fc->start) |
|---|
| 417 | | - goto use_server; |
|---|
| 435 | + afs_end_cursor(&op->ac); |
|---|
| 436 | + goto pick_server; |
|---|
| 418 | 437 | |
|---|
| 438 | +no_more_servers: |
|---|
| 419 | 439 | /* That's all the servers poked to no good effect. Try again if some |
|---|
| 420 | 440 | * of them were busy. |
|---|
| 421 | 441 | */ |
|---|
| 422 | | - if (fc->flags & AFS_FS_CURSOR_VBUSY) |
|---|
| 442 | + if (op->flags & AFS_OPERATION_VBUSY) |
|---|
| 423 | 443 | goto restart_from_beginning; |
|---|
| 424 | 444 | |
|---|
| 425 | | - fc->ac.error = -EDESTADDRREQ; |
|---|
| 426 | | - goto failed; |
|---|
| 445 | + e.error = -EDESTADDRREQ; |
|---|
| 446 | + e.responded = false; |
|---|
| 447 | + for (i = 0; i < op->server_list->nr_servers; i++) { |
|---|
| 448 | + struct afs_server *s = op->server_list->servers[i].server; |
|---|
| 427 | 449 | |
|---|
| 450 | + afs_prioritise_error(&e, READ_ONCE(s->probe.error), |
|---|
| 451 | + s->probe.abort_code); |
|---|
| 452 | + } |
|---|
| 453 | + |
|---|
| 454 | + error = e.error; |
|---|
| 455 | + |
|---|
| 456 | +failed_set_error: |
|---|
| 457 | + op->error = error; |
|---|
| 428 | 458 | failed: |
|---|
| 429 | | - fc->flags |= AFS_FS_CURSOR_STOP; |
|---|
| 430 | | - afs_end_cursor(&fc->ac); |
|---|
| 431 | | - _leave(" = f [failed %d]", fc->ac.error); |
|---|
| 459 | + op->flags |= AFS_OPERATION_STOP; |
|---|
| 460 | + afs_end_cursor(&op->ac); |
|---|
| 461 | + _leave(" = f [failed %d]", op->error); |
|---|
| 432 | 462 | return false; |
|---|
| 433 | 463 | } |
|---|
| 434 | 464 | |
|---|
| 435 | 465 | /* |
|---|
| 436 | | - * Select the same fileserver we used for a vnode before and only that |
|---|
| 437 | | - * fileserver. We use this when we have a lock on that file, which is backed |
|---|
| 438 | | - * only by the fileserver we obtained it from. |
|---|
| 466 | + * Dump cursor state in the case of the error being EDESTADDRREQ. |
|---|
| 439 | 467 | */ |
|---|
| 440 | | -bool afs_select_current_fileserver(struct afs_fs_cursor *fc) |
|---|
| 468 | +void afs_dump_edestaddrreq(const struct afs_operation *op) |
|---|
| 441 | 469 | { |
|---|
| 442 | | - struct afs_vnode *vnode = fc->vnode; |
|---|
| 443 | | - struct afs_cb_interest *cbi = vnode->cb_interest; |
|---|
| 444 | | - struct afs_addr_list *alist; |
|---|
| 470 | + static int count; |
|---|
| 471 | + int i; |
|---|
| 445 | 472 | |
|---|
| 446 | | - _enter(""); |
|---|
| 473 | + if (!IS_ENABLED(CONFIG_AFS_DEBUG_CURSOR) || count > 3) |
|---|
| 474 | + return; |
|---|
| 475 | + count++; |
|---|
| 447 | 476 | |
|---|
| 448 | | - switch (fc->ac.error) { |
|---|
| 449 | | - case SHRT_MAX: |
|---|
| 450 | | - if (!cbi) { |
|---|
| 451 | | - fc->ac.error = -ESTALE; |
|---|
| 452 | | - fc->flags |= AFS_FS_CURSOR_STOP; |
|---|
| 453 | | - return false; |
|---|
| 477 | + rcu_read_lock(); |
|---|
| 478 | + |
|---|
| 479 | + pr_notice("EDESTADDR occurred\n"); |
|---|
| 480 | + pr_notice("FC: cbb=%x cbb2=%x fl=%x err=%hd\n", |
|---|
| 481 | + op->file[0].cb_break_before, |
|---|
| 482 | + op->file[1].cb_break_before, op->flags, op->error); |
|---|
| 483 | + pr_notice("FC: ut=%lx ix=%d ni=%u\n", |
|---|
| 484 | + op->untried, op->index, op->nr_iterations); |
|---|
| 485 | + |
|---|
| 486 | + if (op->server_list) { |
|---|
| 487 | + const struct afs_server_list *sl = op->server_list; |
|---|
| 488 | + pr_notice("FC: SL nr=%u pr=%u vnov=%hx\n", |
|---|
| 489 | + sl->nr_servers, sl->preferred, sl->vnovol_mask); |
|---|
| 490 | + for (i = 0; i < sl->nr_servers; i++) { |
|---|
| 491 | + const struct afs_server *s = sl->servers[i].server; |
|---|
| 492 | + pr_notice("FC: server fl=%lx av=%u %pU\n", |
|---|
| 493 | + s->flags, s->addr_version, &s->uuid); |
|---|
| 494 | + if (s->addresses) { |
|---|
| 495 | + const struct afs_addr_list *a = |
|---|
| 496 | + rcu_dereference(s->addresses); |
|---|
| 497 | + pr_notice("FC: - av=%u nr=%u/%u/%u pr=%u\n", |
|---|
| 498 | + a->version, |
|---|
| 499 | + a->nr_ipv4, a->nr_addrs, a->max_addrs, |
|---|
| 500 | + a->preferred); |
|---|
| 501 | + pr_notice("FC: - R=%lx F=%lx\n", |
|---|
| 502 | + a->responded, a->failed); |
|---|
| 503 | + if (a == op->ac.alist) |
|---|
| 504 | + pr_notice("FC: - current\n"); |
|---|
| 505 | + } |
|---|
| 454 | 506 | } |
|---|
| 455 | | - |
|---|
| 456 | | - fc->cbi = afs_get_cb_interest(vnode->cb_interest); |
|---|
| 457 | | - |
|---|
| 458 | | - read_lock(&cbi->server->fs_lock); |
|---|
| 459 | | - alist = rcu_dereference_protected(cbi->server->addresses, |
|---|
| 460 | | - lockdep_is_held(&cbi->server->fs_lock)); |
|---|
| 461 | | - afs_get_addrlist(alist); |
|---|
| 462 | | - read_unlock(&cbi->server->fs_lock); |
|---|
| 463 | | - if (!alist) { |
|---|
| 464 | | - fc->ac.error = -ESTALE; |
|---|
| 465 | | - fc->flags |= AFS_FS_CURSOR_STOP; |
|---|
| 466 | | - return false; |
|---|
| 467 | | - } |
|---|
| 468 | | - |
|---|
| 469 | | - memset(&fc->ac, 0, sizeof(fc->ac)); |
|---|
| 470 | | - fc->ac.alist = alist; |
|---|
| 471 | | - fc->ac.start = READ_ONCE(alist->index); |
|---|
| 472 | | - fc->ac.index = fc->ac.start; |
|---|
| 473 | | - goto iterate_address; |
|---|
| 474 | | - |
|---|
| 475 | | - case 0: |
|---|
| 476 | | - default: |
|---|
| 477 | | - /* Success or local failure. Stop. */ |
|---|
| 478 | | - fc->flags |= AFS_FS_CURSOR_STOP; |
|---|
| 479 | | - _leave(" = f [okay/local %d]", fc->ac.error); |
|---|
| 480 | | - return false; |
|---|
| 481 | | - |
|---|
| 482 | | - case -ECONNABORTED: |
|---|
| 483 | | - fc->flags |= AFS_FS_CURSOR_STOP; |
|---|
| 484 | | - _leave(" = f [abort]"); |
|---|
| 485 | | - return false; |
|---|
| 486 | | - |
|---|
| 487 | | - case -ENETUNREACH: |
|---|
| 488 | | - case -EHOSTUNREACH: |
|---|
| 489 | | - case -ECONNREFUSED: |
|---|
| 490 | | - case -ETIMEDOUT: |
|---|
| 491 | | - case -ETIME: |
|---|
| 492 | | - _debug("no conn"); |
|---|
| 493 | | - goto iterate_address; |
|---|
| 494 | 507 | } |
|---|
| 495 | 508 | |
|---|
| 496 | | -iterate_address: |
|---|
| 497 | | - /* Iterate over the current server's address list to try and find an |
|---|
| 498 | | - * address on which it will respond to us. |
|---|
| 499 | | - */ |
|---|
| 500 | | - if (afs_iterate_addresses(&fc->ac)) { |
|---|
| 501 | | - _leave(" = t"); |
|---|
| 502 | | - return true; |
|---|
| 503 | | - } |
|---|
| 504 | | - |
|---|
| 505 | | - afs_end_cursor(&fc->ac); |
|---|
| 506 | | - return false; |
|---|
| 507 | | -} |
|---|
| 508 | | - |
|---|
| 509 | | -/* |
|---|
| 510 | | - * Tidy up a filesystem cursor and unlock the vnode. |
|---|
| 511 | | - */ |
|---|
| 512 | | -int afs_end_vnode_operation(struct afs_fs_cursor *fc) |
|---|
| 513 | | -{ |
|---|
| 514 | | - struct afs_net *net = afs_v2net(fc->vnode); |
|---|
| 515 | | - int ret; |
|---|
| 516 | | - |
|---|
| 517 | | - mutex_unlock(&fc->vnode->io_lock); |
|---|
| 518 | | - |
|---|
| 519 | | - afs_end_cursor(&fc->ac); |
|---|
| 520 | | - afs_put_cb_interest(net, fc->cbi); |
|---|
| 521 | | - afs_put_serverlist(net, fc->server_list); |
|---|
| 522 | | - |
|---|
| 523 | | - ret = fc->ac.error; |
|---|
| 524 | | - if (ret == -ECONNABORTED) |
|---|
| 525 | | - afs_abort_to_error(fc->ac.abort_code); |
|---|
| 526 | | - |
|---|
| 527 | | - return fc->ac.error; |
|---|
| 509 | + pr_notice("AC: t=%lx ax=%u ac=%d er=%d r=%u ni=%u\n", |
|---|
| 510 | + op->ac.tried, op->ac.index, op->ac.abort_code, op->ac.error, |
|---|
| 511 | + op->ac.responded, op->ac.nr_iterations); |
|---|
| 512 | + rcu_read_unlock(); |
|---|
| 528 | 513 | } |
|---|