]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - fs/afs/rotate.c
c7975b3ba59afd2f0a87ccb0314c446f303b9912
[mirror_ubuntu-bionic-kernel.git] / fs / afs / rotate.c
1 /* Handle fileserver selection and rotation.
2 *
3 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
4 * 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 */
11
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
14 #include "internal.h"
15
16 /*
17 * Initialise a filesystem server cursor for iterating over FS servers.
18 */
19 void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
20 {
21 memset(fc, 0, sizeof(*fc));
22 }
23
24 /*
25 * Set a filesystem server cursor for using a specific FS server.
26 */
27 int afs_set_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
28 {
29 afs_init_fs_cursor(fc, vnode);
30
31 read_seqlock_excl(&vnode->cb_lock);
32 if (vnode->cb_interest) {
33 if (vnode->cb_interest->server->fs_state == 0)
34 fc->server = afs_get_server(vnode->cb_interest->server);
35 else
36 fc->ac.error = vnode->cb_interest->server->fs_state;
37 } else {
38 fc->ac.error = -ESTALE;
39 }
40 read_sequnlock_excl(&vnode->cb_lock);
41
42 return fc->ac.error;
43 }
44
45 /*
46 * pick a server to use to try accessing this volume
47 * - returns with an elevated usage count on the server chosen
48 */
49 bool afs_volume_pick_fileserver(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
50 {
51 struct afs_volume *volume = vnode->volume;
52 struct afs_server *server;
53 int ret, state, loop;
54
55 _enter("%s", volume->vlocation->vldb.name);
56
57 /* stick with the server we're already using if we can */
58 if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) {
59 fc->server = afs_get_server(vnode->cb_interest->server);
60 goto set_server;
61 }
62
63 down_read(&volume->server_sem);
64
65 /* handle the no-server case */
66 if (volume->nservers == 0) {
67 fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE;
68 up_read(&volume->server_sem);
69 _leave(" = f [no servers %d]", fc->ac.error);
70 return false;
71 }
72
73 /* basically, just search the list for the first live server and use
74 * that */
75 ret = 0;
76 for (loop = 0; loop < volume->nservers; loop++) {
77 server = volume->servers[loop];
78 state = server->fs_state;
79
80 _debug("consider %d [%d]", loop, state);
81
82 switch (state) {
83 case 0:
84 goto picked_server;
85
86 case -ENETUNREACH:
87 if (ret == 0)
88 ret = state;
89 break;
90
91 case -EHOSTUNREACH:
92 if (ret == 0 ||
93 ret == -ENETUNREACH)
94 ret = state;
95 break;
96
97 case -ECONNREFUSED:
98 if (ret == 0 ||
99 ret == -ENETUNREACH ||
100 ret == -EHOSTUNREACH)
101 ret = state;
102 break;
103
104 default:
105 case -EREMOTEIO:
106 if (ret == 0 ||
107 ret == -ENETUNREACH ||
108 ret == -EHOSTUNREACH ||
109 ret == -ECONNREFUSED)
110 ret = state;
111 break;
112 }
113 }
114
115 error:
116 fc->ac.error = ret;
117
118 /* no available servers
119 * - TODO: handle the no active servers case better
120 */
121 up_read(&volume->server_sem);
122 _leave(" = f [%d]", fc->ac.error);
123 return false;
124
125 picked_server:
126 /* Found an apparently healthy server. We need to register an interest
127 * in receiving callbacks before we talk to it.
128 */
129 ret = afs_register_server_cb_interest(vnode,
130 &volume->cb_interests[loop], server);
131 if (ret < 0)
132 goto error;
133
134 fc->server = afs_get_server(server);
135 up_read(&volume->server_sem);
136 set_server:
137 fc->ac.alist = afs_get_addrlist(fc->server->addrs);
138 fc->ac.addr = &fc->ac.alist->addrs[0];
139 _debug("USING SERVER: %pIS\n", &fc->ac.addr->transport);
140 _leave(" = t (picked %pIS)", &fc->ac.addr->transport);
141 return true;
142 }
143
144 /*
145 * release a server after use
146 * - releases the ref on the server struct that was acquired by picking
147 * - records result of using a particular server to access a volume
148 * - return true to try again, false if okay or to issue error
149 * - the caller must release the server struct if result was false
150 */
151 bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc,
152 struct afs_vnode *vnode)
153 {
154 struct afs_volume *volume = vnode->volume;
155 struct afs_server *server = fc->server;
156 unsigned loop;
157
158 _enter("%s,%pIS,%d",
159 volume->vlocation->vldb.name, &fc->ac.addr->transport,
160 fc->ac.error);
161
162 switch (fc->ac.error) {
163 /* success */
164 case 0:
165 server->fs_state = 0;
166 _leave(" = f");
167 return false;
168
169 /* the fileserver denied all knowledge of the volume */
170 case -ENOMEDIUM:
171 down_write(&volume->server_sem);
172
173 /* firstly, find where the server is in the active list (if it
174 * is) */
175 for (loop = 0; loop < volume->nservers; loop++)
176 if (volume->servers[loop] == server)
177 goto present;
178
179 /* no longer there - may have been discarded by another op */
180 goto try_next_server_upw;
181
182 present:
183 volume->nservers--;
184 memmove(&volume->servers[loop],
185 &volume->servers[loop + 1],
186 sizeof(volume->servers[loop]) *
187 (volume->nservers - loop));
188 volume->servers[volume->nservers] = NULL;
189 afs_put_server(afs_v2net(vnode), server);
190 volume->rjservers++;
191
192 if (volume->nservers > 0)
193 /* another server might acknowledge its existence */
194 goto try_next_server_upw;
195
196 /* handle the case where all the fileservers have rejected the
197 * volume
198 * - TODO: try asking the fileservers for volume information
199 * - TODO: contact the VL server again to see if the volume is
200 * no longer registered
201 */
202 up_write(&volume->server_sem);
203 afs_put_server(afs_v2net(vnode), server);
204 fc->server = NULL;
205 _leave(" = f [completely rejected]");
206 return false;
207
208 /* problem reaching the server */
209 case -ENETUNREACH:
210 case -EHOSTUNREACH:
211 case -ECONNREFUSED:
212 case -ETIME:
213 case -ETIMEDOUT:
214 case -EREMOTEIO:
215 /* mark the server as dead
216 * TODO: vary dead timeout depending on error
217 */
218 spin_lock(&server->fs_lock);
219 if (!server->fs_state) {
220 server->fs_state = fc->ac.error;
221 printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error);
222 }
223 spin_unlock(&server->fs_lock);
224 goto try_next_server;
225
226 /* miscellaneous error */
227 default:
228 case -ENOMEM:
229 case -ENONET:
230 /* tell the caller to accept the result */
231 afs_put_server(afs_v2net(vnode), server);
232 fc->server = NULL;
233 _leave(" = f [local failure]");
234 return false;
235 }
236
237 /* tell the caller to loop around and try the next server */
238 try_next_server_upw:
239 up_write(&volume->server_sem);
240 try_next_server:
241 afs_put_server(afs_v2net(vnode), server);
242 _leave(" = t [try next server]");
243 return true;
244 }
245
246 /*
247 * Clean up a fileserver cursor.
248 */
249 int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net)
250 {
251 afs_end_cursor(&fc->ac);
252 afs_put_server(net, fc->server);
253 return fc->ac.error;
254 }