// SPDX-License-Identifier: GPL-2.0-or-later /* Handle fileserver selection and rotation. * * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
if (op->server_states) { for (i = 0; i < op->server_list->nr_servers; i++)
afs_put_endpoint_state(op->server_states[i].endpoint_state,
afs_estate_trace_put_server_state);
kfree(op->server_states);
}
}
/* * Begin iteration through a server list, starting with the vnode's last used * server if possible, or the last recorded good server if not.
*/ staticbool afs_start_fs_iteration(struct afs_operation *op, struct afs_vnode *vnode)
{ struct afs_server *server; void *cb_server; int i;
cb_server = vnode->cb_server; if (cb_server) { /* See if the vnode's preferred record is still available */ for (i = 0; i < op->server_list->nr_servers; i++) {
server = op->server_list->servers[i].server; if (server == cb_server) {
op->server_index = i; goto found_interest;
}
}
/* If we have a lock outstanding on a server that's no longer * serving this vnode, then we can't switch to another server * and have to return an error.
*/ if (op->flags & AFS_OPERATION_CUR_ONLY) {
afs_op_set_error(op, -ESTALE);
trace_afs_rotate(op, afs_rotate_trace_stale_lock, 0); returnfalse;
}
/* Note that the callback promise is effectively broken */
write_seqlock(&vnode->cb_lock);
ASSERTCMP(cb_server, ==, vnode->cb_server);
vnode->cb_server = NULL; if (afs_clear_cb_promise(vnode, afs_cb_promise_clear_rotate_server))
vnode->cb_break++;
write_sequnlock(&vnode->cb_lock);
}
switch (abort_code) { case VOFFLINE: m = "offline"; break; case VRESTARTING: m = "restarting"; break; case VSALVAGING: m = "being salvaged"; break; default: m = "busy"; break;
}
pr_notice("kAFS: Volume %llu '%s' on server %pU is %s\n",
op->volume->vid, op->volume->name, &op->server->uuid, m);
}
/* * Sleep and retry the operation to the same fileserver.
*/ staticbool afs_sleep_and_retry(struct afs_operation *op)
{
trace_afs_rotate(op, afs_rotate_trace_busy_sleep, 0); if (!(op->flags & AFS_OPERATION_UNINTR)) {
msleep_interruptible(1000); if (signal_pending(current)) {
afs_op_set_error(op, -ERESTARTSYS); returnfalse;
}
} else {
msleep(1000);
}
returntrue;
}
/* * Select the fileserver to use. May be called multiple times to rotate * through the fileservers.
*/ bool afs_select_fileserver(struct afs_operation *op)
{ struct afs_addr_list *alist; struct afs_server *server; struct afs_vnode *vnode = op->file[0].vnode; unsignedlong set, failed;
s32 abort_code = op->call_abort_code; int best_prio = 0; int error = op->call_error, addr_index, i, j;
/* Evaluate the result of the previous operation, if there was one. */ switch (op->call_error) { case 0:
clear_bit(AFS_SE_VOLUME_OFFLINE,
&op->server_list->servers[op->server_index].flags);
clear_bit(AFS_SE_VOLUME_BUSY,
&op->server_list->servers[op->server_index].flags);
op->cumul_error.responded = true;
/* We succeeded, but we may need to redo the op from another * server if we're looking at a set of RO volumes where some of * the servers have not yet been brought up to date lest we * regress the data. We only switch to the new version once * >=50% of the servers are updated.
*/
error = afs_update_volume_state(op); if (error != 0) { if (error == 1) {
afs_sleep_and_retry(op); goto restart_from_beginning;
}
afs_op_set_error(op, error); goto failed;
}
fallthrough; default: /* Success or local failure. Stop. */
afs_op_set_error(op, error);
op->flags |= AFS_OPERATION_STOP;
trace_afs_rotate(op, afs_rotate_trace_stop, error);
_leave(" = f [okay/local %d]", error); returnfalse;
case -ECONNABORTED: /* The far side rejected the operation on some grounds. This * might involve the server being busy or the volume having been moved. * * Note that various V* errors should not be sent to a cache manager * by a fileserver as they should be translated to more modern UAE* * errors instead. IBM AFS and OpenAFS fileservers, however, do leak * these abort codes.
*/
trace_afs_rotate(op, afs_rotate_trace_aborted, abort_code);
op->cumul_error.responded = true; switch (abort_code) { case VNOVOL: /* This fileserver doesn't know about the volume. * - May indicate that the VL is wrong - retry once and compare * the results. * - May indicate that the fileserver couldn't attach to the vol. * - The volume might have been temporarily removed so that it can * be replaced by a volume restore. "vos" might have ended one * transaction and has yet to create the next. * - The volume might not be blessed or might not be in-service * (administrative action).
*/ if (op->flags & AFS_OPERATION_VNOVOL) {
afs_op_accumulate_error(op, -EREMOTEIO, abort_code); goto next_server;
}
if (test_bit(AFS_VOLUME_DELETED, &op->volume->flags)) {
afs_op_set_error(op, -ENOMEDIUM); goto failed;
}
/* If the server list didn't change, then assume that * it's the fileserver having trouble.
*/ if (rcu_access_pointer(op->volume->servers) == op->server_list) {
afs_op_accumulate_error(op, -EREMOTEIO, abort_code); goto next_server;
}
/* Try again */
op->flags |= AFS_OPERATION_VNOVOL;
_leave(" = t [vnovol]"); returntrue;
case VVOLEXISTS: case VONLINE: /* These should not be returned from the fileserver. */
pr_warn("Fileserver returned unexpected abort %d\n",
abort_code);
afs_op_accumulate_error(op, -EREMOTEIO, abort_code); goto next_server;
case VNOSERVICE: /* Prior to AFS 3.2 VNOSERVICE was returned from the fileserver * if the volume was neither in-service nor administratively * blessed. All usage was replaced by VNOVOL because AFS 3.1 and * earlier cache managers did not handle VNOSERVICE and assumed * it was the client OSes errno 105. * * Starting with OpenAFS 1.4.8 VNOSERVICE was repurposed as the * fileserver idle dead time error which was sent in place of * RX_CALL_TIMEOUT (-3). The error was intended to be sent if the * fileserver took too long to send a reply to the client. * RX_CALL_TIMEOUT would have caused the cache manager to mark the * server down whereas VNOSERVICE since AFS 3.2 would cause cache * manager to temporarily (up to 15 minutes) mark the volume * instance as unusable. * * The idle dead logic resulted in cache inconsistency since a * state changing call that the cache manager assumed was dead * could still be processed to completion by the fileserver. This * logic was removed in OpenAFS 1.8.0 and VNOSERVICE is no longer * returned. However, many 1.4.8 through 1.6.24 fileservers are * still in existence. * * AuriStorFS fileservers have never returned VNOSERVICE. * * VNOSERVICE should be treated as an alias for RX_CALL_TIMEOUT.
*/ case RX_CALL_TIMEOUT:
afs_op_accumulate_error(op, -ETIMEDOUT, abort_code); goto next_server;
case VSALVAGING: /* This error should not be leaked to cache managers * but is from OpenAFS demand attach fileservers. * It should be treated as an alias for VOFFLINE.
*/ case VSALVAGE: /* VSALVAGE should be treated as a synonym of VOFFLINE */ case VOFFLINE: /* The volume is in use by the volserver or another volume utility * for an operation that might alter the contents. The volume is * expected to come back but it might take a long time (could be * days).
*/ if (!test_and_set_bit(AFS_SE_VOLUME_OFFLINE,
&op->server_list->servers[op->server_index].flags)) {
afs_busy(op, abort_code);
clear_bit(AFS_SE_VOLUME_BUSY,
&op->server_list->servers[op->server_index].flags);
} if (op->flags & AFS_OPERATION_NO_VSLEEP) {
afs_op_set_error(op, -EADV); goto failed;
} goto busy;
case VRESTARTING: /* The fileserver is either shutting down or starting up. */ case VBUSY: /* The volume is in use by the volserver or another volume * utility for an operation that is not expected to alter the * contents of the volume. VBUSY does not need to be returned * for a ROVOL or BACKVOL bound to an ITBusy volserver * transaction. The fileserver is permitted to continue serving * content from ROVOLs and BACKVOLs during an ITBusy transaction * because the content will not change. However, many fileserver * releases do return VBUSY for ROVOL and BACKVOL instances under * many circumstances. * * Retry after going round all the servers unless we have a file * lock we need to maintain.
*/ if (op->flags & AFS_OPERATION_NO_VSLEEP) {
afs_op_set_error(op, -EBUSY); goto failed;
} if (!test_and_set_bit(AFS_SE_VOLUME_BUSY,
&op->server_list->servers[op->server_index].flags)) {
afs_busy(op, abort_code);
clear_bit(AFS_SE_VOLUME_OFFLINE,
&op->server_list->servers[op->server_index].flags);
}
busy: if (op->flags & AFS_OPERATION_CUR_ONLY) { if (!afs_sleep_and_retry(op)) goto failed;
/* Retry with same server & address */
_leave(" = t [vbusy]"); returntrue;
}
case VMOVED: /* The volume migrated to another server. We consider * consider all locks and callbacks broken and request * an update from the VLDB. * * We also limit the number of VMOVED hops we will * honour, just in case someone sets up a loop.
*/ if (op->flags & AFS_OPERATION_VMOVED) {
afs_op_set_error(op, -EREMOTEIO); goto failed;
}
op->flags |= AFS_OPERATION_VMOVED;
/* If the server list didn't change, then the VLDB is * out of sync with the fileservers. This is hopefully * a temporary condition, however, so we don't want to * permanently block access to the file. * * TODO: Try other fileservers if we can. * * TODO: Retry a few times with sleeps.
*/ if (rcu_access_pointer(op->volume->servers) == op->server_list) {
afs_op_accumulate_error(op, -ENOMEDIUM, abort_code); goto failed;
}
goto restart_from_beginning;
case UAEIO: case VIO:
afs_op_accumulate_error(op, -EREMOTEIO, abort_code); if (op->volume->type != AFSVL_RWVOL) goto next_server; goto failed;
case VDISKFULL: case UAENOSPC: /* The partition is full. Only applies to RWVOLs. * Translate locally and return ENOSPC. * No replicas to failover to.
*/
afs_op_set_error(op, -ENOSPC); goto failed_but_online;
case VOVERQUOTA: case UAEDQUOT: /* Volume is full. Only applies to RWVOLs. * Translate locally and return EDQUOT. * No replicas to failover to.
*/
afs_op_set_error(op, -EDQUOT); goto failed_but_online;
case -ETIMEDOUT: case -ETIME: if (afs_op_error(op) != -EDESTADDRREQ) goto iterate_address;
fallthrough; case -ERFKILL: case -EADDRNOTAVAIL: case -ENETUNREACH: case -EHOSTUNREACH: case -EHOSTDOWN: case -ECONNREFUSED:
_debug("no conn");
afs_op_accumulate_error(op, error, 0); goto iterate_address;
restart_from_beginning:
trace_afs_rotate(op, afs_rotate_trace_restart, 0);
_debug("restart");
op->estate = NULL;
op->server = NULL;
afs_clear_server_states(op);
op->server_states = NULL;
afs_put_serverlist(op->net, op->server_list);
op->server_list = NULL;
start:
_debug("start");
ASSERTCMP(op->estate, ==, NULL); /* See if we need to do an update of the volume record. Note that the * volume may have moved or even have been deleted.
*/
error = afs_check_volume_status(op->volume, op);
trace_afs_rotate(op, afs_rotate_trace_check_vol_status, error); if (error < 0) {
afs_op_set_error(op, error); goto failed;
}
if (!afs_start_fs_iteration(op, vnode)) goto failed;
error = afs_wait_for_fs_probes(op, op->server_states,
!(op->flags & AFS_OPERATION_UNINTR)); switch (error) { case 0: /* No untried responsive servers and no outstanding probes */
trace_afs_rotate(op, afs_rotate_trace_probe_none, 0); goto no_more_servers; case 1: /* Got a response */
trace_afs_rotate(op, afs_rotate_trace_probe_response, 0); break; case 2: /* Probe data superseded */
trace_afs_rotate(op, afs_rotate_trace_probe_superseded, 0); goto restart_from_beginning; default:
trace_afs_rotate(op, afs_rotate_trace_probe_error, error);
afs_op_set_error(op, error); goto failed;
}
/* Pick the untried server with the highest priority untried endpoint. * If we have outstanding callbacks, we stick with the server we're * already using if we can.
*/ if (op->server) {
_debug("server %u", op->server_index); if (test_bit(op->server_index, &op->untried_servers)) goto selected_server;
op->server = NULL;
_debug("no server");
}
/* We're starting on a different fileserver from the list. We need to * check it, create a callback intercept, find its address list and * probe its capabilities before we use it.
*/
ASSERTCMP(op->estate, ==, NULL);
server = op->server_list->servers[op->server_index].server;
if (!afs_check_server_record(op, server, op->key)) goto failed;
iterate_address: /* Iterate over the current server's address list to try and find an * address on which it will respond to us.
*/
op->estate = op->server_states[op->server_index].endpoint_state;
set = READ_ONCE(op->estate->responsive_set);
failed = READ_ONCE(op->estate->failed_set);
_debug("iterate ES=%x rs=%lx fs=%lx", op->estate->probe_seq, set, failed);
set &= ~(failed | op->addr_tried);
trace_afs_rotate(op, afs_rotate_trace_iterate_addr, set); if (!set) goto wait_for_more_probe_results;
alist = op->estate->addresses;
best_prio = -1;
addr_index = 0; for (i = 0; i < alist->nr_addrs; i++) { if (!(set & (1 << i))) continue; if (alist->addrs[i].prio > best_prio) {
addr_index = i;
best_prio = alist->addrs[i].prio;
}
}
wait_for_more_probe_results:
error = afs_wait_for_one_fs_probe(op->server, op->estate, op->addr_tried,
!(op->flags & AFS_OPERATION_UNINTR)); if (error == 1) goto iterate_address; if (!error) goto restart_from_beginning;
/* We've now had a failure to respond on all of a server's addresses - * immediately probe them again and consider retrying the server.
*/
trace_afs_rotate(op, afs_rotate_trace_probe_fileserver, 0);
afs_probe_fileserver(op->net, op->server); if (op->flags & AFS_OPERATION_RETRY_SERVER) {
error = afs_wait_for_one_fs_probe(op->server, op->estate, op->addr_tried,
!(op->flags & AFS_OPERATION_UNINTR)); switch (error) { case 1:
op->flags &= ~AFS_OPERATION_RETRY_SERVER;
trace_afs_rotate(op, afs_rotate_trace_retry_server, 1); goto retry_server; case 0:
trace_afs_rotate(op, afs_rotate_trace_retry_server, 0); goto restart_from_beginning; case -ERESTARTSYS:
afs_op_set_error(op, error); goto failed; case -ETIME: case -EDESTADDRREQ: goto next_server;
}
}
no_more_servers: /* That's all the servers poked to no good effect. Try again if some * of them were busy.
*/
trace_afs_rotate(op, afs_rotate_trace_no_more_servers, 0); if (op->flags & AFS_OPERATION_VBUSY) {
afs_sleep_and_retry(op);
op->flags &= ~AFS_OPERATION_VBUSY; goto restart_from_beginning;
}
rcu_read_lock(); for (i = 0; i < op->server_list->nr_servers; i++) { struct afs_endpoint_state *estate;
/* * Dump cursor state in the case of the error being EDESTADDRREQ.
*/ void afs_dump_edestaddrreq(conststruct afs_operation *op)
{ staticint count; int i;
if (!IS_ENABLED(CONFIG_AFS_DEBUG_CURSOR) || count > 3) return;
count++;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.