xref: /openbmc/linux/fs/afs/rotate.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29cc6fc50SDavid Howells /* Handle fileserver selection and rotation.
39cc6fc50SDavid Howells  *
49cc6fc50SDavid Howells  * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
59cc6fc50SDavid Howells  * Written by David Howells (dhowells@redhat.com)
69cc6fc50SDavid Howells  */
79cc6fc50SDavid Howells 
89cc6fc50SDavid Howells #include <linux/kernel.h>
99cc6fc50SDavid Howells #include <linux/slab.h>
10d2ddc776SDavid Howells #include <linux/fs.h>
11d2ddc776SDavid Howells #include <linux/sched.h>
12d2ddc776SDavid Howells #include <linux/delay.h>
13d2ddc776SDavid Howells #include <linux/sched/signal.h>
149cc6fc50SDavid Howells #include "internal.h"
15d2ddc776SDavid Howells #include "afs_fs.h"
169cc6fc50SDavid Howells 
179cc6fc50SDavid Howells /*
18d2ddc776SDavid Howells  * Begin iteration through a server list, starting with the vnode's last used
19d2ddc776SDavid Howells  * server if possible, or the last recorded good server if not.
20d2ddc776SDavid Howells  */
afs_start_fs_iteration(struct afs_operation * op,struct afs_vnode * vnode)21a310082fSDavid Howells static bool afs_start_fs_iteration(struct afs_operation *op,
22d2ddc776SDavid Howells 				   struct afs_vnode *vnode)
23d2ddc776SDavid Howells {
2420325960SDavid Howells 	struct afs_server *server;
2520325960SDavid Howells 	void *cb_server;
26d2ddc776SDavid Howells 	int i;
27d2ddc776SDavid Howells 
28e49c7b2fSDavid Howells 	read_lock(&op->volume->servers_lock);
298a070a96SDavid Howells 	op->server_list = afs_get_serverlist(
308a070a96SDavid Howells 		rcu_dereference_protected(op->volume->servers,
318a070a96SDavid Howells 					  lockdep_is_held(&op->volume->servers_lock)));
32e49c7b2fSDavid Howells 	read_unlock(&op->volume->servers_lock);
33d2ddc776SDavid Howells 
34a310082fSDavid Howells 	op->untried = (1UL << op->server_list->nr_servers) - 1;
35a310082fSDavid Howells 	op->index = READ_ONCE(op->server_list->preferred);
363bf0fb6fSDavid Howells 
3720325960SDavid Howells 	cb_server = vnode->cb_server;
3820325960SDavid Howells 	if (cb_server) {
39d2ddc776SDavid Howells 		/* See if the vnode's preferred record is still available */
40a310082fSDavid Howells 		for (i = 0; i < op->server_list->nr_servers; i++) {
4120325960SDavid Howells 			server = op->server_list->servers[i].server;
4220325960SDavid Howells 			if (server == cb_server) {
43a310082fSDavid Howells 				op->index = i;
44d2ddc776SDavid Howells 				goto found_interest;
45d2ddc776SDavid Howells 			}
46d2ddc776SDavid Howells 		}
47d2ddc776SDavid Howells 
48d2ddc776SDavid Howells 		/* If we have a lock outstanding on a server that's no longer
49d2ddc776SDavid Howells 		 * serving this vnode, then we can't switch to another server
50d2ddc776SDavid Howells 		 * and have to return an error.
51d2ddc776SDavid Howells 		 */
52a310082fSDavid Howells 		if (op->flags & AFS_OPERATION_CUR_ONLY) {
53a310082fSDavid Howells 			op->error = -ESTALE;
54d2ddc776SDavid Howells 			return false;
55d2ddc776SDavid Howells 		}
56d2ddc776SDavid Howells 
57d2ddc776SDavid Howells 		/* Note that the callback promise is effectively broken */
58d2ddc776SDavid Howells 		write_seqlock(&vnode->cb_lock);
5920325960SDavid Howells 		ASSERTCMP(cb_server, ==, vnode->cb_server);
6020325960SDavid Howells 		vnode->cb_server = NULL;
61d2ddc776SDavid Howells 		if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
62d2ddc776SDavid Howells 			vnode->cb_break++;
63d2ddc776SDavid Howells 		write_sequnlock(&vnode->cb_lock);
64d2ddc776SDavid Howells 	}
65d2ddc776SDavid Howells 
66d2ddc776SDavid Howells found_interest:
67d2ddc776SDavid Howells 	return true;
68d2ddc776SDavid Howells }
69d2ddc776SDavid Howells 
70d2ddc776SDavid Howells /*
71d2ddc776SDavid Howells  * Post volume busy note.
72d2ddc776SDavid Howells  */
afs_busy(struct afs_volume * volume,u32 abort_code)73d2ddc776SDavid Howells static void afs_busy(struct afs_volume *volume, u32 abort_code)
74d2ddc776SDavid Howells {
75d2ddc776SDavid Howells 	const char *m;
76d2ddc776SDavid Howells 
77d2ddc776SDavid Howells 	switch (abort_code) {
78d2ddc776SDavid Howells 	case VOFFLINE:		m = "offline";		break;
79d2ddc776SDavid Howells 	case VRESTARTING:	m = "restarting";	break;
80d2ddc776SDavid Howells 	case VSALVAGING:	m = "being salvaged";	break;
81d2ddc776SDavid Howells 	default:		m = "busy";		break;
82d2ddc776SDavid Howells 	}
83d2ddc776SDavid Howells 
843b6492dfSDavid Howells 	pr_notice("kAFS: Volume %llu '%s' is %s\n", volume->vid, volume->name, m);
85d2ddc776SDavid Howells }
86d2ddc776SDavid Howells 
87d2ddc776SDavid Howells /*
88d2ddc776SDavid Howells  * Sleep and retry the operation to the same fileserver.
89d2ddc776SDavid Howells  */
afs_sleep_and_retry(struct afs_operation * op)90a310082fSDavid Howells static bool afs_sleep_and_retry(struct afs_operation *op)
91d2ddc776SDavid Howells {
92e49c7b2fSDavid Howells 	if (!(op->flags & AFS_OPERATION_UNINTR)) {
93d2ddc776SDavid Howells 		msleep_interruptible(1000);
94d2ddc776SDavid Howells 		if (signal_pending(current)) {
95a310082fSDavid Howells 			op->error = -ERESTARTSYS;
96d2ddc776SDavid Howells 			return false;
97d2ddc776SDavid Howells 		}
9820b8391fSDavid Howells 	} else {
9920b8391fSDavid Howells 		msleep(1000);
10020b8391fSDavid Howells 	}
101d2ddc776SDavid Howells 
102d2ddc776SDavid Howells 	return true;
103d2ddc776SDavid Howells }
104d2ddc776SDavid Howells 
105d2ddc776SDavid Howells /*
106d2ddc776SDavid Howells  * Select the fileserver to use.  May be called multiple times to rotate
107d2ddc776SDavid Howells  * through the fileservers.
108d2ddc776SDavid Howells  */
afs_select_fileserver(struct afs_operation * op)109a310082fSDavid Howells bool afs_select_fileserver(struct afs_operation *op)
110d2ddc776SDavid Howells {
111d2ddc776SDavid Howells 	struct afs_addr_list *alist;
112d2ddc776SDavid Howells 	struct afs_server *server;
113e49c7b2fSDavid Howells 	struct afs_vnode *vnode = op->file[0].vnode;
1144584ae96SDavid Howells 	struct afs_error e;
1154584ae96SDavid Howells 	u32 rtt;
116a310082fSDavid Howells 	int error = op->ac.error, i;
117d2ddc776SDavid Howells 
1183bf0fb6fSDavid Howells 	_enter("%lx[%d],%lx[%d],%d,%d",
119a310082fSDavid Howells 	       op->untried, op->index,
120a310082fSDavid Howells 	       op->ac.tried, op->ac.index,
121a310082fSDavid Howells 	       error, op->ac.abort_code);
122d2ddc776SDavid Howells 
123a310082fSDavid Howells 	if (op->flags & AFS_OPERATION_STOP) {
124d2ddc776SDavid Howells 		_leave(" = f [stopped]");
125d2ddc776SDavid Howells 		return false;
126d2ddc776SDavid Howells 	}
127d2ddc776SDavid Howells 
128a310082fSDavid Howells 	op->nr_iterations++;
129744bcd71SDavid Howells 
130d2ddc776SDavid Howells 	/* Evaluate the result of the previous operation, if there was one. */
131e7f680f4SDavid Howells 	switch (error) {
132d2ddc776SDavid Howells 	case SHRT_MAX:
133d2ddc776SDavid Howells 		goto start;
134d2ddc776SDavid Howells 
135d2ddc776SDavid Howells 	case 0:
136d2ddc776SDavid Howells 	default:
137d2ddc776SDavid Howells 		/* Success or local failure.  Stop. */
138a310082fSDavid Howells 		op->error = error;
139a310082fSDavid Howells 		op->flags |= AFS_OPERATION_STOP;
140e7f680f4SDavid Howells 		_leave(" = f [okay/local %d]", error);
141d2ddc776SDavid Howells 		return false;
142d2ddc776SDavid Howells 
143d2ddc776SDavid Howells 	case -ECONNABORTED:
144d2ddc776SDavid Howells 		/* The far side rejected the operation on some grounds.  This
145d2ddc776SDavid Howells 		 * might involve the server being busy or the volume having been moved.
146d2ddc776SDavid Howells 		 */
147a310082fSDavid Howells 		switch (op->ac.abort_code) {
148d2ddc776SDavid Howells 		case VNOVOL:
149d2ddc776SDavid Howells 			/* This fileserver doesn't know about the volume.
150d2ddc776SDavid Howells 			 * - May indicate that the VL is wrong - retry once and compare
151d2ddc776SDavid Howells 			 *   the results.
152d2ddc776SDavid Howells 			 * - May indicate that the fileserver couldn't attach to the vol.
153d2ddc776SDavid Howells 			 */
154a310082fSDavid Howells 			if (op->flags & AFS_OPERATION_VNOVOL) {
155a310082fSDavid Howells 				op->error = -EREMOTEIO;
1563d9fa911SDavid Howells 				goto next_server;
157d2ddc776SDavid Howells 			}
158d2ddc776SDavid Howells 
159e49c7b2fSDavid Howells 			write_lock(&op->volume->servers_lock);
160a310082fSDavid Howells 			op->server_list->vnovol_mask |= 1 << op->index;
161e49c7b2fSDavid Howells 			write_unlock(&op->volume->servers_lock);
162d2ddc776SDavid Howells 
163e49c7b2fSDavid Howells 			set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags);
164e49c7b2fSDavid Howells 			error = afs_check_volume_status(op->volume, op);
165e7f680f4SDavid Howells 			if (error < 0)
166e7f680f4SDavid Howells 				goto failed_set_error;
167d2ddc776SDavid Howells 
168e49c7b2fSDavid Howells 			if (test_bit(AFS_VOLUME_DELETED, &op->volume->flags)) {
169a310082fSDavid Howells 				op->error = -ENOMEDIUM;
170d2ddc776SDavid Howells 				goto failed;
171d2ddc776SDavid Howells 			}
172d2ddc776SDavid Howells 
173d2ddc776SDavid Howells 			/* If the server list didn't change, then assume that
174d2ddc776SDavid Howells 			 * it's the fileserver having trouble.
175d2ddc776SDavid Howells 			 */
1768a070a96SDavid Howells 			if (rcu_access_pointer(op->volume->servers) == op->server_list) {
177a310082fSDavid Howells 				op->error = -EREMOTEIO;
1783d9fa911SDavid Howells 				goto next_server;
179d2ddc776SDavid Howells 			}
180d2ddc776SDavid Howells 
181d2ddc776SDavid Howells 			/* Try again */
182a310082fSDavid Howells 			op->flags |= AFS_OPERATION_VNOVOL;
183d2ddc776SDavid Howells 			_leave(" = t [vnovol]");
184d2ddc776SDavid Howells 			return true;
185d2ddc776SDavid Howells 
186d2ddc776SDavid Howells 		case VSALVAGE: /* TODO: Should this return an error or iterate? */
187d2ddc776SDavid Howells 		case VVOLEXISTS:
188d2ddc776SDavid Howells 		case VNOSERVICE:
189d2ddc776SDavid Howells 		case VONLINE:
190d2ddc776SDavid Howells 		case VDISKFULL:
191d2ddc776SDavid Howells 		case VOVERQUOTA:
192a310082fSDavid Howells 			op->error = afs_abort_to_error(op->ac.abort_code);
193d2ddc776SDavid Howells 			goto next_server;
194d2ddc776SDavid Howells 
195d2ddc776SDavid Howells 		case VOFFLINE:
196e49c7b2fSDavid Howells 			if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &op->volume->flags)) {
197e49c7b2fSDavid Howells 				afs_busy(op->volume, op->ac.abort_code);
198e49c7b2fSDavid Howells 				clear_bit(AFS_VOLUME_BUSY, &op->volume->flags);
199d2ddc776SDavid Howells 			}
200a310082fSDavid Howells 			if (op->flags & AFS_OPERATION_NO_VSLEEP) {
201a310082fSDavid Howells 				op->error = -EADV;
202d2ddc776SDavid Howells 				goto failed;
203d2ddc776SDavid Howells 			}
204a310082fSDavid Howells 			if (op->flags & AFS_OPERATION_CUR_ONLY) {
205a310082fSDavid Howells 				op->error = -ESTALE;
206d2ddc776SDavid Howells 				goto failed;
207d2ddc776SDavid Howells 			}
208d2ddc776SDavid Howells 			goto busy;
209d2ddc776SDavid Howells 
210d2ddc776SDavid Howells 		case VSALVAGING:
211d2ddc776SDavid Howells 		case VRESTARTING:
212d2ddc776SDavid Howells 		case VBUSY:
213d2ddc776SDavid Howells 			/* Retry after going round all the servers unless we
214d2ddc776SDavid Howells 			 * have a file lock we need to maintain.
215d2ddc776SDavid Howells 			 */
216a310082fSDavid Howells 			if (op->flags & AFS_OPERATION_NO_VSLEEP) {
217a310082fSDavid Howells 				op->error = -EBUSY;
218d2ddc776SDavid Howells 				goto failed;
219d2ddc776SDavid Howells 			}
220e49c7b2fSDavid Howells 			if (!test_and_set_bit(AFS_VOLUME_BUSY, &op->volume->flags)) {
221e49c7b2fSDavid Howells 				afs_busy(op->volume, op->ac.abort_code);
222e49c7b2fSDavid Howells 				clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags);
223d2ddc776SDavid Howells 			}
224d2ddc776SDavid Howells 		busy:
225a310082fSDavid Howells 			if (op->flags & AFS_OPERATION_CUR_ONLY) {
226a310082fSDavid Howells 				if (!afs_sleep_and_retry(op))
227d2ddc776SDavid Howells 					goto failed;
228d2ddc776SDavid Howells 
229d2ddc776SDavid Howells 				 /* Retry with same server & address */
230d2ddc776SDavid Howells 				_leave(" = t [vbusy]");
231d2ddc776SDavid Howells 				return true;
232d2ddc776SDavid Howells 			}
233d2ddc776SDavid Howells 
234a310082fSDavid Howells 			op->flags |= AFS_OPERATION_VBUSY;
235d2ddc776SDavid Howells 			goto next_server;
236d2ddc776SDavid Howells 
237d2ddc776SDavid Howells 		case VMOVED:
238d2ddc776SDavid Howells 			/* The volume migrated to another server.  We consider
239d2ddc776SDavid Howells 			 * consider all locks and callbacks broken and request
240d2ddc776SDavid Howells 			 * an update from the VLDB.
241d2ddc776SDavid Howells 			 *
242d2ddc776SDavid Howells 			 * We also limit the number of VMOVED hops we will
243d2ddc776SDavid Howells 			 * honour, just in case someone sets up a loop.
244d2ddc776SDavid Howells 			 */
245a310082fSDavid Howells 			if (op->flags & AFS_OPERATION_VMOVED) {
246a310082fSDavid Howells 				op->error = -EREMOTEIO;
247d2ddc776SDavid Howells 				goto failed;
248d2ddc776SDavid Howells 			}
249a310082fSDavid Howells 			op->flags |= AFS_OPERATION_VMOVED;
250d2ddc776SDavid Howells 
251e49c7b2fSDavid Howells 			set_bit(AFS_VOLUME_WAIT, &op->volume->flags);
252e49c7b2fSDavid Howells 			set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags);
253e49c7b2fSDavid Howells 			error = afs_check_volume_status(op->volume, op);
254e7f680f4SDavid Howells 			if (error < 0)
255e7f680f4SDavid Howells 				goto failed_set_error;
256d2ddc776SDavid Howells 
257d2ddc776SDavid Howells 			/* If the server list didn't change, then the VLDB is
258d2ddc776SDavid Howells 			 * out of sync with the fileservers.  This is hopefully
259d2ddc776SDavid Howells 			 * a temporary condition, however, so we don't want to
260d2ddc776SDavid Howells 			 * permanently block access to the file.
261d2ddc776SDavid Howells 			 *
262d2ddc776SDavid Howells 			 * TODO: Try other fileservers if we can.
263d2ddc776SDavid Howells 			 *
264d2ddc776SDavid Howells 			 * TODO: Retry a few times with sleeps.
265d2ddc776SDavid Howells 			 */
2668a070a96SDavid Howells 			if (rcu_access_pointer(op->volume->servers) == op->server_list) {
267a310082fSDavid Howells 				op->error = -ENOMEDIUM;
268d2ddc776SDavid Howells 				goto failed;
269d2ddc776SDavid Howells 			}
270d2ddc776SDavid Howells 
271d2ddc776SDavid Howells 			goto restart_from_beginning;
272d2ddc776SDavid Howells 
273d2ddc776SDavid Howells 		default:
274e49c7b2fSDavid Howells 			clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags);
275e49c7b2fSDavid Howells 			clear_bit(AFS_VOLUME_BUSY, &op->volume->flags);
276a310082fSDavid Howells 			op->error = afs_abort_to_error(op->ac.abort_code);
277d2ddc776SDavid Howells 			goto failed;
278d2ddc776SDavid Howells 		}
279d2ddc776SDavid Howells 
280e7f680f4SDavid Howells 	case -ETIMEDOUT:
281e7f680f4SDavid Howells 	case -ETIME:
282a310082fSDavid Howells 		if (op->error != -EDESTADDRREQ)
283e7f680f4SDavid Howells 			goto iterate_address;
284df561f66SGustavo A. R. Silva 		fallthrough;
2854584ae96SDavid Howells 	case -ERFKILL:
2864584ae96SDavid Howells 	case -EADDRNOTAVAIL:
287d2ddc776SDavid Howells 	case -ENETUNREACH:
288d2ddc776SDavid Howells 	case -EHOSTUNREACH:
2894584ae96SDavid Howells 	case -EHOSTDOWN:
290d2ddc776SDavid Howells 	case -ECONNREFUSED:
291d2ddc776SDavid Howells 		_debug("no conn");
292a310082fSDavid Howells 		op->error = error;
293d2ddc776SDavid Howells 		goto iterate_address;
2941a025028SDavid Howells 
295*adc9613fSDavid Howells 	case -ENETRESET:
296*adc9613fSDavid Howells 		pr_warn("kAFS: Peer reset %s (op=%x)\n",
297*adc9613fSDavid Howells 			op->type ? op->type->name : "???", op->debug_id);
298*adc9613fSDavid Howells 		fallthrough;
2991a025028SDavid Howells 	case -ECONNRESET:
3001a025028SDavid Howells 		_debug("call reset");
301a310082fSDavid Howells 		op->error = error;
3021a025028SDavid Howells 		goto failed;
303d2ddc776SDavid Howells 	}
304d2ddc776SDavid Howells 
305d2ddc776SDavid Howells restart_from_beginning:
306d2ddc776SDavid Howells 	_debug("restart");
307a310082fSDavid Howells 	afs_end_cursor(&op->ac);
30820325960SDavid Howells 	op->server = NULL;
309e49c7b2fSDavid Howells 	afs_put_serverlist(op->net, op->server_list);
310a310082fSDavid Howells 	op->server_list = NULL;
311d2ddc776SDavid Howells start:
312d2ddc776SDavid Howells 	_debug("start");
313d2ddc776SDavid Howells 	/* See if we need to do an update of the volume record.  Note that the
314d2ddc776SDavid Howells 	 * volume may have moved or even have been deleted.
315d2ddc776SDavid Howells 	 */
316e49c7b2fSDavid Howells 	error = afs_check_volume_status(op->volume, op);
317e7f680f4SDavid Howells 	if (error < 0)
318e7f680f4SDavid Howells 		goto failed_set_error;
319d2ddc776SDavid Howells 
320a310082fSDavid Howells 	if (!afs_start_fs_iteration(op, vnode))
321d2ddc776SDavid Howells 		goto failed;
322d2ddc776SDavid Howells 
323e49c7b2fSDavid Howells 	_debug("__ VOL %llx __", op->volume->vid);
3243bf0fb6fSDavid Howells 
3253bf0fb6fSDavid Howells pick_server:
326a310082fSDavid Howells 	_debug("pick [%lx]", op->untried);
3273bf0fb6fSDavid Howells 
328a310082fSDavid Howells 	error = afs_wait_for_fs_probes(op->server_list, op->untried);
3293bf0fb6fSDavid Howells 	if (error < 0)
3303bf0fb6fSDavid Howells 		goto failed_set_error;
3313bf0fb6fSDavid Howells 
3323bf0fb6fSDavid Howells 	/* Pick the untried server with the lowest RTT.  If we have outstanding
3333bf0fb6fSDavid Howells 	 * callbacks, we stick with the server we're already using if we can.
3343bf0fb6fSDavid Howells 	 */
33520325960SDavid Howells 	if (op->server) {
33620325960SDavid Howells 		_debug("server %u", op->index);
337a310082fSDavid Howells 		if (test_bit(op->index, &op->untried))
3383bf0fb6fSDavid Howells 			goto selected_server;
33920325960SDavid Howells 		op->server = NULL;
34020325960SDavid Howells 		_debug("no server");
3413bf0fb6fSDavid Howells 	}
3423bf0fb6fSDavid Howells 
343a310082fSDavid Howells 	op->index = -1;
3443bf0fb6fSDavid Howells 	rtt = U32_MAX;
345a310082fSDavid Howells 	for (i = 0; i < op->server_list->nr_servers; i++) {
346a310082fSDavid Howells 		struct afs_server *s = op->server_list->servers[i].server;
3473bf0fb6fSDavid Howells 
348f3c130e6SDavid Howells 		if (!test_bit(i, &op->untried) ||
349f3c130e6SDavid Howells 		    !test_bit(AFS_SERVER_FL_RESPONDING, &s->flags))
3503bf0fb6fSDavid Howells 			continue;
3513bf0fb6fSDavid Howells 		if (s->probe.rtt < rtt) {
352a310082fSDavid Howells 			op->index = i;
3533bf0fb6fSDavid Howells 			rtt = s->probe.rtt;
3543bf0fb6fSDavid Howells 		}
3553bf0fb6fSDavid Howells 	}
3563bf0fb6fSDavid Howells 
357a310082fSDavid Howells 	if (op->index == -1)
3583bf0fb6fSDavid Howells 		goto no_more_servers;
3593bf0fb6fSDavid Howells 
3603bf0fb6fSDavid Howells selected_server:
361a310082fSDavid Howells 	_debug("use %d", op->index);
362a310082fSDavid Howells 	__clear_bit(op->index, &op->untried);
3633bf0fb6fSDavid Howells 
364d2ddc776SDavid Howells 	/* We're starting on a different fileserver from the list.  We need to
365d2ddc776SDavid Howells 	 * check it, create a callback intercept, find its address list and
366d2ddc776SDavid Howells 	 * probe its capabilities before we use it.
367d2ddc776SDavid Howells 	 */
368a310082fSDavid Howells 	ASSERTCMP(op->ac.alist, ==, NULL);
369a310082fSDavid Howells 	server = op->server_list->servers[op->index].server;
370d2ddc776SDavid Howells 
371a310082fSDavid Howells 	if (!afs_check_server_record(op, server))
372d2ddc776SDavid Howells 		goto failed;
373d2ddc776SDavid Howells 
374d2ddc776SDavid Howells 	_debug("USING SERVER: %pU", &server->uuid);
375d2ddc776SDavid Howells 
3768409f67bSDavid Howells 	op->flags |= AFS_OPERATION_RETRY_SERVER;
37720325960SDavid Howells 	op->server = server;
37820325960SDavid Howells 	if (vnode->cb_server != server) {
37920325960SDavid Howells 		vnode->cb_server = server;
38020325960SDavid Howells 		vnode->cb_s_break = server->cb_s_break;
3814fe6a946SDavid Howells 		vnode->cb_fs_s_break = atomic_read(&server->cell->fs_s_break);
38220325960SDavid Howells 		vnode->cb_v_break = vnode->volume->cb_v_break;
38320325960SDavid Howells 		clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
38420325960SDavid Howells 	}
385d2ddc776SDavid Howells 
386d2ddc776SDavid Howells 	read_lock(&server->fs_lock);
387d2ddc776SDavid Howells 	alist = rcu_dereference_protected(server->addresses,
388d2ddc776SDavid Howells 					  lockdep_is_held(&server->fs_lock));
389d2ddc776SDavid Howells 	afs_get_addrlist(alist);
390d2ddc776SDavid Howells 	read_unlock(&server->fs_lock);
391d2ddc776SDavid Howells 
3928409f67bSDavid Howells retry_server:
393a310082fSDavid Howells 	memset(&op->ac, 0, sizeof(op->ac));
394d2ddc776SDavid Howells 
395a310082fSDavid Howells 	if (!op->ac.alist)
396a310082fSDavid Howells 		op->ac.alist = alist;
397d2ddc776SDavid Howells 	else
398d2ddc776SDavid Howells 		afs_put_addrlist(alist);
399d2ddc776SDavid Howells 
400a310082fSDavid Howells 	op->ac.index = -1;
401d2ddc776SDavid Howells 
402d2ddc776SDavid Howells iterate_address:
403a310082fSDavid Howells 	ASSERT(op->ac.alist);
404d2ddc776SDavid Howells 	/* Iterate over the current server's address list to try and find an
405d2ddc776SDavid Howells 	 * address on which it will respond to us.
406d2ddc776SDavid Howells 	 */
407a310082fSDavid Howells 	if (!afs_iterate_addresses(&op->ac))
4088409f67bSDavid Howells 		goto out_of_addresses;
409fe4d774cSDavid Howells 
4108409f67bSDavid Howells 	_debug("address [%u] %u/%u %pISp",
4118409f67bSDavid Howells 	       op->index, op->ac.index, op->ac.alist->nr_addrs,
4128409f67bSDavid Howells 	       &op->ac.alist->addrs[op->ac.index].transport);
4133bf0fb6fSDavid Howells 
414d2ddc776SDavid Howells 	_leave(" = t");
415d2ddc776SDavid Howells 	return true;
416d2ddc776SDavid Howells 
4178409f67bSDavid Howells out_of_addresses:
4188409f67bSDavid Howells 	/* We've now had a failure to respond on all of a server's addresses -
4198409f67bSDavid Howells 	 * immediately probe them again and consider retrying the server.
4208409f67bSDavid Howells 	 */
4218409f67bSDavid Howells 	afs_probe_fileserver(op->net, op->server);
4228409f67bSDavid Howells 	if (op->flags & AFS_OPERATION_RETRY_SERVER) {
4238409f67bSDavid Howells 		alist = op->ac.alist;
4248409f67bSDavid Howells 		error = afs_wait_for_one_fs_probe(
4258409f67bSDavid Howells 			op->server, !(op->flags & AFS_OPERATION_UNINTR));
4268409f67bSDavid Howells 		switch (error) {
4278409f67bSDavid Howells 		case 0:
4288409f67bSDavid Howells 			op->flags &= ~AFS_OPERATION_RETRY_SERVER;
4298409f67bSDavid Howells 			goto retry_server;
4308409f67bSDavid Howells 		case -ERESTARTSYS:
4318409f67bSDavid Howells 			goto failed_set_error;
4328409f67bSDavid Howells 		case -ETIME:
4338409f67bSDavid Howells 		case -EDESTADDRREQ:
4348409f67bSDavid Howells 			goto next_server;
4358409f67bSDavid Howells 		}
4368409f67bSDavid Howells 	}
4378409f67bSDavid Howells 
43816280a15SDavid Howells next_server:
43916280a15SDavid Howells 	_debug("next");
440a310082fSDavid Howells 	afs_end_cursor(&op->ac);
4413bf0fb6fSDavid Howells 	goto pick_server;
44216280a15SDavid Howells 
4433bf0fb6fSDavid Howells no_more_servers:
44416280a15SDavid Howells 	/* That's all the servers poked to no good effect.  Try again if some
44516280a15SDavid Howells 	 * of them were busy.
44616280a15SDavid Howells 	 */
447a310082fSDavid Howells 	if (op->flags & AFS_OPERATION_VBUSY)
44816280a15SDavid Howells 		goto restart_from_beginning;
44916280a15SDavid Howells 
4504584ae96SDavid Howells 	e.error = -EDESTADDRREQ;
4514584ae96SDavid Howells 	e.responded = false;
452a310082fSDavid Howells 	for (i = 0; i < op->server_list->nr_servers; i++) {
453a310082fSDavid Howells 		struct afs_server *s = op->server_list->servers[i].server;
4543bf0fb6fSDavid Howells 
4554584ae96SDavid Howells 		afs_prioritise_error(&e, READ_ONCE(s->probe.error),
4564584ae96SDavid Howells 				     s->probe.abort_code);
4573bf0fb6fSDavid Howells 	}
45816280a15SDavid Howells 
45951eba999SDavid Howells 	error = e.error;
46051eba999SDavid Howells 
461e7f680f4SDavid Howells failed_set_error:
462a310082fSDavid Howells 	op->error = error;
463d2ddc776SDavid Howells failed:
464a310082fSDavid Howells 	op->flags |= AFS_OPERATION_STOP;
465a310082fSDavid Howells 	afs_end_cursor(&op->ac);
466a310082fSDavid Howells 	_leave(" = f [failed %d]", op->error);
467d2ddc776SDavid Howells 	return false;
468d2ddc776SDavid Howells }
469d2ddc776SDavid Howells 
470d2ddc776SDavid Howells /*
471744bcd71SDavid Howells  * Dump cursor state in the case of the error being EDESTADDRREQ.
472744bcd71SDavid Howells  */
afs_dump_edestaddrreq(const struct afs_operation * op)473e49c7b2fSDavid Howells void afs_dump_edestaddrreq(const struct afs_operation *op)
474744bcd71SDavid Howells {
475744bcd71SDavid Howells 	static int count;
476744bcd71SDavid Howells 	int i;
477744bcd71SDavid Howells 
478744bcd71SDavid Howells 	if (!IS_ENABLED(CONFIG_AFS_DEBUG_CURSOR) || count > 3)
479744bcd71SDavid Howells 		return;
480744bcd71SDavid Howells 	count++;
481744bcd71SDavid Howells 
482744bcd71SDavid Howells 	rcu_read_lock();
483744bcd71SDavid Howells 
484744bcd71SDavid Howells 	pr_notice("EDESTADDR occurred\n");
485e49c7b2fSDavid Howells 	pr_notice("FC: cbb=%x cbb2=%x fl=%x err=%hd\n",
486e49c7b2fSDavid Howells 		  op->file[0].cb_break_before,
487e49c7b2fSDavid Howells 		  op->file[1].cb_break_before, op->flags, op->error);
4883bf0fb6fSDavid Howells 	pr_notice("FC: ut=%lx ix=%d ni=%u\n",
489a310082fSDavid Howells 		  op->untried, op->index, op->nr_iterations);
490744bcd71SDavid Howells 
491a310082fSDavid Howells 	if (op->server_list) {
492a310082fSDavid Howells 		const struct afs_server_list *sl = op->server_list;
4933bf0fb6fSDavid Howells 		pr_notice("FC: SL nr=%u pr=%u vnov=%hx\n",
4943bf0fb6fSDavid Howells 			  sl->nr_servers, sl->preferred, sl->vnovol_mask);
495744bcd71SDavid Howells 		for (i = 0; i < sl->nr_servers; i++) {
496744bcd71SDavid Howells 			const struct afs_server *s = sl->servers[i].server;
497744bcd71SDavid Howells 			pr_notice("FC: server fl=%lx av=%u %pU\n",
498744bcd71SDavid Howells 				  s->flags, s->addr_version, &s->uuid);
499744bcd71SDavid Howells 			if (s->addresses) {
500744bcd71SDavid Howells 				const struct afs_addr_list *a =
501744bcd71SDavid Howells 					rcu_dereference(s->addresses);
5023bf0fb6fSDavid Howells 				pr_notice("FC:  - av=%u nr=%u/%u/%u pr=%u\n",
503744bcd71SDavid Howells 					  a->version,
504744bcd71SDavid Howells 					  a->nr_ipv4, a->nr_addrs, a->max_addrs,
5053bf0fb6fSDavid Howells 					  a->preferred);
506f6cbb368SDavid Howells 				pr_notice("FC:  - R=%lx F=%lx\n",
507f6cbb368SDavid Howells 					  a->responded, a->failed);
508a310082fSDavid Howells 				if (a == op->ac.alist)
509744bcd71SDavid Howells 					pr_notice("FC:  - current\n");
510744bcd71SDavid Howells 			}
511744bcd71SDavid Howells 		}
512744bcd71SDavid Howells 	}
513744bcd71SDavid Howells 
5143bf0fb6fSDavid Howells 	pr_notice("AC: t=%lx ax=%u ac=%d er=%d r=%u ni=%u\n",
515a310082fSDavid Howells 		  op->ac.tried, op->ac.index, op->ac.abort_code, op->ac.error,
516a310082fSDavid Howells 		  op->ac.responded, op->ac.nr_iterations);
517744bcd71SDavid Howells 	rcu_read_unlock();
518744bcd71SDavid Howells }
519