diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c index b76abf500713..a1f3c995e328 100644 --- a/fs/afs/addr_list.c +++ b/fs/afs/addr_list.c @@ -386,26 +386,24 @@ bool afs_iterate_addresses(struct afs_addr_cursor *ac) selected: ac->index = index; set_bit(index, &ac->tried); - ac->responded = false; + ac->call_responded = false; return true; } /* * Release an address list cursor. */ -int afs_end_cursor(struct afs_addr_cursor *ac) +void afs_end_cursor(struct afs_addr_cursor *ac) { struct afs_addr_list *alist; alist = ac->alist; if (alist) { - if (ac->responded && + if (ac->call_responded && ac->index != alist->preferred && test_bit(ac->alist->preferred, &ac->tried)) WRITE_ONCE(alist->preferred, ac->index); afs_put_addrlist(alist); ac->alist = NULL; } - - return ac->error; } diff --git a/fs/afs/dir.c b/fs/afs/dir.c index b40f7ae850a8..ecb889a269fb 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -693,8 +693,9 @@ static void afs_do_lookup_success(struct afs_operation *op) vp = &op->file[0]; abort_code = vp->scb.status.abort_code; if (abort_code != 0) { - op->ac.abort_code = abort_code; - op->error = afs_abort_to_error(abort_code); + op->call_abort_code = abort_code; + afs_op_set_error(op, afs_abort_to_error(abort_code)); + op->cumul_error.abort_code = abort_code; } break; @@ -846,13 +847,14 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, _debug("nr_files %u", op->nr_files); /* Need space for examining all the selected files */ - op->error = -ENOMEM; if (op->nr_files > 2) { op->more_files = kvcalloc(op->nr_files - 2, sizeof(struct afs_vnode_param), GFP_KERNEL); - if (!op->more_files) + if (!op->more_files) { + afs_op_nomem(op); goto out_op; + } for (i = 2; i < op->nr_files; i++) { vp = &op->more_files[i - 2]; @@ -1255,7 +1257,7 @@ void afs_check_for_remote_deletion(struct afs_operation *op) { struct afs_vnode *vnode = op->file[0].vnode; - switch (op->ac.abort_code) { + switch (afs_op_abort_code(op)) { case VNOVNODE: set_bit(AFS_VNODE_DELETED, &vnode->flags); afs_break_callback(vnode, afs_cb_break_for_deleted); @@ -1280,7 +1282,7 @@ static void afs_vnode_new_inode(struct afs_operation *op) /* ENOMEM or EINTR at a really inconvenient time - just abandon * the new directory on the server. */ - op->error = PTR_ERR(inode); + afs_op_accumulate_error(op, PTR_ERR(inode), 0); return; } diff --git a/fs/afs/dir_silly.c b/fs/afs/dir_silly.c index bb5807e87fa4..a1e581946b93 100644 --- a/fs/afs/dir_silly.c +++ b/fs/afs/dir_silly.c @@ -218,7 +218,7 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode /* If there was a conflict with a third party, check the status of the * unlinked vnode. */ - if (op->error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) { + if (op->cumul_error.error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) { op->file[1].update_ctime = false; op->fetch_status.which = 1; op->ops = &afs_fetch_status_operation; diff --git a/fs/afs/file.c b/fs/afs/file.c index 0c81c39c32f5..8f9b42427569 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -245,10 +245,7 @@ static void afs_fetch_data_notify(struct afs_operation *op) struct netfs_io_subrequest *subreq = req->subreq; int error = afs_op_error(op); - if (error == -ECONNABORTED) - error = afs_abort_to_error(op->ac.abort_code); req->error = error; - if (subreq) { __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags); netfs_subreq_terminated(subreq, error ?: req->actual_len, false); diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c index 1c22d6e77846..cebe4fad8192 100644 --- a/fs/afs/fs_operation.c +++ b/fs/afs/fs_operation.c @@ -169,9 +169,6 @@ static void afs_end_vnode_operation(struct afs_operation *op) } afs_drop_io_locks(op); - - if (op->error == -ECONNABORTED) - op->error = afs_abort_to_error(op->ac.abort_code); } /* @@ -182,6 +179,8 @@ void afs_wait_for_operation(struct afs_operation *op) _enter(""); while (afs_select_fileserver(op)) { + op->call_error = 0; + op->call_abort_code = 0; op->cb_s_break = op->server->cb_s_break; if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) && op->ops->issue_yfs_rpc) @@ -189,28 +188,29 @@ void afs_wait_for_operation(struct afs_operation *op) else if (op->ops->issue_afs_rpc) op->ops->issue_afs_rpc(op); else - op->ac.error = -ENOTSUPP; + op->call_error = -ENOTSUPP; if (op->call) { afs_wait_for_call_to_complete(op->call, &op->ac); - op->error = op->ac.error; + op->call_abort_code = op->call->abort_code; + op->call_error = op->call->error; + op->call_responded = op->call->responded; + op->ac.call_responded = true; + WRITE_ONCE(op->ac.alist->addrs[op->ac.index].last_error, + op->call_error); afs_put_call(op->call); } } - switch (op->error) { - case 0: + if (!afs_op_error(op)) { _debug("success"); op->ops->success(op); - break; - case -ECONNABORTED: + } else if (op->cumul_error.aborted) { if (op->ops->aborted) op->ops->aborted(op); - fallthrough; - default: + } else { if (op->ops->failed) op->ops->failed(op); - break; } afs_end_vnode_operation(op); diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 020073387111..2a56dea22519 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -1629,6 +1629,7 @@ int afs_fs_give_up_all_callbacks(struct afs_net *net, call->server = afs_use_server(server, afs_server_trace_give_up_cb); afs_make_call(ac, call, GFP_NOFS); afs_wait_for_call_to_complete(call, ac); + ret = call->error; afs_put_call(call); return ret; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 1bc69a115223..d67c75d4d2bd 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -75,6 +75,7 @@ enum afs_call_state { struct afs_address { struct rxrpc_peer *peer; u16 service_id; + short last_error; /* Last error from this address */ }; /* @@ -121,7 +122,6 @@ struct afs_call { }; void *buffer; /* reply receive buffer */ union { - long ret0; /* Value to reply with instead of 0 */ struct afs_addr_list *ret_alist; struct afs_vldb_entry *ret_vldb; char *ret_str; @@ -145,6 +145,7 @@ struct afs_call { bool upgrade; /* T to request service upgrade */ bool intr; /* T if interruptible */ bool unmarshalling_error; /* T if an unmarshalling error occurred */ + bool responded; /* Got a response from the call (may be abort) */ u16 service_id; /* Actual service ID (after upgrade) */ unsigned int debug_id; /* Trace ID */ u32 operation_ID; /* operation ID for an incoming call */ @@ -719,8 +720,10 @@ struct afs_permits { * Error prioritisation and accumulation. */ struct afs_error { - short error; /* Accumulated error */ + s32 abort_code; /* Cumulative abort code */ + short error; /* Cumulative error */ bool responded; /* T if server responded */ + bool aborted; /* T if ->error is from an abort */ }; /* @@ -730,10 +733,8 @@ struct afs_addr_cursor { struct afs_addr_list *alist; /* Current address list (pins ref) */ unsigned long tried; /* Tried addresses */ signed char index; /* Current address */ - bool responded; /* T if the current address responded */ unsigned short nr_iterations; /* Number of address iterations */ - short error; - u32 abort_code; + bool call_responded; }; /* @@ -746,13 +747,16 @@ struct afs_vl_cursor { struct afs_vlserver *server; /* Server on which this resides */ struct key *key; /* Key for the server */ unsigned long untried; /* Bitmask of untried servers */ + struct afs_error cumul_error; /* Cumulative error */ + s32 call_abort_code; short index; /* Current server */ - short error; + short call_error; /* Error from single call */ unsigned short flags; #define AFS_VL_CURSOR_STOP 0x0001 /* Set to cease iteration */ #define AFS_VL_CURSOR_RETRY 0x0002 /* Set to do a retry */ #define AFS_VL_CURSOR_RETRIED 0x0004 /* Set if started a retry */ - unsigned short nr_iterations; /* Number of server iterations */ + short nr_iterations; /* Number of server iterations */ + bool call_responded; /* T if the current address responded */ }; /* @@ -803,8 +807,10 @@ struct afs_operation { struct dentry *dentry_2; /* Second dentry to be altered */ struct timespec64 mtime; /* Modification time to record */ struct timespec64 ctime; /* Change time to set */ + struct afs_error cumul_error; /* Cumulative error */ short nr_files; /* Number of entries in file[], more_files */ - short error; + short call_error; /* Error from single call */ + s32 call_abort_code; /* Abort code from single call */ unsigned int debug_id; unsigned int cb_v_break; /* Volume break counter before op */ @@ -860,6 +866,8 @@ struct afs_operation { unsigned long untried; /* Bitmask of untried servers */ short index; /* Current server */ short nr_iterations; /* Number of server iterations */ + bool call_responded; /* T if the current address responded */ + unsigned int flags; #define AFS_OPERATION_STOP 0x0001 /* Set to cease iteration */ @@ -976,7 +984,7 @@ bool afs_addr_list_same(const struct afs_addr_list *a, const struct afs_addr_list *b); extern struct afs_vlserver_list *afs_dns_query(struct afs_cell *, time64_t *); extern bool afs_iterate_addresses(struct afs_addr_cursor *); -extern int afs_end_cursor(struct afs_addr_cursor *); +extern void afs_end_cursor(struct afs_addr_cursor *ac); extern int afs_merge_fs_addr4(struct afs_net *net, struct afs_addr_list *addr, __be32 xdr, u16 port); @@ -1235,17 +1243,27 @@ extern void afs_prioritise_error(struct afs_error *, int, u32); static inline void afs_op_nomem(struct afs_operation *op) { - op->error = -ENOMEM; + op->cumul_error.error = -ENOMEM; } static inline int afs_op_error(const struct afs_operation *op) { - return op->error; + return op->cumul_error.error; +} + +static inline s32 afs_op_abort_code(const struct afs_operation *op) +{ + return op->cumul_error.abort_code; } static inline int afs_op_set_error(struct afs_operation *op, int error) { - return op->error = error; + return op->cumul_error.error = error; +} + +static inline void afs_op_accumulate_error(struct afs_operation *op, int error, s32 abort_code) +{ + afs_prioritise_error(&op->cumul_error, error, abort_code); } /* @@ -1619,7 +1637,7 @@ static inline void afs_update_dentry_version(struct afs_operation *op, struct afs_vnode_param *dir_vp, struct dentry *dentry) { - if (!op->error) + if (!op->cumul_error.error) dentry->d_fsdata = (void *)(unsigned long)dir_vp->scb.status.data_version; } diff --git a/fs/afs/misc.c b/fs/afs/misc.c index 805328ca5428..b8180bf2281f 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c @@ -116,6 +116,8 @@ void afs_prioritise_error(struct afs_error *e, int error, u32 abort_code) { switch (error) { case 0: + e->aborted = false; + e->error = 0; return; default: if (e->error == -ETIMEDOUT || @@ -161,12 +163,16 @@ void afs_prioritise_error(struct afs_error *e, int error, u32 abort_code) if (e->responded) return; e->error = error; + e->aborted = false; return; case -ECONNABORTED: - error = afs_abort_to_error(abort_code); - fallthrough; + e->error = afs_abort_to_error(abort_code); + e->aborted = true; + e->responded = true; + return; case -ENETRESET: /* Responded, but we seem to have changed address */ + e->aborted = false; e->responded = true; e->error = error; return; diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index 965ee8f0804a..a778d53681fe 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -112,9 +112,9 @@ 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; - struct afs_error e; unsigned int rtt; - int error = op->ac.error, i; + s32 abort_code = op->call_abort_code; + int error = op->call_error, i; op->nr_iterations++; @@ -122,7 +122,7 @@ bool afs_select_fileserver(struct afs_operation *op) op->debug_id, op->nr_iterations, op->volume->vid, op->untried, op->index, op->ac.tried, op->ac.index, - error, op->ac.abort_code); + error, abort_code); if (op->flags & AFS_OPERATION_STOP) { _leave(" = f [stopped]"); @@ -133,8 +133,10 @@ bool afs_select_fileserver(struct afs_operation *op) goto start; /* Evaluate the result of the previous operation, if there was one. */ - switch (error) { + switch (op->call_error) { case 0: + op->cumul_error.responded = true; + fallthrough; default: /* Success or local failure. Stop. */ afs_op_set_error(op, error); @@ -151,7 +153,8 @@ bool afs_select_fileserver(struct afs_operation *op) * errors instead. IBM AFS and OpenAFS fileservers, however, do leak * these abort codes. */ - switch (op->ac.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 @@ -164,7 +167,7 @@ bool afs_select_fileserver(struct afs_operation *op) * (administrative action). */ if (op->flags & AFS_OPERATION_VNOVOL) { - op->error = -EREMOTEIO; + afs_op_accumulate_error(op, -EREMOTEIO, abort_code); goto next_server; } @@ -188,7 +191,7 @@ bool afs_select_fileserver(struct afs_operation *op) * it's the fileserver having trouble. */ if (rcu_access_pointer(op->volume->servers) == op->server_list) { - op->error = -EREMOTEIO; + afs_op_accumulate_error(op, -EREMOTEIO, abort_code); goto next_server; } @@ -201,8 +204,8 @@ bool afs_select_fileserver(struct afs_operation *op) case VONLINE: /* These should not be returned from the fileserver. */ pr_warn("Fileserver returned unexpected abort %d\n", - op->ac.abort_code); - op->error = -EREMOTEIO; + abort_code); + afs_op_accumulate_error(op, -EREMOTEIO, abort_code); goto next_server; case VNOSERVICE: @@ -233,7 +236,7 @@ bool afs_select_fileserver(struct afs_operation *op) * VNOSERVICE should be treated as an alias for RX_CALL_TIMEOUT. */ case RX_CALL_TIMEOUT: - op->error = -ETIMEDOUT; + afs_op_accumulate_error(op, -ETIMEDOUT, abort_code); goto next_server; case VSALVAGING: /* This error should not be leaked to cache managers @@ -248,7 +251,7 @@ bool afs_select_fileserver(struct afs_operation *op) * days). */ if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &op->volume->flags)) { - afs_busy(op->volume, op->ac.abort_code); + afs_busy(op->volume, abort_code); clear_bit(AFS_VOLUME_BUSY, &op->volume->flags); } if (op->flags & AFS_OPERATION_NO_VSLEEP) { @@ -281,7 +284,7 @@ bool afs_select_fileserver(struct afs_operation *op) goto failed; } if (!test_and_set_bit(AFS_VOLUME_BUSY, &op->volume->flags)) { - afs_busy(op->volume, op->ac.abort_code); + afs_busy(op->volume, abort_code); clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags); } busy: @@ -329,7 +332,7 @@ bool afs_select_fileserver(struct afs_operation *op) * TODO: Retry a few times with sleeps. */ if (rcu_access_pointer(op->volume->servers) == op->server_list) { - op->error = -ENOMEDIUM; + afs_op_accumulate_error(op, -ENOMEDIUM, abort_code); goto failed; } @@ -337,7 +340,7 @@ bool afs_select_fileserver(struct afs_operation *op) case UAEIO: case VIO: - op->error = -EREMOTEIO; + afs_op_accumulate_error(op, -EREMOTEIO, abort_code); if (op->volume->type != AFSVL_RWVOL) goto next_server; goto failed; @@ -361,7 +364,7 @@ bool afs_select_fileserver(struct afs_operation *op) goto failed_but_online; default: - op->error = afs_abort_to_error(op->ac.abort_code); + afs_op_accumulate_error(op, error, abort_code); failed_but_online: clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags); clear_bit(AFS_VOLUME_BUSY, &op->volume->flags); @@ -380,7 +383,7 @@ bool afs_select_fileserver(struct afs_operation *op) case -EHOSTDOWN: case -ECONNREFUSED: _debug("no conn"); - op->error = error; + afs_op_accumulate_error(op, error, 0); goto iterate_address; case -ENETRESET: @@ -506,6 +509,7 @@ iterate_address: op->index, op->ac.index, op->ac.alist->nr_addrs, rxrpc_kernel_remote_addr(op->ac.alist->addrs[op->ac.index].peer)); + op->call_responded = false; _leave(" = t"); return true; @@ -543,17 +547,14 @@ no_more_servers: if (op->flags & AFS_OPERATION_VBUSY) goto restart_from_beginning; - e.error = -EDESTADDRREQ; - e.responded = false; for (i = 0; i < op->server_list->nr_servers; i++) { struct afs_server *s = op->server_list->servers[i].server; - afs_prioritise_error(&e, READ_ONCE(s->probe.error), - s->probe.abort_code); + error = READ_ONCE(s->probe.error); + if (error < 0) + afs_op_accumulate_error(op, error, s->probe.abort_code); } - error = e.error; - op->error = error; failed: op->flags |= AFS_OPERATION_STOP; afs_end_cursor(&op->ac); @@ -576,11 +577,13 @@ void afs_dump_edestaddrreq(const struct afs_operation *op) rcu_read_lock(); pr_notice("EDESTADDR occurred\n"); - pr_notice("FC: cbb=%x cbb2=%x fl=%x err=%hd\n", + pr_notice("OP: cbb=%x cbb2=%x fl=%x err=%hd\n", op->file[0].cb_break_before, - op->file[1].cb_break_before, op->flags, op->error); - pr_notice("FC: ut=%lx ix=%d ni=%u\n", + op->file[1].cb_break_before, op->flags, op->cumul_error.error); + pr_notice("OP: ut=%lx ix=%d ni=%u\n", op->untried, op->index, op->nr_iterations); + pr_notice("OP: call er=%d ac=%d r=%u\n", + op->call_error, op->call_abort_code, op->call_responded); if (op->server_list) { const struct afs_server_list *sl = op->server_list; @@ -605,8 +608,7 @@ void afs_dump_edestaddrreq(const struct afs_operation *op) } } - pr_notice("AC: t=%lx ax=%u ac=%d er=%d r=%u ni=%u\n", - op->ac.tried, op->ac.index, op->ac.abort_code, op->ac.error, - op->ac.responded, op->ac.nr_iterations); + pr_notice("AC: t=%lx ax=%u ni=%u\n", + op->ac.tried, op->ac.index, op->ac.nr_iterations); rcu_read_unlock(); } diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index dad8efadbc44..0b3e2f20b0e0 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -408,8 +408,7 @@ error_do_abort: rxrpc_kernel_recv_data(call->net->socket, rxcall, &msg.msg_iter, &len, false, &call->abort_code, &call->service_id); - ac->abort_code = call->abort_code; - ac->responded = true; + call->responded = true; } call->error = ret; trace_afs_call_done(call); @@ -429,7 +428,7 @@ error_kill_call: afs_set_call_complete(call, ret, 0); } - ac->error = ret; + call->error = ret; call->state = AFS_CALL_COMPLETE; _leave(" = %d", ret); } @@ -510,6 +509,7 @@ static void afs_deliver_to_call(struct afs_call *call) ret = -EBADMSG; switch (ret) { case 0: + call->responded = true; afs_queue_call_work(call); if (state == AFS_CALL_CL_PROC_REPLY) { if (call->op) @@ -524,9 +524,11 @@ static void afs_deliver_to_call(struct afs_call *call) goto out; case -ECONNABORTED: ASSERTCMP(state, ==, AFS_CALL_COMPLETE); + call->responded = true; afs_log_error(call, call->abort_code); goto done; case -ENOTSUPP: + call->responded = true; abort_code = RXGEN_OPCODE; rxrpc_kernel_abort_call(call->net->socket, call->rxcall, abort_code, ret, @@ -573,7 +575,7 @@ call_complete: } /* - * Wait synchronously for a call to complete and clean up the call struct. + * Wait synchronously for a call to complete. */ void afs_wait_for_call_to_complete(struct afs_call *call, struct afs_addr_cursor *ac) { @@ -626,13 +628,8 @@ void afs_wait_for_call_to_complete(struct afs_call *call, struct afs_addr_cursor } } - spin_lock_bh(&call->state_lock); - ac->abort_code = call->abort_code; - ac->error = call->error; - spin_unlock_bh(&call->state_lock); - if (call->error == 0 || call->error == -ECONNABORTED) - ac->responded = true; + call->responded = true; } /* diff --git a/fs/afs/server.c b/fs/afs/server.c index 2826e6eced71..f7791ef13618 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -437,7 +437,6 @@ static void afs_give_up_callbacks(struct afs_net *net, struct afs_server *server struct afs_addr_cursor ac = { .alist = alist, .index = alist->preferred, - .error = 0, }; afs_fs_give_up_all_callbacks(net, server, &ac, NULL); diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c index 6fdf9f1bedc0..89cadd9a69e1 100644 --- a/fs/afs/vl_alias.c +++ b/fs/afs/vl_alias.c @@ -236,7 +236,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key) while (afs_select_vlserver(&vc)) { if (!test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) { - vc.ac.error = -EOPNOTSUPP; + vc.call_error = -EOPNOTSUPP; skipped = true; continue; } diff --git a/fs/afs/vl_probe.c b/fs/afs/vl_probe.c index 44bff3a2a5ac..138f5715619d 100644 --- a/fs/afs/vl_probe.c +++ b/fs/afs/vl_probe.c @@ -169,10 +169,11 @@ static bool afs_do_probe_vlserver(struct afs_net *net, call = afs_vl_get_capabilities(net, &ac, key, server, server_index); if (!IS_ERR(call)) { + afs_prioritise_error(_e, call->error, call->abort_code); afs_put_call(call); in_progress = true; } else { - afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code); + afs_prioritise_error(_e, PTR_ERR(call), 0); afs_done_one_vl_probe(server, false); } } @@ -187,12 +188,10 @@ int afs_send_vl_probes(struct afs_net *net, struct key *key, struct afs_vlserver_list *vllist) { struct afs_vlserver *server; - struct afs_error e; + struct afs_error e = {}; bool in_progress = false; int i; - e.error = 0; - e.responded = false; for (i = 0; i < vllist->nr_servers; i++) { server = vllist->servers[i].server; if (test_bit(AFS_VLSERVER_FL_PROBED, &server->flags)) diff --git a/fs/afs/vl_rotate.c b/fs/afs/vl_rotate.c index 01c890b3d18d..7ae73418697d 100644 --- a/fs/afs/vl_rotate.c +++ b/fs/afs/vl_rotate.c @@ -20,11 +20,11 @@ bool afs_begin_vlserver_operation(struct afs_vl_cursor *vc, struct afs_cell *cel memset(vc, 0, sizeof(*vc)); vc->cell = cell; vc->key = key; - vc->error = -EDESTADDRREQ; - vc->ac.error = SHRT_MAX; + vc->cumul_error.error = -EDESTADDRREQ; + vc->nr_iterations = -1; if (signal_pending(current)) { - vc->error = -EINTR; + vc->cumul_error.error = -EINTR; vc->flags |= AFS_VL_CURSOR_STOP; return false; } @@ -52,7 +52,7 @@ static bool afs_start_vl_iteration(struct afs_vl_cursor *vc) &cell->dns_lookup_count, smp_load_acquire(&cell->dns_lookup_count) != dns_lookup_count) < 0) { - vc->error = -ERESTARTSYS; + vc->cumul_error.error = -ERESTARTSYS; return false; } } @@ -60,12 +60,12 @@ static bool afs_start_vl_iteration(struct afs_vl_cursor *vc) /* Status load is ordered after lookup counter load */ if (cell->dns_status == DNS_LOOKUP_GOT_NOT_FOUND) { pr_warn("No record of cell %s\n", cell->name); - vc->error = -ENOENT; + vc->cumul_error.error = -ENOENT; return false; } if (cell->dns_source == DNS_RECORD_UNAVAILABLE) { - vc->error = -EDESTADDRREQ; + vc->cumul_error.error = -EDESTADDRREQ; return false; } } @@ -91,52 +91,52 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc) { struct afs_addr_list *alist; struct afs_vlserver *vlserver; - struct afs_error e; unsigned int rtt; - int error = vc->ac.error, i; + s32 abort_code = vc->call_abort_code; + int error = vc->call_error, i; + + vc->nr_iterations++; _enter("%lx[%d],%lx[%d],%d,%d", vc->untried, vc->index, vc->ac.tried, vc->ac.index, - error, vc->ac.abort_code); + error, abort_code); if (vc->flags & AFS_VL_CURSOR_STOP) { _leave(" = f [stopped]"); return false; } - vc->nr_iterations++; + if (vc->nr_iterations == 0) + goto start; /* Evaluate the result of the previous operation, if there was one. */ switch (error) { - case SHRT_MAX: - goto start; - default: case 0: /* Success or local failure. Stop. */ - vc->error = error; + vc->cumul_error.error = error; vc->flags |= AFS_VL_CURSOR_STOP; - _leave(" = f [okay/local %d]", vc->ac.error); + _leave(" = f [okay/local %d]", vc->cumul_error.error); return false; case -ECONNABORTED: /* The far side rejected the operation on some grounds. This * might involve the server being busy or the volume having been moved. */ - switch (vc->ac.abort_code) { + switch (abort_code) { case AFSVL_IO: case AFSVL_BADVOLOPER: case AFSVL_NOMEM: /* The server went weird. */ - vc->error = -EREMOTEIO; + afs_prioritise_error(&vc->cumul_error, -EREMOTEIO, abort_code); //write_lock(&vc->cell->vl_servers_lock); //vc->server_list->weird_mask |= 1 << vc->index; //write_unlock(&vc->cell->vl_servers_lock); goto next_server; default: - vc->error = afs_abort_to_error(vc->ac.abort_code); + afs_prioritise_error(&vc->cumul_error, error, abort_code); goto failed; } @@ -149,12 +149,12 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc) case -ETIMEDOUT: case -ETIME: _debug("no conn %d", error); - vc->error = error; + afs_prioritise_error(&vc->cumul_error, error, 0); goto iterate_address; case -ECONNRESET: _debug("call reset"); - vc->error = error; + afs_prioritise_error(&vc->cumul_error, error, 0); vc->flags |= AFS_VL_CURSOR_RETRY; goto next_server; @@ -178,15 +178,19 @@ start: goto failed; error = afs_send_vl_probes(vc->cell->net, vc->key, vc->server_list); - if (error < 0) - goto failed_set_error; + if (error < 0) { + afs_prioritise_error(&vc->cumul_error, error, 0); + goto failed; + } pick_server: _debug("pick [%lx]", vc->untried); error = afs_wait_for_vl_probes(vc->server_list, vc->untried); - if (error < 0) - goto failed_set_error; + if (error < 0) { + afs_prioritise_error(&vc->cumul_error, error, 0); + goto failed; + } /* Pick the untried server with the lowest RTT. */ vc->index = vc->server_list->preferred; @@ -249,6 +253,7 @@ iterate_address: _debug("VL address %d/%d", vc->ac.index, vc->ac.alist->nr_addrs); + vc->call_responded = false; _leave(" = t %pISpc", rxrpc_kernel_remote_addr(vc->ac.alist->addrs[vc->ac.index].peer)); return true; @@ -264,25 +269,19 @@ no_more_servers: if (vc->flags & AFS_VL_CURSOR_RETRY) goto restart_from_beginning; - e.error = -EDESTADDRREQ; - e.responded = false; for (i = 0; i < vc->server_list->nr_servers; i++) { struct afs_vlserver *s = vc->server_list->servers[i].server; if (test_bit(AFS_VLSERVER_FL_RESPONDING, &s->flags)) - e.responded = true; - afs_prioritise_error(&e, READ_ONCE(s->probe.error), + vc->cumul_error.responded = true; + afs_prioritise_error(&vc->cumul_error, READ_ONCE(s->probe.error), s->probe.abort_code); } - error = e.error; - -failed_set_error: - vc->error = error; failed: vc->flags |= AFS_VL_CURSOR_STOP; afs_end_cursor(&vc->ac); - _leave(" = f [failed %d]", vc->error); + _leave(" = f [failed %d]", vc->cumul_error.error); return false; } @@ -305,7 +304,10 @@ static void afs_vl_dump_edestaddrreq(const struct afs_vl_cursor *vc) pr_notice("DNS: src=%u st=%u lc=%x\n", cell->dns_source, cell->dns_status, cell->dns_lookup_count); pr_notice("VC: ut=%lx ix=%u ni=%hu fl=%hx err=%hd\n", - vc->untried, vc->index, vc->nr_iterations, vc->flags, vc->error); + vc->untried, vc->index, vc->nr_iterations, vc->flags, + vc->cumul_error.error); + pr_notice("VC: call er=%d ac=%d r=%u\n", + vc->call_error, vc->call_abort_code, vc->call_responded); if (vc->server_list) { const struct afs_vlserver_list *sl = vc->server_list; @@ -329,9 +331,8 @@ static void afs_vl_dump_edestaddrreq(const struct afs_vl_cursor *vc) } } - pr_notice("AC: t=%lx ax=%u ac=%d er=%d r=%u ni=%u\n", - vc->ac.tried, vc->ac.index, vc->ac.abort_code, vc->ac.error, - vc->ac.responded, vc->ac.nr_iterations); + pr_notice("AC: t=%lx ax=%u ni=%u\n", + vc->ac.tried, vc->ac.index, vc->ac.nr_iterations); rcu_read_unlock(); } @@ -342,17 +343,16 @@ int afs_end_vlserver_operation(struct afs_vl_cursor *vc) { struct afs_net *net = vc->cell->net; - if (vc->error == -EDESTADDRREQ || - vc->error == -EADDRNOTAVAIL || - vc->error == -ENETUNREACH || - vc->error == -EHOSTUNREACH) + switch (vc->cumul_error.error) { + case -EDESTADDRREQ: + case -EADDRNOTAVAIL: + case -ENETUNREACH: + case -EHOSTUNREACH: afs_vl_dump_edestaddrreq(vc); + break; + } afs_end_cursor(&vc->ac); afs_put_vlserverlist(net, vc->server_list); - - if (vc->error == -ECONNABORTED) - vc->error = afs_abort_to_error(vc->ac.abort_code); - - return vc->error; + return vc->cumul_error.error; } diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c index 650534892a20..db7e94584e87 100644 --- a/fs/afs/vlclient.c +++ b/fs/afs/vlclient.c @@ -161,10 +161,13 @@ struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc, trace_afs_make_vl_call(call); afs_make_call(&vc->ac, call, GFP_KERNEL); afs_wait_for_call_to_complete(call, &vc->ac); + vc->call_abort_code = call->abort_code; + vc->call_error = call->error; + vc->call_responded = call->responded; afs_put_call(call); - if (vc->ac.error) { + if (vc->call_error) { kfree(entry); - return ERR_PTR(vc->ac.error); + return ERR_PTR(vc->call_error); } return entry; } @@ -305,11 +308,14 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc, trace_afs_make_vl_call(call); afs_make_call(&vc->ac, call, GFP_KERNEL); afs_wait_for_call_to_complete(call, &vc->ac); - alist = call->ret_alist; + vc->call_abort_code = call->abort_code; + vc->call_error = call->error; + vc->call_responded = call->responded; + alist = call->ret_alist; afs_put_call(call); - if (vc->ac.error) { + if (vc->call_error) { afs_put_addrlist(alist); - return ERR_PTR(vc->ac.error); + return ERR_PTR(vc->call_error); } return alist; } @@ -656,11 +662,14 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc, trace_afs_make_vl_call(call); afs_make_call(&vc->ac, call, GFP_KERNEL); afs_wait_for_call_to_complete(call, &vc->ac); - alist = call->ret_alist; + vc->call_abort_code = call->abort_code; + vc->call_error = call->error; + vc->call_responded = call->responded; + alist = call->ret_alist; afs_put_call(call); - if (vc->ac.error) { + if (vc->call_error) { afs_put_addrlist(alist); - return ERR_PTR(vc->ac.error); + return ERR_PTR(vc->call_error); } return alist; } @@ -769,11 +778,14 @@ char *afs_yfsvl_get_cell_name(struct afs_vl_cursor *vc) trace_afs_make_vl_call(call); afs_make_call(&vc->ac, call, GFP_KERNEL); afs_wait_for_call_to_complete(call, &vc->ac); - cellname = call->ret_str; + vc->call_abort_code = call->abort_code; + vc->call_error = call->error; + vc->call_responded = call->responded; + cellname = call->ret_str; afs_put_call(call); - if (vc->ac.error) { + if (vc->call_error) { kfree(cellname); - return ERR_PTR(vc->ac.error); + return ERR_PTR(vc->call_error); } return cellname; }