diff options
author | David Howells <dhowells@redhat.com> | 2007-04-26 18:55:03 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-04-26 18:55:03 -0400 |
commit | 08e0e7c82eeadec6f4871a386b86bf0f0fbcb4eb (patch) | |
tree | 1c4f7e91e20e56ff2ec755e988a6ee828b1a21c0 /fs/afs/vlocation.c | |
parent | 651350d10f93bed7003c9a66e24cf25e0f8eed3d (diff) |
[AF_RXRPC]: Make the in-kernel AFS filesystem use AF_RXRPC.
Make the in-kernel AFS filesystem use AF_RXRPC instead of the old RxRPC code.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'fs/afs/vlocation.c')
-rw-r--r-- | fs/afs/vlocation.c | 1153 |
1 files changed, 469 insertions, 684 deletions
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index e48728c92175..60cb2f408c75 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* volume location management | 1 | /* AFS volume location management |
2 | * | 2 | * |
3 | * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
@@ -12,130 +12,60 @@ | |||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/slab.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/pagemap.h> | ||
18 | #include "volume.h" | ||
19 | #include "cell.h" | ||
20 | #include "cmservice.h" | ||
21 | #include "fsclient.h" | ||
22 | #include "vlclient.h" | ||
23 | #include "kafstimod.h" | ||
24 | #include <rxrpc/connection.h> | ||
25 | #include "internal.h" | 15 | #include "internal.h" |
26 | 16 | ||
27 | #define AFS_VLDB_TIMEOUT HZ*1000 | 17 | unsigned afs_vlocation_timeout = 10; /* volume location timeout in seconds */ |
18 | unsigned afs_vlocation_update_timeout = 10 * 60; | ||
28 | 19 | ||
29 | static void afs_vlocation_update_timer(struct afs_timer *timer); | 20 | static void afs_vlocation_reaper(struct work_struct *); |
30 | static void afs_vlocation_update_attend(struct afs_async_op *op); | 21 | static void afs_vlocation_updater(struct work_struct *); |
31 | static void afs_vlocation_update_discard(struct afs_async_op *op); | ||
32 | static void __afs_put_vlocation(struct afs_vlocation *vlocation); | ||
33 | 22 | ||
34 | static void __afs_vlocation_timeout(struct afs_timer *timer) | 23 | static LIST_HEAD(afs_vlocation_updates); |
35 | { | 24 | static LIST_HEAD(afs_vlocation_graveyard); |
36 | struct afs_vlocation *vlocation = | 25 | static DEFINE_SPINLOCK(afs_vlocation_updates_lock); |
37 | list_entry(timer, struct afs_vlocation, timeout); | 26 | static DEFINE_SPINLOCK(afs_vlocation_graveyard_lock); |
38 | 27 | static DECLARE_DELAYED_WORK(afs_vlocation_reap, afs_vlocation_reaper); | |
39 | _debug("VL TIMEOUT [%s{u=%d}]", | 28 | static DECLARE_DELAYED_WORK(afs_vlocation_update, afs_vlocation_updater); |
40 | vlocation->vldb.name, atomic_read(&vlocation->usage)); | 29 | static struct workqueue_struct *afs_vlocation_update_worker; |
41 | |||
42 | afs_vlocation_do_timeout(vlocation); | ||
43 | } | ||
44 | |||
45 | static const struct afs_timer_ops afs_vlocation_timer_ops = { | ||
46 | .timed_out = __afs_vlocation_timeout, | ||
47 | }; | ||
48 | |||
49 | static const struct afs_timer_ops afs_vlocation_update_timer_ops = { | ||
50 | .timed_out = afs_vlocation_update_timer, | ||
51 | }; | ||
52 | |||
53 | static const struct afs_async_op_ops afs_vlocation_update_op_ops = { | ||
54 | .attend = afs_vlocation_update_attend, | ||
55 | .discard = afs_vlocation_update_discard, | ||
56 | }; | ||
57 | |||
58 | static LIST_HEAD(afs_vlocation_update_pendq); /* queue of VLs awaiting update */ | ||
59 | static struct afs_vlocation *afs_vlocation_update; /* VL currently being updated */ | ||
60 | static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */ | ||
61 | |||
62 | #ifdef AFS_CACHING_SUPPORT | ||
63 | static cachefs_match_val_t afs_vlocation_cache_match(void *target, | ||
64 | const void *entry); | ||
65 | static void afs_vlocation_cache_update(void *source, void *entry); | ||
66 | |||
67 | struct cachefs_index_def afs_vlocation_cache_index_def = { | ||
68 | .name = "vldb", | ||
69 | .data_size = sizeof(struct afs_cache_vlocation), | ||
70 | .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, | ||
71 | .match = afs_vlocation_cache_match, | ||
72 | .update = afs_vlocation_cache_update, | ||
73 | }; | ||
74 | #endif | ||
75 | 30 | ||
76 | /* | 31 | /* |
77 | * iterate through the VL servers in a cell until one of them admits knowing | 32 | * iterate through the VL servers in a cell until one of them admits knowing |
78 | * about the volume in question | 33 | * about the volume in question |
79 | * - caller must have cell->vl_sem write-locked | ||
80 | */ | 34 | */ |
81 | static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation, | 35 | static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl, |
82 | const char *name, | ||
83 | unsigned namesz, | ||
84 | struct afs_cache_vlocation *vldb) | 36 | struct afs_cache_vlocation *vldb) |
85 | { | 37 | { |
86 | struct afs_server *server = NULL; | 38 | struct afs_cell *cell = vl->cell; |
87 | struct afs_cell *cell = vlocation->cell; | 39 | struct in_addr addr; |
88 | int count, ret; | 40 | int count, ret; |
89 | 41 | ||
90 | _enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz); | 42 | _enter("%s,%s", cell->name, vl->vldb.name); |
91 | 43 | ||
44 | down_write(&vl->cell->vl_sem); | ||
92 | ret = -ENOMEDIUM; | 45 | ret = -ENOMEDIUM; |
93 | for (count = cell->vl_naddrs; count > 0; count--) { | 46 | for (count = cell->vl_naddrs; count > 0; count--) { |
94 | _debug("CellServ[%hu]: %08x", | 47 | addr = cell->vl_addrs[cell->vl_curr_svix]; |
95 | cell->vl_curr_svix, | 48 | |
96 | cell->vl_addrs[cell->vl_curr_svix].s_addr); | 49 | _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr); |
97 | |||
98 | /* try and create a server */ | ||
99 | ret = afs_server_lookup(cell, | ||
100 | &cell->vl_addrs[cell->vl_curr_svix], | ||
101 | &server); | ||
102 | switch (ret) { | ||
103 | case 0: | ||
104 | break; | ||
105 | case -ENOMEM: | ||
106 | case -ENONET: | ||
107 | goto out; | ||
108 | default: | ||
109 | goto rotate; | ||
110 | } | ||
111 | 50 | ||
112 | /* attempt to access the VL server */ | 51 | /* attempt to access the VL server */ |
113 | ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb); | 52 | ret = afs_vl_get_entry_by_name(&addr, vl->vldb.name, vldb, |
53 | &afs_sync_call); | ||
114 | switch (ret) { | 54 | switch (ret) { |
115 | case 0: | 55 | case 0: |
116 | afs_put_server(server); | ||
117 | goto out; | 56 | goto out; |
118 | case -ENOMEM: | 57 | case -ENOMEM: |
119 | case -ENONET: | 58 | case -ENONET: |
120 | case -ENETUNREACH: | 59 | case -ENETUNREACH: |
121 | case -EHOSTUNREACH: | 60 | case -EHOSTUNREACH: |
122 | case -ECONNREFUSED: | 61 | case -ECONNREFUSED: |
123 | down_write(&server->sem); | ||
124 | if (server->vlserver) { | ||
125 | rxrpc_put_connection(server->vlserver); | ||
126 | server->vlserver = NULL; | ||
127 | } | ||
128 | up_write(&server->sem); | ||
129 | afs_put_server(server); | ||
130 | if (ret == -ENOMEM || ret == -ENONET) | 62 | if (ret == -ENOMEM || ret == -ENONET) |
131 | goto out; | 63 | goto out; |
132 | goto rotate; | 64 | goto rotate; |
133 | case -ENOMEDIUM: | 65 | case -ENOMEDIUM: |
134 | afs_put_server(server); | ||
135 | goto out; | 66 | goto out; |
136 | default: | 67 | default: |
137 | afs_put_server(server); | 68 | ret = -EIO; |
138 | ret = -ENOMEDIUM; | ||
139 | goto rotate; | 69 | goto rotate; |
140 | } | 70 | } |
141 | 71 | ||
@@ -146,6 +76,7 @@ static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation, | |||
146 | } | 76 | } |
147 | 77 | ||
148 | out: | 78 | out: |
79 | up_write(&vl->cell->vl_sem); | ||
149 | _leave(" = %d", ret); | 80 | _leave(" = %d", ret); |
150 | return ret; | 81 | return ret; |
151 | } | 82 | } |
@@ -153,66 +84,56 @@ out: | |||
153 | /* | 84 | /* |
154 | * iterate through the VL servers in a cell until one of them admits knowing | 85 | * iterate through the VL servers in a cell until one of them admits knowing |
155 | * about the volume in question | 86 | * about the volume in question |
156 | * - caller must have cell->vl_sem write-locked | ||
157 | */ | 87 | */ |
158 | static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation, | 88 | static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl, |
159 | afs_volid_t volid, | 89 | afs_volid_t volid, |
160 | afs_voltype_t voltype, | 90 | afs_voltype_t voltype, |
161 | struct afs_cache_vlocation *vldb) | 91 | struct afs_cache_vlocation *vldb) |
162 | { | 92 | { |
163 | struct afs_server *server = NULL; | 93 | struct afs_cell *cell = vl->cell; |
164 | struct afs_cell *cell = vlocation->cell; | 94 | struct in_addr addr; |
165 | int count, ret; | 95 | int count, ret; |
166 | 96 | ||
167 | _enter("%s,%x,%d,", cell->name, volid, voltype); | 97 | _enter("%s,%x,%d,", cell->name, volid, voltype); |
168 | 98 | ||
99 | down_write(&vl->cell->vl_sem); | ||
169 | ret = -ENOMEDIUM; | 100 | ret = -ENOMEDIUM; |
170 | for (count = cell->vl_naddrs; count > 0; count--) { | 101 | for (count = cell->vl_naddrs; count > 0; count--) { |
171 | _debug("CellServ[%hu]: %08x", | 102 | addr = cell->vl_addrs[cell->vl_curr_svix]; |
172 | cell->vl_curr_svix, | 103 | |
173 | cell->vl_addrs[cell->vl_curr_svix].s_addr); | 104 | _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr); |
174 | |||
175 | /* try and create a server */ | ||
176 | ret = afs_server_lookup(cell, | ||
177 | &cell->vl_addrs[cell->vl_curr_svix], | ||
178 | &server); | ||
179 | switch (ret) { | ||
180 | case 0: | ||
181 | break; | ||
182 | case -ENOMEM: | ||
183 | case -ENONET: | ||
184 | goto out; | ||
185 | default: | ||
186 | goto rotate; | ||
187 | } | ||
188 | 105 | ||
189 | /* attempt to access the VL server */ | 106 | /* attempt to access the VL server */ |
190 | ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb); | 107 | ret = afs_vl_get_entry_by_id(&addr, volid, voltype, vldb, |
108 | &afs_sync_call); | ||
191 | switch (ret) { | 109 | switch (ret) { |
192 | case 0: | 110 | case 0: |
193 | afs_put_server(server); | ||
194 | goto out; | 111 | goto out; |
195 | case -ENOMEM: | 112 | case -ENOMEM: |
196 | case -ENONET: | 113 | case -ENONET: |
197 | case -ENETUNREACH: | 114 | case -ENETUNREACH: |
198 | case -EHOSTUNREACH: | 115 | case -EHOSTUNREACH: |
199 | case -ECONNREFUSED: | 116 | case -ECONNREFUSED: |
200 | down_write(&server->sem); | ||
201 | if (server->vlserver) { | ||
202 | rxrpc_put_connection(server->vlserver); | ||
203 | server->vlserver = NULL; | ||
204 | } | ||
205 | up_write(&server->sem); | ||
206 | afs_put_server(server); | ||
207 | if (ret == -ENOMEM || ret == -ENONET) | 117 | if (ret == -ENOMEM || ret == -ENONET) |
208 | goto out; | 118 | goto out; |
209 | goto rotate; | 119 | goto rotate; |
120 | case -EBUSY: | ||
121 | vl->upd_busy_cnt++; | ||
122 | if (vl->upd_busy_cnt <= 3) { | ||
123 | if (vl->upd_busy_cnt > 1) { | ||
124 | /* second+ BUSY - sleep a little bit */ | ||
125 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
126 | schedule_timeout(1); | ||
127 | __set_current_state(TASK_RUNNING); | ||
128 | } | ||
129 | continue; | ||
130 | } | ||
131 | break; | ||
210 | case -ENOMEDIUM: | 132 | case -ENOMEDIUM: |
211 | afs_put_server(server); | 133 | vl->upd_rej_cnt++; |
212 | goto out; | 134 | goto rotate; |
213 | default: | 135 | default: |
214 | afs_put_server(server); | 136 | ret = -EIO; |
215 | ret = -ENOMEDIUM; | ||
216 | goto rotate; | 137 | goto rotate; |
217 | } | 138 | } |
218 | 139 | ||
@@ -220,150 +141,83 @@ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation, | |||
220 | rotate: | 141 | rotate: |
221 | cell->vl_curr_svix++; | 142 | cell->vl_curr_svix++; |
222 | cell->vl_curr_svix %= cell->vl_naddrs; | 143 | cell->vl_curr_svix %= cell->vl_naddrs; |
144 | vl->upd_busy_cnt = 0; | ||
223 | } | 145 | } |
224 | 146 | ||
225 | out: | 147 | out: |
148 | if (ret < 0 && vl->upd_rej_cnt > 0) { | ||
149 | printk(KERN_NOTICE "kAFS:" | ||
150 | " Active volume no longer valid '%s'\n", | ||
151 | vl->vldb.name); | ||
152 | vl->valid = 0; | ||
153 | ret = -ENOMEDIUM; | ||
154 | } | ||
155 | |||
156 | up_write(&vl->cell->vl_sem); | ||
226 | _leave(" = %d", ret); | 157 | _leave(" = %d", ret); |
227 | return ret; | 158 | return ret; |
228 | } | 159 | } |
229 | 160 | ||
230 | /* | 161 | /* |
231 | * lookup volume location | 162 | * allocate a volume location record |
232 | * - caller must have cell->vol_sem write-locked | ||
233 | * - iterate through the VL servers in a cell until one of them admits knowing | ||
234 | * about the volume in question | ||
235 | * - lookup in the local cache if not able to find on the VL server | ||
236 | * - insert/update in the local cache if did get a VL response | ||
237 | */ | 163 | */ |
238 | int afs_vlocation_lookup(struct afs_cell *cell, | 164 | static struct afs_vlocation *afs_vlocation_alloc(struct afs_cell *cell, |
239 | const char *name, | 165 | const char *name, |
240 | unsigned namesz, | 166 | size_t namesz) |
241 | struct afs_vlocation **_vlocation) | ||
242 | { | 167 | { |
243 | struct afs_cache_vlocation vldb; | 168 | struct afs_vlocation *vl; |
244 | struct afs_vlocation *vlocation; | 169 | |
245 | afs_voltype_t voltype; | 170 | vl = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL); |
246 | afs_volid_t vid; | 171 | if (vl) { |
247 | int active = 0, ret; | 172 | vl->cell = cell; |
248 | 173 | vl->state = AFS_VL_NEW; | |
249 | _enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz); | 174 | atomic_set(&vl->usage, 1); |
250 | 175 | INIT_LIST_HEAD(&vl->link); | |
251 | if (namesz > sizeof(vlocation->vldb.name)) { | 176 | INIT_LIST_HEAD(&vl->grave); |
252 | _leave(" = -ENAMETOOLONG"); | 177 | INIT_LIST_HEAD(&vl->update); |
253 | return -ENAMETOOLONG; | 178 | init_waitqueue_head(&vl->waitq); |
254 | } | 179 | rwlock_init(&vl->lock); |
255 | 180 | memcpy(vl->vldb.name, name, namesz); | |
256 | /* search the cell's active list first */ | ||
257 | list_for_each_entry(vlocation, &cell->vl_list, link) { | ||
258 | if (namesz < sizeof(vlocation->vldb.name) && | ||
259 | vlocation->vldb.name[namesz] != '\0') | ||
260 | continue; | ||
261 | |||
262 | if (memcmp(vlocation->vldb.name, name, namesz) == 0) | ||
263 | goto found_in_memory; | ||
264 | } | ||
265 | |||
266 | /* search the cell's graveyard list second */ | ||
267 | spin_lock(&cell->vl_gylock); | ||
268 | list_for_each_entry(vlocation, &cell->vl_graveyard, link) { | ||
269 | if (namesz < sizeof(vlocation->vldb.name) && | ||
270 | vlocation->vldb.name[namesz] != '\0') | ||
271 | continue; | ||
272 | |||
273 | if (memcmp(vlocation->vldb.name, name, namesz) == 0) | ||
274 | goto found_in_graveyard; | ||
275 | } | ||
276 | spin_unlock(&cell->vl_gylock); | ||
277 | |||
278 | /* not in the cell's in-memory lists - create a new record */ | ||
279 | vlocation = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL); | ||
280 | if (!vlocation) | ||
281 | return -ENOMEM; | ||
282 | |||
283 | atomic_set(&vlocation->usage, 1); | ||
284 | INIT_LIST_HEAD(&vlocation->link); | ||
285 | rwlock_init(&vlocation->lock); | ||
286 | memcpy(vlocation->vldb.name, name, namesz); | ||
287 | |||
288 | afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops); | ||
289 | afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops); | ||
290 | afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops); | ||
291 | |||
292 | afs_get_cell(cell); | ||
293 | vlocation->cell = cell; | ||
294 | |||
295 | list_add_tail(&vlocation->link, &cell->vl_list); | ||
296 | |||
297 | #ifdef AFS_CACHING_SUPPORT | ||
298 | /* we want to store it in the cache, plus it might already be | ||
299 | * encached */ | ||
300 | cachefs_acquire_cookie(cell->cache, | ||
301 | &afs_volume_cache_index_def, | ||
302 | vlocation, | ||
303 | &vlocation->cache); | ||
304 | |||
305 | if (vlocation->valid) | ||
306 | goto found_in_cache; | ||
307 | #endif | ||
308 | |||
309 | /* try to look up an unknown volume in the cell VL databases by name */ | ||
310 | ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb); | ||
311 | if (ret < 0) { | ||
312 | printk("kAFS: failed to locate '%*.*s' in cell '%s'\n", | ||
313 | namesz, namesz, name, cell->name); | ||
314 | goto error; | ||
315 | } | 181 | } |
316 | 182 | ||
317 | goto found_on_vlserver; | 183 | _leave(" = %p", vl); |
318 | 184 | return vl; | |
319 | found_in_graveyard: | 185 | } |
320 | /* found in the graveyard - resurrect */ | ||
321 | _debug("found in graveyard"); | ||
322 | atomic_inc(&vlocation->usage); | ||
323 | list_move_tail(&vlocation->link, &cell->vl_list); | ||
324 | spin_unlock(&cell->vl_gylock); | ||
325 | |||
326 | afs_kafstimod_del_timer(&vlocation->timeout); | ||
327 | goto active; | ||
328 | |||
329 | found_in_memory: | ||
330 | /* found in memory - check to see if it's active */ | ||
331 | _debug("found in memory"); | ||
332 | atomic_inc(&vlocation->usage); | ||
333 | 186 | ||
334 | active: | 187 | /* |
335 | active = 1; | 188 | * update record if we found it in the cache |
189 | */ | ||
190 | static int afs_vlocation_update_record(struct afs_vlocation *vl, | ||
191 | struct afs_cache_vlocation *vldb) | ||
192 | { | ||
193 | afs_voltype_t voltype; | ||
194 | afs_volid_t vid; | ||
195 | int ret; | ||
336 | 196 | ||
337 | #ifdef AFS_CACHING_SUPPORT | ||
338 | found_in_cache: | ||
339 | #endif | ||
340 | /* try to look up a cached volume in the cell VL databases by ID */ | 197 | /* try to look up a cached volume in the cell VL databases by ID */ |
341 | _debug("found in cache"); | ||
342 | |||
343 | _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", | 198 | _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", |
344 | vlocation->vldb.name, | 199 | vl->vldb.name, |
345 | vlocation->vldb.vidmask, | 200 | vl->vldb.vidmask, |
346 | ntohl(vlocation->vldb.servers[0].s_addr), | 201 | ntohl(vl->vldb.servers[0].s_addr), |
347 | vlocation->vldb.srvtmask[0], | 202 | vl->vldb.srvtmask[0], |
348 | ntohl(vlocation->vldb.servers[1].s_addr), | 203 | ntohl(vl->vldb.servers[1].s_addr), |
349 | vlocation->vldb.srvtmask[1], | 204 | vl->vldb.srvtmask[1], |
350 | ntohl(vlocation->vldb.servers[2].s_addr), | 205 | ntohl(vl->vldb.servers[2].s_addr), |
351 | vlocation->vldb.srvtmask[2] | 206 | vl->vldb.srvtmask[2]); |
352 | ); | ||
353 | 207 | ||
354 | _debug("Vids: %08x %08x %08x", | 208 | _debug("Vids: %08x %08x %08x", |
355 | vlocation->vldb.vid[0], | 209 | vl->vldb.vid[0], |
356 | vlocation->vldb.vid[1], | 210 | vl->vldb.vid[1], |
357 | vlocation->vldb.vid[2]); | 211 | vl->vldb.vid[2]); |
358 | 212 | ||
359 | if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { | 213 | if (vl->vldb.vidmask & AFS_VOL_VTM_RW) { |
360 | vid = vlocation->vldb.vid[0]; | 214 | vid = vl->vldb.vid[0]; |
361 | voltype = AFSVL_RWVOL; | 215 | voltype = AFSVL_RWVOL; |
362 | } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { | 216 | } else if (vl->vldb.vidmask & AFS_VOL_VTM_RO) { |
363 | vid = vlocation->vldb.vid[1]; | 217 | vid = vl->vldb.vid[1]; |
364 | voltype = AFSVL_ROVOL; | 218 | voltype = AFSVL_ROVOL; |
365 | } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { | 219 | } else if (vl->vldb.vidmask & AFS_VOL_VTM_BAK) { |
366 | vid = vlocation->vldb.vid[2]; | 220 | vid = vl->vldb.vid[2]; |
367 | voltype = AFSVL_BACKVOL; | 221 | voltype = AFSVL_BACKVOL; |
368 | } else { | 222 | } else { |
369 | BUG(); | 223 | BUG(); |
@@ -371,551 +225,482 @@ found_in_cache: | |||
371 | voltype = 0; | 225 | voltype = 0; |
372 | } | 226 | } |
373 | 227 | ||
374 | ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb); | 228 | /* contact the server to make sure the volume is still available |
229 | * - TODO: need to handle disconnected operation here | ||
230 | */ | ||
231 | ret = afs_vlocation_access_vl_by_id(vl, vid, voltype, vldb); | ||
375 | switch (ret) { | 232 | switch (ret) { |
376 | /* net error */ | 233 | /* net error */ |
377 | default: | 234 | default: |
378 | printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n", | 235 | printk(KERN_WARNING "kAFS:" |
379 | namesz, namesz, name, vid, cell->name, ret); | 236 | " failed to update volume '%s' (%x) up in '%s': %d\n", |
380 | goto error; | 237 | vl->vldb.name, vid, vl->cell->name, ret); |
238 | _leave(" = %d", ret); | ||
239 | return ret; | ||
381 | 240 | ||
382 | /* pulled from local cache into memory */ | 241 | /* pulled from local cache into memory */ |
383 | case 0: | 242 | case 0: |
384 | goto found_on_vlserver; | 243 | _leave(" = 0"); |
244 | return 0; | ||
385 | 245 | ||
386 | /* uh oh... looks like the volume got deleted */ | 246 | /* uh oh... looks like the volume got deleted */ |
387 | case -ENOMEDIUM: | 247 | case -ENOMEDIUM: |
388 | printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n", | 248 | printk(KERN_ERR "kAFS:" |
389 | namesz, namesz, name, vid, cell->name); | 249 | " volume '%s' (%x) does not exist '%s'\n", |
250 | vl->vldb.name, vid, vl->cell->name); | ||
390 | 251 | ||
391 | /* TODO: make existing record unavailable */ | 252 | /* TODO: make existing record unavailable */ |
392 | goto error; | 253 | _leave(" = %d", ret); |
254 | return ret; | ||
393 | } | 255 | } |
256 | } | ||
394 | 257 | ||
395 | found_on_vlserver: | 258 | /* |
396 | _debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }", | 259 | * apply the update to a VL record |
397 | namesz, namesz, name, | 260 | */ |
398 | vldb.vidmask, | 261 | static void afs_vlocation_apply_update(struct afs_vlocation *vl, |
399 | ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], | 262 | struct afs_cache_vlocation *vldb) |
400 | ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], | 263 | { |
401 | ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] | 264 | _debug("Done VL Lookup: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", |
402 | ); | 265 | vldb->name, vldb->vidmask, |
403 | 266 | ntohl(vldb->servers[0].s_addr), vldb->srvtmask[0], | |
404 | _debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]); | 267 | ntohl(vldb->servers[1].s_addr), vldb->srvtmask[1], |
268 | ntohl(vldb->servers[2].s_addr), vldb->srvtmask[2]); | ||
405 | 269 | ||
406 | if ((namesz < sizeof(vlocation->vldb.name) && | 270 | _debug("Vids: %08x %08x %08x", |
407 | vlocation->vldb.name[namesz] != '\0') || | 271 | vldb->vid[0], vldb->vid[1], vldb->vid[2]); |
408 | memcmp(vldb.name, name, namesz) != 0) | ||
409 | printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n", | ||
410 | namesz, namesz, name, vldb.name); | ||
411 | 272 | ||
412 | memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); | 273 | if (strcmp(vldb->name, vl->vldb.name) != 0) |
274 | printk(KERN_NOTICE "kAFS:" | ||
275 | " name of volume '%s' changed to '%s' on server\n", | ||
276 | vl->vldb.name, vldb->name); | ||
413 | 277 | ||
414 | afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ); | 278 | vl->vldb = *vldb; |
415 | 279 | ||
416 | #ifdef AFS_CACHING_SUPPORT | 280 | #ifdef AFS_CACHING_SUPPORT |
417 | /* update volume entry in local cache */ | 281 | /* update volume entry in local cache */ |
418 | cachefs_update_cookie(vlocation->cache); | 282 | cachefs_update_cookie(vl->cache); |
419 | #endif | ||
420 | |||
421 | *_vlocation = vlocation; | ||
422 | _leave(" = 0 (%p)",vlocation); | ||
423 | return 0; | ||
424 | |||
425 | error: | ||
426 | if (vlocation) { | ||
427 | if (active) { | ||
428 | __afs_put_vlocation(vlocation); | ||
429 | } else { | ||
430 | list_del(&vlocation->link); | ||
431 | #ifdef AFS_CACHING_SUPPORT | ||
432 | cachefs_relinquish_cookie(vlocation->cache, 0); | ||
433 | #endif | 283 | #endif |
434 | afs_put_cell(vlocation->cell); | ||
435 | kfree(vlocation); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | _leave(" = %d", ret); | ||
440 | return ret; | ||
441 | } | 284 | } |
442 | 285 | ||
443 | /* | 286 | /* |
444 | * finish using a volume location record | 287 | * fill in a volume location record, consulting the cache and the VL server |
445 | * - caller must have cell->vol_sem write-locked | 288 | * both |
446 | */ | 289 | */ |
447 | static void __afs_put_vlocation(struct afs_vlocation *vlocation) | 290 | static int afs_vlocation_fill_in_record(struct afs_vlocation *vl) |
448 | { | 291 | { |
449 | struct afs_cell *cell; | 292 | struct afs_cache_vlocation vldb; |
293 | int ret; | ||
450 | 294 | ||
451 | if (!vlocation) | 295 | _enter(""); |
452 | return; | ||
453 | 296 | ||
454 | _enter("%s", vlocation->vldb.name); | 297 | ASSERTCMP(vl->valid, ==, 0); |
455 | 298 | ||
456 | cell = vlocation->cell; | 299 | memset(&vldb, 0, sizeof(vldb)); |
457 | 300 | ||
458 | /* sanity check */ | 301 | /* see if we have an in-cache copy (will set vl->valid if there is) */ |
459 | BUG_ON(atomic_read(&vlocation->usage) <= 0); | 302 | #ifdef AFS_CACHING_SUPPORT |
303 | cachefs_acquire_cookie(cell->cache, | ||
304 | &afs_volume_cache_index_def, | ||
305 | vlocation, | ||
306 | &vl->cache); | ||
307 | #endif | ||
460 | 308 | ||
461 | spin_lock(&cell->vl_gylock); | 309 | if (vl->valid) { |
462 | if (likely(!atomic_dec_and_test(&vlocation->usage))) { | 310 | /* try to update a known volume in the cell VL databases by |
463 | spin_unlock(&cell->vl_gylock); | 311 | * ID as the name may have changed */ |
464 | _leave(""); | 312 | _debug("found in cache"); |
465 | return; | 313 | ret = afs_vlocation_update_record(vl, &vldb); |
314 | } else { | ||
315 | /* try to look up an unknown volume in the cell VL databases by | ||
316 | * name */ | ||
317 | ret = afs_vlocation_access_vl_by_name(vl, &vldb); | ||
318 | if (ret < 0) { | ||
319 | printk("kAFS: failed to locate '%s' in cell '%s'\n", | ||
320 | vl->vldb.name, vl->cell->name); | ||
321 | return ret; | ||
322 | } | ||
466 | } | 323 | } |
467 | 324 | ||
468 | /* move to graveyard queue */ | 325 | afs_vlocation_apply_update(vl, &vldb); |
469 | list_move_tail(&vlocation->link,&cell->vl_graveyard); | 326 | _leave(" = 0"); |
470 | 327 | return 0; | |
471 | /* remove from pending timeout queue (refcounted if actually being | ||
472 | * updated) */ | ||
473 | list_del_init(&vlocation->upd_op.link); | ||
474 | |||
475 | /* time out in 10 secs */ | ||
476 | afs_kafstimod_del_timer(&vlocation->upd_timer); | ||
477 | afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ); | ||
478 | |||
479 | spin_unlock(&cell->vl_gylock); | ||
480 | |||
481 | _leave(" [killed]"); | ||
482 | } | 328 | } |
483 | 329 | ||
484 | /* | 330 | /* |
485 | * finish using a volume location record | 331 | * queue a vlocation record for updates |
486 | */ | 332 | */ |
487 | void afs_put_vlocation(struct afs_vlocation *vlocation) | 333 | void afs_vlocation_queue_for_updates(struct afs_vlocation *vl) |
488 | { | 334 | { |
489 | if (vlocation) { | 335 | struct afs_vlocation *xvl; |
490 | struct afs_cell *cell = vlocation->cell; | ||
491 | 336 | ||
492 | down_write(&cell->vl_sem); | 337 | /* wait at least 10 minutes before updating... */ |
493 | __afs_put_vlocation(vlocation); | 338 | vl->update_at = get_seconds() + afs_vlocation_update_timeout; |
494 | up_write(&cell->vl_sem); | 339 | |
340 | spin_lock(&afs_vlocation_updates_lock); | ||
341 | |||
342 | if (!list_empty(&afs_vlocation_updates)) { | ||
343 | /* ... but wait at least 1 second more than the newest record | ||
344 | * already queued so that we don't spam the VL server suddenly | ||
345 | * with lots of requests | ||
346 | */ | ||
347 | xvl = list_entry(afs_vlocation_updates.prev, | ||
348 | struct afs_vlocation, update); | ||
349 | if (vl->update_at <= xvl->update_at) | ||
350 | vl->update_at = xvl->update_at + 1; | ||
351 | } else { | ||
352 | queue_delayed_work(afs_vlocation_update_worker, | ||
353 | &afs_vlocation_update, | ||
354 | afs_vlocation_update_timeout * HZ); | ||
495 | } | 355 | } |
356 | |||
357 | list_add_tail(&vl->update, &afs_vlocation_updates); | ||
358 | spin_unlock(&afs_vlocation_updates_lock); | ||
496 | } | 359 | } |
497 | 360 | ||
498 | /* | 361 | /* |
499 | * timeout vlocation record | 362 | * lookup volume location |
500 | * - removes from the cell's graveyard if the usage count is zero | 363 | * - iterate through the VL servers in a cell until one of them admits knowing |
364 | * about the volume in question | ||
365 | * - lookup in the local cache if not able to find on the VL server | ||
366 | * - insert/update in the local cache if did get a VL response | ||
501 | */ | 367 | */ |
502 | void afs_vlocation_do_timeout(struct afs_vlocation *vlocation) | 368 | struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell, |
369 | const char *name, | ||
370 | size_t namesz) | ||
503 | { | 371 | { |
504 | struct afs_cell *cell; | 372 | struct afs_vlocation *vl; |
505 | 373 | int ret; | |
506 | _enter("%s", vlocation->vldb.name); | ||
507 | |||
508 | cell = vlocation->cell; | ||
509 | 374 | ||
510 | BUG_ON(atomic_read(&vlocation->usage) < 0); | 375 | _enter("{%s},%*.*s,%zu", |
376 | cell->name, (int) namesz, (int) namesz, name, namesz); | ||
511 | 377 | ||
512 | /* remove from graveyard if still dead */ | 378 | if (namesz > sizeof(vl->vldb.name)) { |
513 | spin_lock(&cell->vl_gylock); | 379 | _leave(" = -ENAMETOOLONG"); |
514 | if (atomic_read(&vlocation->usage) == 0) | 380 | return ERR_PTR(-ENAMETOOLONG); |
515 | list_del_init(&vlocation->link); | 381 | } |
516 | else | ||
517 | vlocation = NULL; | ||
518 | spin_unlock(&cell->vl_gylock); | ||
519 | 382 | ||
520 | if (!vlocation) { | 383 | /* see if we have an in-memory copy first */ |
521 | _leave(""); | 384 | down_write(&cell->vl_sem); |
522 | return; /* resurrected */ | 385 | spin_lock(&cell->vl_lock); |
386 | list_for_each_entry(vl, &cell->vl_list, link) { | ||
387 | if (vl->vldb.name[namesz] != '\0') | ||
388 | continue; | ||
389 | if (memcmp(vl->vldb.name, name, namesz) == 0) | ||
390 | goto found_in_memory; | ||
523 | } | 391 | } |
392 | spin_unlock(&cell->vl_lock); | ||
524 | 393 | ||
525 | /* we can now destroy it properly */ | 394 | /* not in the cell's in-memory lists - create a new record */ |
526 | #ifdef AFS_CACHING_SUPPORT | 395 | vl = afs_vlocation_alloc(cell, name, namesz); |
527 | cachefs_relinquish_cookie(vlocation->cache, 0); | 396 | if (!vl) { |
528 | #endif | 397 | up_write(&cell->vl_sem); |
529 | afs_put_cell(cell); | 398 | return ERR_PTR(-ENOMEM); |
399 | } | ||
530 | 400 | ||
531 | kfree(vlocation); | 401 | afs_get_cell(cell); |
532 | 402 | ||
533 | _leave(" [destroyed]"); | 403 | list_add_tail(&vl->link, &cell->vl_list); |
534 | } | 404 | vl->state = AFS_VL_CREATING; |
405 | up_write(&cell->vl_sem); | ||
535 | 406 | ||
536 | /* | 407 | fill_in_record: |
537 | * send an update operation to the currently selected server | 408 | ret = afs_vlocation_fill_in_record(vl); |
538 | */ | 409 | if (ret < 0) |
539 | static int afs_vlocation_update_begin(struct afs_vlocation *vlocation) | 410 | goto error_abandon; |
540 | { | 411 | vl->state = AFS_VL_VALID; |
541 | afs_voltype_t voltype; | 412 | wake_up(&vl->waitq); |
542 | afs_volid_t vid; | ||
543 | int ret; | ||
544 | 413 | ||
545 | _enter("%s{ufs=%u ucs=%u}", | 414 | /* schedule for regular updates */ |
546 | vlocation->vldb.name, | 415 | afs_vlocation_queue_for_updates(vl); |
547 | vlocation->upd_first_svix, | 416 | goto success; |
548 | vlocation->upd_curr_svix); | ||
549 | 417 | ||
550 | /* try to look up a cached volume in the cell VL databases by ID */ | 418 | found_in_memory: |
551 | if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { | 419 | /* found in memory */ |
552 | vid = vlocation->vldb.vid[0]; | 420 | _debug("found in memory"); |
553 | voltype = AFSVL_RWVOL; | 421 | atomic_inc(&vl->usage); |
554 | } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { | 422 | spin_unlock(&cell->vl_lock); |
555 | vid = vlocation->vldb.vid[1]; | 423 | if (!list_empty(&vl->grave)) { |
556 | voltype = AFSVL_ROVOL; | 424 | spin_lock(&afs_vlocation_graveyard_lock); |
557 | } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { | 425 | list_del_init(&vl->grave); |
558 | vid = vlocation->vldb.vid[2]; | 426 | spin_unlock(&afs_vlocation_graveyard_lock); |
559 | voltype = AFSVL_BACKVOL; | ||
560 | } else { | ||
561 | BUG(); | ||
562 | vid = 0; | ||
563 | voltype = 0; | ||
564 | } | 427 | } |
428 | up_write(&cell->vl_sem); | ||
565 | 429 | ||
566 | /* contact the chosen server */ | 430 | /* see if it was an abandoned record that we might try filling in */ |
567 | ret = afs_server_lookup( | 431 | while (vl->state != AFS_VL_VALID) { |
568 | vlocation->cell, | 432 | afs_vlocation_state_t state = vl->state; |
569 | &vlocation->cell->vl_addrs[vlocation->upd_curr_svix], | ||
570 | &vlocation->upd_op.server); | ||
571 | 433 | ||
572 | switch (ret) { | 434 | _debug("invalid [state %d]", state); |
573 | case 0: | ||
574 | break; | ||
575 | case -ENOMEM: | ||
576 | case -ENONET: | ||
577 | default: | ||
578 | _leave(" = %d", ret); | ||
579 | return ret; | ||
580 | } | ||
581 | 435 | ||
582 | /* initiate the update operation */ | 436 | if ((state == AFS_VL_NEW || state == AFS_VL_NO_VOLUME)) { |
583 | ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype); | 437 | if (cmpxchg(&vl->state, state, AFS_VL_CREATING) == |
584 | if (ret < 0) { | 438 | state) |
585 | _leave(" = %d", ret); | 439 | goto fill_in_record; |
586 | return ret; | 440 | continue; |
441 | } | ||
442 | |||
443 | /* must now wait for creation or update by someone else to | ||
444 | * complete */ | ||
445 | _debug("wait"); | ||
446 | |||
447 | ret = wait_event_interruptible( | ||
448 | vl->waitq, | ||
449 | vl->state == AFS_VL_NEW || | ||
450 | vl->state == AFS_VL_VALID || | ||
451 | vl->state == AFS_VL_NO_VOLUME); | ||
452 | if (ret < 0) | ||
453 | goto error; | ||
587 | } | 454 | } |
588 | 455 | ||
456 | success: | ||
457 | _leave(" = %p",vl); | ||
458 | return vl; | ||
459 | |||
460 | error_abandon: | ||
461 | vl->state = AFS_VL_NEW; | ||
462 | wake_up(&vl->waitq); | ||
463 | error: | ||
464 | ASSERT(vl != NULL); | ||
465 | afs_put_vlocation(vl); | ||
589 | _leave(" = %d", ret); | 466 | _leave(" = %d", ret); |
590 | return ret; | 467 | return ERR_PTR(ret); |
591 | } | 468 | } |
592 | 469 | ||
593 | /* | 470 | /* |
594 | * abandon updating a VL record | 471 | * finish using a volume location record |
595 | * - does not restart the update timer | ||
596 | */ | 472 | */ |
597 | static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation, | 473 | void afs_put_vlocation(struct afs_vlocation *vl) |
598 | afs_vlocation_upd_t state, | ||
599 | int ret) | ||
600 | { | 474 | { |
601 | _enter("%s,%u", vlocation->vldb.name, state); | 475 | if (!vl) |
602 | 476 | return; | |
603 | if (ret < 0) | ||
604 | printk("kAFS: Abandoning VL update '%s': %d\n", | ||
605 | vlocation->vldb.name, ret); | ||
606 | |||
607 | /* discard the server record */ | ||
608 | afs_put_server(vlocation->upd_op.server); | ||
609 | vlocation->upd_op.server = NULL; | ||
610 | 477 | ||
611 | spin_lock(&afs_vlocation_update_lock); | 478 | _enter("%s", vl->vldb.name); |
612 | afs_vlocation_update = NULL; | ||
613 | vlocation->upd_state = state; | ||
614 | 479 | ||
615 | /* TODO: start updating next VL record on pending list */ | 480 | ASSERTCMP(atomic_read(&vl->usage), >, 0); |
616 | 481 | ||
617 | spin_unlock(&afs_vlocation_update_lock); | 482 | if (likely(!atomic_dec_and_test(&vl->usage))) { |
483 | _leave(""); | ||
484 | return; | ||
485 | } | ||
618 | 486 | ||
619 | _leave(""); | 487 | spin_lock(&afs_vlocation_graveyard_lock); |
488 | if (atomic_read(&vl->usage) == 0) { | ||
489 | _debug("buried"); | ||
490 | list_move_tail(&vl->grave, &afs_vlocation_graveyard); | ||
491 | vl->time_of_death = get_seconds(); | ||
492 | schedule_delayed_work(&afs_vlocation_reap, | ||
493 | afs_vlocation_timeout * HZ); | ||
494 | |||
495 | /* suspend updates on this record */ | ||
496 | if (!list_empty(&vl->update)) { | ||
497 | spin_lock(&afs_vlocation_updates_lock); | ||
498 | list_del_init(&vl->update); | ||
499 | spin_unlock(&afs_vlocation_updates_lock); | ||
500 | } | ||
501 | } | ||
502 | spin_unlock(&afs_vlocation_graveyard_lock); | ||
503 | _leave(" [killed?]"); | ||
620 | } | 504 | } |
621 | 505 | ||
622 | /* | 506 | /* |
623 | * handle periodic update timeouts and busy retry timeouts | 507 | * destroy a dead volume location record |
624 | * - called from kafstimod | ||
625 | */ | 508 | */ |
626 | static void afs_vlocation_update_timer(struct afs_timer *timer) | 509 | static void afs_vlocation_destroy(struct afs_vlocation *vl) |
627 | { | 510 | { |
628 | struct afs_vlocation *vlocation = | 511 | _enter("%p", vl); |
629 | list_entry(timer, struct afs_vlocation, upd_timer); | ||
630 | int ret; | ||
631 | 512 | ||
632 | _enter("%s", vlocation->vldb.name); | 513 | #ifdef AFS_CACHING_SUPPORT |
514 | cachefs_relinquish_cookie(vl->cache, 0); | ||
515 | #endif | ||
633 | 516 | ||
634 | /* only update if not in the graveyard (defend against putting too) */ | 517 | afs_put_cell(vl->cell); |
635 | spin_lock(&vlocation->cell->vl_gylock); | 518 | kfree(vl); |
519 | } | ||
520 | |||
521 | /* | ||
522 | * reap dead volume location records | ||
523 | */ | ||
524 | static void afs_vlocation_reaper(struct work_struct *work) | ||
525 | { | ||
526 | LIST_HEAD(corpses); | ||
527 | struct afs_vlocation *vl; | ||
528 | unsigned long delay, expiry; | ||
529 | time_t now; | ||
636 | 530 | ||
637 | if (!atomic_read(&vlocation->usage)) | 531 | _enter(""); |
638 | goto out_unlock1; | ||
639 | 532 | ||
640 | spin_lock(&afs_vlocation_update_lock); | 533 | now = get_seconds(); |
534 | spin_lock(&afs_vlocation_graveyard_lock); | ||
535 | |||
536 | while (!list_empty(&afs_vlocation_graveyard)) { | ||
537 | vl = list_entry(afs_vlocation_graveyard.next, | ||
538 | struct afs_vlocation, grave); | ||
539 | |||
540 | _debug("check %p", vl); | ||
541 | |||
542 | /* the queue is ordered most dead first */ | ||
543 | expiry = vl->time_of_death + afs_vlocation_timeout; | ||
544 | if (expiry > now) { | ||
545 | delay = (expiry - now) * HZ; | ||
546 | _debug("delay %lu", delay); | ||
547 | if (!schedule_delayed_work(&afs_vlocation_reap, | ||
548 | delay)) { | ||
549 | cancel_delayed_work(&afs_vlocation_reap); | ||
550 | schedule_delayed_work(&afs_vlocation_reap, | ||
551 | delay); | ||
552 | } | ||
553 | break; | ||
554 | } | ||
641 | 555 | ||
642 | /* if we were woken up due to EBUSY sleep then restart immediately if | 556 | spin_lock(&vl->cell->vl_lock); |
643 | * possible or else jump to front of pending queue */ | 557 | if (atomic_read(&vl->usage) > 0) { |
644 | if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) { | 558 | _debug("no reap"); |
645 | if (afs_vlocation_update) { | 559 | list_del_init(&vl->grave); |
646 | list_add(&vlocation->upd_op.link, | ||
647 | &afs_vlocation_update_pendq); | ||
648 | } else { | 560 | } else { |
649 | afs_get_vlocation(vlocation); | 561 | _debug("reap"); |
650 | afs_vlocation_update = vlocation; | 562 | list_move_tail(&vl->grave, &corpses); |
651 | vlocation->upd_state = AFS_VLUPD_INPROGRESS; | 563 | list_del_init(&vl->link); |
652 | } | 564 | } |
653 | goto out_unlock2; | 565 | spin_unlock(&vl->cell->vl_lock); |
654 | } | 566 | } |
655 | 567 | ||
656 | /* put on pending queue if there's already another update in progress */ | 568 | spin_unlock(&afs_vlocation_graveyard_lock); |
657 | if (afs_vlocation_update) { | ||
658 | vlocation->upd_state = AFS_VLUPD_PENDING; | ||
659 | list_add_tail(&vlocation->upd_op.link, | ||
660 | &afs_vlocation_update_pendq); | ||
661 | goto out_unlock2; | ||
662 | } | ||
663 | 569 | ||
664 | /* hold a ref on it while actually updating */ | 570 | /* now reap the corpses we've extracted */ |
665 | afs_get_vlocation(vlocation); | 571 | while (!list_empty(&corpses)) { |
666 | afs_vlocation_update = vlocation; | 572 | vl = list_entry(corpses.next, struct afs_vlocation, grave); |
667 | vlocation->upd_state = AFS_VLUPD_INPROGRESS; | 573 | list_del(&vl->grave); |
668 | 574 | afs_vlocation_destroy(vl); | |
669 | spin_unlock(&afs_vlocation_update_lock); | ||
670 | spin_unlock(&vlocation->cell->vl_gylock); | ||
671 | |||
672 | /* okay... we can start the update */ | ||
673 | _debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name); | ||
674 | vlocation->upd_first_svix = vlocation->cell->vl_curr_svix; | ||
675 | vlocation->upd_curr_svix = vlocation->upd_first_svix; | ||
676 | vlocation->upd_rej_cnt = 0; | ||
677 | vlocation->upd_busy_cnt = 0; | ||
678 | |||
679 | ret = afs_vlocation_update_begin(vlocation); | ||
680 | if (ret < 0) { | ||
681 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); | ||
682 | afs_kafstimod_add_timer(&vlocation->upd_timer, | ||
683 | AFS_VLDB_TIMEOUT); | ||
684 | afs_put_vlocation(vlocation); | ||
685 | } | 575 | } |
686 | 576 | ||
687 | _leave(""); | 577 | _leave(""); |
688 | return; | 578 | } |
689 | 579 | ||
690 | out_unlock2: | 580 | /* |
691 | spin_unlock(&afs_vlocation_update_lock); | 581 | * initialise the VL update process |
692 | out_unlock1: | 582 | */ |
693 | spin_unlock(&vlocation->cell->vl_gylock); | 583 | int __init afs_vlocation_update_init(void) |
694 | _leave(""); | 584 | { |
585 | afs_vlocation_update_worker = | ||
586 | create_singlethread_workqueue("kafs_vlupdated"); | ||
587 | return afs_vlocation_update_worker ? 0 : -ENOMEM; | ||
588 | } | ||
589 | |||
590 | /* | ||
591 | * discard all the volume location records for rmmod | ||
592 | */ | ||
593 | void __exit afs_vlocation_purge(void) | ||
594 | { | ||
595 | afs_vlocation_timeout = 0; | ||
596 | |||
597 | spin_lock(&afs_vlocation_updates_lock); | ||
598 | list_del_init(&afs_vlocation_updates); | ||
599 | spin_unlock(&afs_vlocation_updates_lock); | ||
600 | cancel_delayed_work(&afs_vlocation_update); | ||
601 | queue_delayed_work(afs_vlocation_update_worker, | ||
602 | &afs_vlocation_update, 0); | ||
603 | destroy_workqueue(afs_vlocation_update_worker); | ||
604 | |||
605 | cancel_delayed_work(&afs_vlocation_reap); | ||
606 | schedule_delayed_work(&afs_vlocation_reap, 0); | ||
695 | } | 607 | } |
696 | 608 | ||
697 | /* | 609 | /* |
698 | * attend to an update operation upon which an event happened | 610 | * update a volume location |
699 | * - called in kafsasyncd context | ||
700 | */ | 611 | */ |
701 | static void afs_vlocation_update_attend(struct afs_async_op *op) | 612 | static void afs_vlocation_updater(struct work_struct *work) |
702 | { | 613 | { |
703 | struct afs_cache_vlocation vldb; | 614 | struct afs_cache_vlocation vldb; |
704 | struct afs_vlocation *vlocation = | 615 | struct afs_vlocation *vl, *xvl; |
705 | list_entry(op, struct afs_vlocation, upd_op); | 616 | time_t now; |
706 | unsigned tmp; | 617 | long timeout; |
707 | int ret; | 618 | int ret; |
708 | 619 | ||
709 | _enter("%s", vlocation->vldb.name); | 620 | _enter(""); |
710 | |||
711 | ret = afs_rxvl_get_entry_by_id_async2(op, &vldb); | ||
712 | switch (ret) { | ||
713 | case -EAGAIN: | ||
714 | _leave(" [unfinished]"); | ||
715 | return; | ||
716 | |||
717 | case 0: | ||
718 | _debug("END VL UPDATE: %d\n", ret); | ||
719 | vlocation->valid = 1; | ||
720 | |||
721 | _debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }", | ||
722 | vldb.vidmask, | ||
723 | ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], | ||
724 | ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], | ||
725 | ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] | ||
726 | ); | ||
727 | |||
728 | _debug("Vids: %08x %08x %08x", | ||
729 | vldb.vid[0], vldb.vid[1], vldb.vid[2]); | ||
730 | |||
731 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); | ||
732 | |||
733 | down_write(&vlocation->cell->vl_sem); | ||
734 | |||
735 | /* actually update the cache */ | ||
736 | if (strncmp(vldb.name, vlocation->vldb.name, | ||
737 | sizeof(vlocation->vldb.name)) != 0) | ||
738 | printk("kAFS: name of volume '%s'" | ||
739 | " changed to '%s' on server\n", | ||
740 | vlocation->vldb.name, vldb.name); | ||
741 | |||
742 | memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); | ||
743 | |||
744 | #if 0 | ||
745 | /* TODO update volume entry in local cache */ | ||
746 | #endif | ||
747 | |||
748 | up_write(&vlocation->cell->vl_sem); | ||
749 | |||
750 | if (ret < 0) | ||
751 | printk("kAFS: failed to update local cache: %d\n", ret); | ||
752 | |||
753 | afs_kafstimod_add_timer(&vlocation->upd_timer, | ||
754 | AFS_VLDB_TIMEOUT); | ||
755 | afs_put_vlocation(vlocation); | ||
756 | _leave(" [found]"); | ||
757 | return; | ||
758 | |||
759 | case -ENOMEDIUM: | ||
760 | vlocation->upd_rej_cnt++; | ||
761 | goto try_next; | ||
762 | |||
763 | /* the server is locked - retry in a very short while */ | ||
764 | case -EBUSY: | ||
765 | vlocation->upd_busy_cnt++; | ||
766 | if (vlocation->upd_busy_cnt > 3) | ||
767 | goto try_next; /* too many retries */ | ||
768 | |||
769 | afs_vlocation_update_abandon(vlocation, | ||
770 | AFS_VLUPD_BUSYSLEEP, 0); | ||
771 | afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2); | ||
772 | afs_put_vlocation(vlocation); | ||
773 | _leave(" [busy]"); | ||
774 | return; | ||
775 | |||
776 | case -ENETUNREACH: | ||
777 | case -EHOSTUNREACH: | ||
778 | case -ECONNREFUSED: | ||
779 | case -EREMOTEIO: | ||
780 | /* record bad vlserver info in the cell too | ||
781 | * - TODO: use down_write_trylock() if available | ||
782 | */ | ||
783 | if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix) | ||
784 | vlocation->cell->vl_curr_svix = | ||
785 | vlocation->cell->vl_curr_svix % | ||
786 | vlocation->cell->vl_naddrs; | ||
787 | |||
788 | case -EBADRQC: | ||
789 | case -EINVAL: | ||
790 | case -EACCES: | ||
791 | case -EBADMSG: | ||
792 | goto try_next; | ||
793 | |||
794 | default: | ||
795 | goto abandon; | ||
796 | } | ||
797 | |||
798 | /* try contacting the next server */ | ||
799 | try_next: | ||
800 | vlocation->upd_busy_cnt = 0; | ||
801 | |||
802 | /* discard the server record */ | ||
803 | afs_put_server(vlocation->upd_op.server); | ||
804 | vlocation->upd_op.server = NULL; | ||
805 | 621 | ||
806 | tmp = vlocation->cell->vl_naddrs; | 622 | now = get_seconds(); |
807 | if (tmp == 0) | ||
808 | goto abandon; | ||
809 | 623 | ||
810 | vlocation->upd_curr_svix++; | 624 | /* find a record to update */ |
811 | if (vlocation->upd_curr_svix >= tmp) | 625 | spin_lock(&afs_vlocation_updates_lock); |
812 | vlocation->upd_curr_svix = 0; | 626 | for (;;) { |
813 | if (vlocation->upd_first_svix >= tmp) | 627 | if (list_empty(&afs_vlocation_updates)) { |
814 | vlocation->upd_first_svix = tmp - 1; | 628 | spin_unlock(&afs_vlocation_updates_lock); |
629 | _leave(" [nothing]"); | ||
630 | return; | ||
631 | } | ||
815 | 632 | ||
816 | /* move to the next server */ | 633 | vl = list_entry(afs_vlocation_updates.next, |
817 | if (vlocation->upd_curr_svix != vlocation->upd_first_svix) { | 634 | struct afs_vlocation, update); |
818 | afs_vlocation_update_begin(vlocation); | 635 | if (atomic_read(&vl->usage) > 0) |
819 | _leave(" [next]"); | 636 | break; |
820 | return; | 637 | list_del_init(&vl->update); |
821 | } | 638 | } |
822 | 639 | ||
823 | /* run out of servers to try - was the volume rejected? */ | 640 | timeout = vl->update_at - now; |
824 | if (vlocation->upd_rej_cnt > 0) { | 641 | if (timeout > 0) { |
825 | printk("kAFS: Active volume no longer valid '%s'\n", | 642 | queue_delayed_work(afs_vlocation_update_worker, |
826 | vlocation->vldb.name); | 643 | &afs_vlocation_update, timeout * HZ); |
827 | vlocation->valid = 0; | 644 | spin_unlock(&afs_vlocation_updates_lock); |
828 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); | 645 | _leave(" [nothing]"); |
829 | afs_kafstimod_add_timer(&vlocation->upd_timer, | ||
830 | AFS_VLDB_TIMEOUT); | ||
831 | afs_put_vlocation(vlocation); | ||
832 | _leave(" [invalidated]"); | ||
833 | return; | 646 | return; |
834 | } | 647 | } |
835 | 648 | ||
836 | /* abandon the update */ | 649 | list_del_init(&vl->update); |
837 | abandon: | 650 | atomic_inc(&vl->usage); |
838 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); | 651 | spin_unlock(&afs_vlocation_updates_lock); |
839 | afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10); | ||
840 | afs_put_vlocation(vlocation); | ||
841 | _leave(" [abandoned]"); | ||
842 | } | ||
843 | 652 | ||
844 | /* | 653 | /* we can now perform the update */ |
845 | * deal with an update operation being discarded | 654 | _debug("update %s", vl->vldb.name); |
846 | * - called in kafsasyncd context when it's dying due to rmmod | 655 | vl->state = AFS_VL_UPDATING; |
847 | * - the call has already been aborted and put()'d | 656 | vl->upd_rej_cnt = 0; |
848 | */ | 657 | vl->upd_busy_cnt = 0; |
849 | static void afs_vlocation_update_discard(struct afs_async_op *op) | ||
850 | { | ||
851 | struct afs_vlocation *vlocation = | ||
852 | list_entry(op, struct afs_vlocation, upd_op); | ||
853 | |||
854 | _enter("%s", vlocation->vldb.name); | ||
855 | 658 | ||
856 | afs_put_server(op->server); | 659 | ret = afs_vlocation_update_record(vl, &vldb); |
857 | op->server = NULL; | 660 | switch (ret) { |
858 | 661 | case 0: | |
859 | afs_put_vlocation(vlocation); | 662 | afs_vlocation_apply_update(vl, &vldb); |
663 | vl->state = AFS_VL_VALID; | ||
664 | break; | ||
665 | case -ENOMEDIUM: | ||
666 | vl->state = AFS_VL_VOLUME_DELETED; | ||
667 | break; | ||
668 | default: | ||
669 | vl->state = AFS_VL_UNCERTAIN; | ||
670 | break; | ||
671 | } | ||
860 | 672 | ||
861 | _leave(""); | 673 | /* and then reschedule */ |
862 | } | 674 | _debug("reschedule"); |
675 | vl->update_at = get_seconds() + afs_vlocation_update_timeout; | ||
863 | 676 | ||
864 | /* | 677 | spin_lock(&afs_vlocation_updates_lock); |
865 | * match a VLDB record stored in the cache | ||
866 | * - may also load target from entry | ||
867 | */ | ||
868 | #ifdef AFS_CACHING_SUPPORT | ||
869 | static cachefs_match_val_t afs_vlocation_cache_match(void *target, | ||
870 | const void *entry) | ||
871 | { | ||
872 | const struct afs_cache_vlocation *vldb = entry; | ||
873 | struct afs_vlocation *vlocation = target; | ||
874 | |||
875 | _enter("{%s},{%s}", vlocation->vldb.name, vldb->name); | ||
876 | |||
877 | if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0 | ||
878 | ) { | ||
879 | if (!vlocation->valid || | ||
880 | vlocation->vldb.rtime == vldb->rtime | ||
881 | ) { | ||
882 | vlocation->vldb = *vldb; | ||
883 | vlocation->valid = 1; | ||
884 | _leave(" = SUCCESS [c->m]"); | ||
885 | return CACHEFS_MATCH_SUCCESS; | ||
886 | } else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) { | ||
887 | /* delete if VIDs for this name differ */ | ||
888 | if (memcmp(&vlocation->vldb.vid, | ||
889 | &vldb->vid, | ||
890 | sizeof(vldb->vid)) != 0) { | ||
891 | _leave(" = DELETE"); | ||
892 | return CACHEFS_MATCH_SUCCESS_DELETE; | ||
893 | } | ||
894 | 678 | ||
895 | _leave(" = UPDATE"); | 679 | if (!list_empty(&afs_vlocation_updates)) { |
896 | return CACHEFS_MATCH_SUCCESS_UPDATE; | 680 | /* next update in 10 minutes, but wait at least 1 second more |
897 | } else { | 681 | * than the newest record already queued so that we don't spam |
898 | _leave(" = SUCCESS"); | 682 | * the VL server suddenly with lots of requests |
899 | return CACHEFS_MATCH_SUCCESS; | 683 | */ |
900 | } | 684 | xvl = list_entry(afs_vlocation_updates.prev, |
685 | struct afs_vlocation, update); | ||
686 | if (vl->update_at <= xvl->update_at) | ||
687 | vl->update_at = xvl->update_at + 1; | ||
688 | xvl = list_entry(afs_vlocation_updates.next, | ||
689 | struct afs_vlocation, update); | ||
690 | timeout = xvl->update_at - now; | ||
691 | if (timeout < 0) | ||
692 | timeout = 0; | ||
693 | } else { | ||
694 | timeout = afs_vlocation_update_timeout; | ||
901 | } | 695 | } |
902 | 696 | ||
903 | _leave(" = FAILED"); | 697 | ASSERT(list_empty(&vl->update)); |
904 | return CACHEFS_MATCH_FAILED; | ||
905 | } | ||
906 | #endif | ||
907 | 698 | ||
908 | /* | 699 | list_add_tail(&vl->update, &afs_vlocation_updates); |
909 | * update a VLDB record stored in the cache | ||
910 | */ | ||
911 | #ifdef AFS_CACHING_SUPPORT | ||
912 | static void afs_vlocation_cache_update(void *source, void *entry) | ||
913 | { | ||
914 | struct afs_cache_vlocation *vldb = entry; | ||
915 | struct afs_vlocation *vlocation = source; | ||
916 | |||
917 | _enter(""); | ||
918 | 700 | ||
919 | *vldb = vlocation->vldb; | 701 | _debug("timeout %ld", timeout); |
702 | queue_delayed_work(afs_vlocation_update_worker, | ||
703 | &afs_vlocation_update, timeout * HZ); | ||
704 | spin_unlock(&afs_vlocation_updates_lock); | ||
705 | afs_put_vlocation(vl); | ||
920 | } | 706 | } |
921 | #endif | ||