ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / afs / volume.c
1 /* volume.c: AFS volume management
2  *
3  * Copyright (C) 2002 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 License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/module.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 "vnode.h"
20 #include "cell.h"
21 #include "cache.h"
22 #include "cmservice.h"
23 #include "fsclient.h"
24 #include "vlclient.h"
25 #include "internal.h"
26
27 const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
28
29 #ifdef AFS_CACHING_SUPPORT
30 static cachefs_match_val_t afs_volume_cache_match(void *target,
31                                                   const void *entry);
32 static void afs_volume_cache_update(void *source, void *entry);
33
34 struct cachefs_index_def afs_volume_cache_index_def = {
35         .name           = "volume",
36         .data_size      = sizeof(struct afs_cache_vhash),
37         .keys[0]        = { CACHEFS_INDEX_KEYS_BIN, 1 },
38         .keys[1]        = { CACHEFS_INDEX_KEYS_BIN, 1 },
39         .match          = afs_volume_cache_match,
40         .update         = afs_volume_cache_update,
41 };
42 #endif
43
44 /*****************************************************************************/
45 /*
46  * lookup a volume by name
47  * - this can be one of the following:
48  *      "%[cell:]volume[.]"             R/W volume
49  *      "#[cell:]volume[.]"             R/O or R/W volume (rwparent=0),
50  *                                       or R/W (rwparent=1) volume
51  *      "%[cell:]volume.readonly"       R/O volume
52  *      "#[cell:]volume.readonly"       R/O volume
53  *      "%[cell:]volume.backup"         Backup volume
54  *      "#[cell:]volume.backup"         Backup volume
55  *
56  * The cell name is optional, and defaults to the current cell.
57  *
58  * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
59  * Guide
60  * - Rule 1: Explicit type suffix forces access of that type or nothing
61  *           (no suffix, then use Rule 2 & 3)
62  * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
63  *           if not available
64  * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
65  *           explicitly told otherwise
66  */
67 int afs_volume_lookup(const char *name, struct afs_cell *cell, int rwpath,
68                       struct afs_volume **_volume)
69 {
70         struct afs_vlocation *vlocation = NULL;
71         struct afs_volume *volume = NULL;
72         afs_voltype_t type;
73         const char *cellname, *volname, *suffix;
74         char srvtmask;
75         int force, ret, loop, cellnamesz, volnamesz;
76
77         _enter("%s,,%d,", name, rwpath);
78
79         if (!name || (name[0] != '%' && name[0] != '#') || !name[1]) {
80                 printk("kAFS: unparsable volume name\n");
81                 return -EINVAL;
82         }
83
84         /* determine the type of volume we're looking for */
85         force = 0;
86         type = AFSVL_ROVOL;
87
88         if (rwpath || name[0] == '%') {
89                 type = AFSVL_RWVOL;
90                 force = 1;
91         }
92
93         suffix = strrchr(name, '.');
94         if (suffix) {
95                 if (strcmp(suffix, ".readonly") == 0) {
96                         type = AFSVL_ROVOL;
97                         force = 1;
98                 }
99                 else if (strcmp(suffix, ".backup") == 0) {
100                         type = AFSVL_BACKVOL;
101                         force = 1;
102                 }
103                 else if (suffix[1] == 0) {
104                 }
105                 else {
106                         suffix = NULL;
107                 }
108         }
109
110         /* split the cell and volume names */
111         name++;
112         volname = strchr(name, ':');
113         if (volname) {
114                 cellname = name;
115                 cellnamesz = volname - name;
116                 volname++;
117         }
118         else {
119                 volname = name;
120                 cellname = NULL;
121                 cellnamesz = 0;
122         }
123
124         volnamesz = suffix ? suffix - volname : strlen(volname);
125
126         _debug("CELL:%*.*s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s",
127                cellnamesz, cellnamesz, cellname ?: "", cell,
128                volnamesz, volnamesz, volname, suffix ?: "-",
129                type,
130                force ? " FORCE" : "");
131
132         /* lookup the cell record */
133         if (cellname || !cell) {
134                 ret = afs_cell_lookup(cellname, cellnamesz, &cell);
135                 if (ret<0) {
136                         printk("kAFS: unable to lookup cell '%s'\n",
137                                cellname ?: "");
138                         goto error;
139                 }
140         }
141         else {
142                 afs_get_cell(cell);
143         }
144
145         /* lookup the volume location record */
146         ret = afs_vlocation_lookup(cell, volname, volnamesz, &vlocation);
147         if (ret < 0)
148                 goto error;
149
150         /* make the final decision on the type we want */
151         ret = -ENOMEDIUM;
152         if (force && !(vlocation->vldb.vidmask & (1 << type)))
153                 goto error;
154
155         srvtmask = 0;
156         for (loop = 0; loop < vlocation->vldb.nservers; loop++)
157                 srvtmask |= vlocation->vldb.srvtmask[loop];
158
159         if (force) {
160                 if (!(srvtmask & (1 << type)))
161                         goto error;
162         }
163         else if (srvtmask & AFS_VOL_VTM_RO) {
164                 type = AFSVL_ROVOL;
165         }
166         else if (srvtmask & AFS_VOL_VTM_RW) {
167                 type = AFSVL_RWVOL;
168         }
169         else {
170                 goto error;
171         }
172
173         down_write(&cell->vl_sem);
174
175         /* is the volume already active? */
176         if (vlocation->vols[type]) {
177                 /* yes - re-use it */
178                 volume = vlocation->vols[type];
179                 afs_get_volume(volume);
180                 goto success;
181         }
182
183         /* create a new volume record */
184         _debug("creating new volume record");
185
186         ret = -ENOMEM;
187         volume = kmalloc(sizeof(struct afs_volume), GFP_KERNEL);
188         if (!volume)
189                 goto error_up;
190
191         memset(volume, 0, sizeof(struct afs_volume));
192         atomic_set(&volume->usage, 1);
193         volume->type            = type;
194         volume->type_force      = force;
195         volume->cell            = cell;
196         volume->vid             = vlocation->vldb.vid[type];
197
198         init_rwsem(&volume->server_sem);
199
200         /* look up all the applicable server records */
201         for (loop = 0; loop < 8; loop++) {
202                 if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
203                         ret = afs_server_lookup(
204                                 volume->cell,
205                                 &vlocation->vldb.servers[loop],
206                                 &volume->servers[volume->nservers]);
207                         if (ret < 0)
208                                 goto error_discard;
209
210                         volume->nservers++;
211                 }
212         }
213
214         /* attach the cache and volume location */
215 #ifdef AFS_CACHING_SUPPORT
216         cachefs_acquire_cookie(vlocation->cache,
217                                &afs_vnode_cache_index_def,
218                                volume,
219                                &volume->cache);
220 #endif
221
222         afs_get_vlocation(vlocation);
223         volume->vlocation = vlocation;
224
225         vlocation->vols[type] = volume;
226
227  success:
228         _debug("kAFS selected %s volume %08x",
229                afs_voltypes[volume->type], volume->vid);
230         *_volume = volume;
231         ret = 0;
232
233         /* clean up */
234  error_up:
235         up_write(&cell->vl_sem);
236  error:
237         afs_put_vlocation(vlocation);
238         afs_put_cell(cell);
239
240         _leave(" = %d (%p)", ret, volume);
241         return ret;
242
243  error_discard:
244         up_write(&cell->vl_sem);
245
246         for (loop = volume->nservers - 1; loop >= 0; loop--)
247                 afs_put_server(volume->servers[loop]);
248
249         kfree(volume);
250         goto error;
251 } /* end afs_volume_lookup() */
252
253 /*****************************************************************************/
254 /*
255  * destroy a volume record
256  */
257 void afs_put_volume(struct afs_volume *volume)
258 {
259         struct afs_vlocation *vlocation;
260         int loop;
261
262         if (!volume)
263                 return;
264
265         _enter("%p", volume);
266
267         vlocation = volume->vlocation;
268
269         /* sanity check */
270         BUG_ON(atomic_read(&volume->usage) <= 0);
271
272         /* to prevent a race, the decrement and the dequeue must be effectively
273          * atomic */
274         down_write(&vlocation->cell->vl_sem);
275
276         if (likely(!atomic_dec_and_test(&volume->usage))) {
277                 up_write(&vlocation->cell->vl_sem);
278                 _leave("");
279                 return;
280         }
281
282         vlocation->vols[volume->type] = NULL;
283
284         up_write(&vlocation->cell->vl_sem);
285
286         /* finish cleaning up the volume */
287 #ifdef AFS_CACHING_SUPPORT
288         cachefs_relinquish_cookie(volume->cache, 0);
289 #endif
290         afs_put_vlocation(vlocation);
291
292         for (loop = volume->nservers - 1; loop >= 0; loop--)
293                 afs_put_server(volume->servers[loop]);
294
295         kfree(volume);
296
297         _leave(" [destroyed]");
298 } /* end afs_put_volume() */
299
300 /*****************************************************************************/
301 /*
302  * pick a server to use to try accessing this volume
303  * - returns with an elevated usage count on the server chosen
304  */
305 int afs_volume_pick_fileserver(struct afs_volume *volume,
306                                struct afs_server **_server)
307 {
308         struct afs_server *server;
309         int ret, state, loop;
310
311         _enter("%s", volume->vlocation->vldb.name);
312
313         down_read(&volume->server_sem);
314
315         /* handle the no-server case */
316         if (volume->nservers == 0) {
317                 ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
318                 up_read(&volume->server_sem);
319                 _leave(" = %d [no servers]", ret);
320                 return ret;
321         }
322
323         /* basically, just search the list for the first live server and use
324          * that */
325         ret = 0;
326         for (loop = 0; loop < volume->nservers; loop++) {
327                 server = volume->servers[loop];
328                 state = server->fs_state;
329
330                 switch (state) {
331                         /* found an apparently healthy server */
332                 case 0:
333                         afs_get_server(server);
334                         up_read(&volume->server_sem);
335                         *_server = server;
336                         _leave(" = 0 (picked %08x)",
337                                ntohl(server->addr.s_addr));
338                         return 0;
339
340                 case -ENETUNREACH:
341                         if (ret == 0)
342                                 ret = state;
343                         break;
344
345                 case -EHOSTUNREACH:
346                         if (ret == 0 ||
347                             ret == -ENETUNREACH)
348                                 ret = state;
349                         break;
350
351                 case -ECONNREFUSED:
352                         if (ret == 0 ||
353                             ret == -ENETUNREACH ||
354                             ret == -EHOSTUNREACH)
355                                 ret = state;
356                         break;
357
358                 default:
359                 case -EREMOTEIO:
360                         if (ret == 0 ||
361                             ret == -ENETUNREACH ||
362                             ret == -EHOSTUNREACH ||
363                             ret == -ECONNREFUSED)
364                                 ret = state;
365                         break;
366                 }
367         }
368
369         /* no available servers
370          * - TODO: handle the no active servers case better
371          */
372         up_read(&volume->server_sem);
373         _leave(" = %d", ret);
374         return ret;
375 } /* end afs_volume_pick_fileserver() */
376
377 /*****************************************************************************/
378 /*
379  * release a server after use
380  * - releases the ref on the server struct that was acquired by picking
381  * - records result of using a particular server to access a volume
382  * - return 0 to try again, 1 if okay or to issue error
383  */
384 int afs_volume_release_fileserver(struct afs_volume *volume,
385                                   struct afs_server *server,
386                                   int result)
387 {
388         unsigned loop;
389
390         _enter("%s,%08x,%d",
391                volume->vlocation->vldb.name, ntohl(server->addr.s_addr),
392                result);
393
394         switch (result) {
395                 /* success */
396         case 0:
397                 server->fs_act_jif = jiffies;
398                 break;
399
400                 /* the fileserver denied all knowledge of the volume */
401         case -ENOMEDIUM:
402                 server->fs_act_jif = jiffies;
403                 down_write(&volume->server_sem);
404
405                 /* first, find where the server is in the active list (if it
406                  * is) */
407                 for (loop = 0; loop < volume->nservers; loop++)
408                         if (volume->servers[loop] == server)
409                                 goto present;
410
411                 /* no longer there - may have been discarded by another op */
412                 goto try_next_server_upw;
413
414         present:
415                 volume->nservers--;
416                 memmove(&volume->servers[loop],
417                         &volume->servers[loop + 1],
418                         sizeof(volume->servers[loop]) *
419                         (volume->nservers - loop));
420                 volume->servers[volume->nservers] = NULL;
421                 afs_put_server(server);
422                 volume->rjservers++;
423
424                 if (volume->nservers > 0)
425                         /* another server might acknowledge its existence */
426                         goto try_next_server_upw;
427
428                 /* handle the case where all the fileservers have rejected the
429                  * volume
430                  * - TODO: try asking the fileservers for volume information
431                  * - TODO: contact the VL server again to see if the volume is
432                  *         no longer registered
433                  */
434                 up_write(&volume->server_sem);
435                 afs_put_server(server);
436                 _leave(" [completely rejected]");
437                 return 1;
438
439                 /* problem reaching the server */
440         case -ENETUNREACH:
441         case -EHOSTUNREACH:
442         case -ECONNREFUSED:
443         case -ETIMEDOUT:
444         case -EREMOTEIO:
445                 /* mark the server as dead
446                  * TODO: vary dead timeout depending on error
447                  */
448                 spin_lock(&server->fs_lock);
449                 if (!server->fs_state) {
450                         server->fs_dead_jif = jiffies + HZ * 10;
451                         server->fs_state = result;
452                         printk("kAFS: SERVER DEAD state=%d\n", result);
453                 }
454                 spin_unlock(&server->fs_lock);
455                 goto try_next_server;
456
457                 /* miscellaneous error */
458         default:
459                 server->fs_act_jif = jiffies;
460         case -ENOMEM:
461         case -ENONET:
462                 break;
463         }
464
465         /* tell the caller to accept the result */
466         afs_put_server(server);
467         _leave("");
468         return 1;
469
470         /* tell the caller to loop around and try the next server */
471  try_next_server_upw:
472         up_write(&volume->server_sem);
473  try_next_server:
474         afs_put_server(server);
475         _leave(" [try next server]");
476         return 0;
477
478 } /* end afs_volume_release_fileserver() */
479
480 /*****************************************************************************/
481 /*
482  * match a volume hash record stored in the cache
483  */
484 #ifdef AFS_CACHING_SUPPORT
485 static cachefs_match_val_t afs_volume_cache_match(void *target,
486                                                   const void *entry)
487 {
488         const struct afs_cache_vhash *vhash = entry;
489         struct afs_volume *volume = target;
490
491         _enter("{%u},{%u}", volume->type, vhash->vtype);
492
493         if (volume->type == vhash->vtype) {
494                 _leave(" = SUCCESS");
495                 return CACHEFS_MATCH_SUCCESS;
496         }
497
498         _leave(" = FAILED");
499         return CACHEFS_MATCH_FAILED;
500 } /* end afs_volume_cache_match() */
501 #endif
502
503 /*****************************************************************************/
504 /*
505  * update a volume hash record stored in the cache
506  */
507 #ifdef AFS_CACHING_SUPPORT
508 static void afs_volume_cache_update(void *source, void *entry)
509 {
510         struct afs_cache_vhash *vhash = entry;
511         struct afs_volume *volume = source;
512
513         _enter("");
514
515         vhash->vtype = volume->type;
516
517 } /* end afs_volume_cache_update() */
518 #endif