Commit | Line | Data |
---|---|---|
ec26815a | 1 | /* AFS volume management |
1da177e4 | 2 | * |
08e0e7c8 | 3 | * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. |
1da177e4 LT |
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> | |
1da177e4 | 13 | #include <linux/slab.h> |
1da177e4 LT |
14 | #include "internal.h" |
15 | ||
1da177e4 | 16 | static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" }; |
1da177e4 | 17 | |
1da177e4 LT |
18 | /* |
19 | * lookup a volume by name | |
20 | * - this can be one of the following: | |
21 | * "%[cell:]volume[.]" R/W volume | |
22 | * "#[cell:]volume[.]" R/O or R/W volume (rwparent=0), | |
23 | * or R/W (rwparent=1) volume | |
24 | * "%[cell:]volume.readonly" R/O volume | |
25 | * "#[cell:]volume.readonly" R/O volume | |
26 | * "%[cell:]volume.backup" Backup volume | |
27 | * "#[cell:]volume.backup" Backup volume | |
28 | * | |
29 | * The cell name is optional, and defaults to the current cell. | |
30 | * | |
31 | * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin | |
32 | * Guide | |
33 | * - Rule 1: Explicit type suffix forces access of that type or nothing | |
34 | * (no suffix, then use Rule 2 & 3) | |
35 | * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W | |
36 | * if not available | |
37 | * - Rule 3: If parent volume is R/W, then only mount R/W volume unless | |
38 | * explicitly told otherwise | |
39 | */ | |
00d3b7a4 | 40 | struct afs_volume *afs_volume_lookup(struct afs_mount_params *params) |
1da177e4 LT |
41 | { |
42 | struct afs_vlocation *vlocation = NULL; | |
43 | struct afs_volume *volume = NULL; | |
08e0e7c8 | 44 | struct afs_server *server = NULL; |
1da177e4 | 45 | char srvtmask; |
00d3b7a4 | 46 | int ret, loop; |
1da177e4 | 47 | |
00d3b7a4 DH |
48 | _enter("{%*.*s,%d}", |
49 | params->volnamesz, params->volnamesz, params->volname, params->rwpath); | |
1da177e4 LT |
50 | |
51 | /* lookup the volume location record */ | |
f044c884 | 52 | vlocation = afs_vlocation_lookup(params->net, params->cell, params->key, |
00d3b7a4 | 53 | params->volname, params->volnamesz); |
08e0e7c8 DH |
54 | if (IS_ERR(vlocation)) { |
55 | ret = PTR_ERR(vlocation); | |
56 | vlocation = NULL; | |
1da177e4 | 57 | goto error; |
08e0e7c8 | 58 | } |
1da177e4 LT |
59 | |
60 | /* make the final decision on the type we want */ | |
61 | ret = -ENOMEDIUM; | |
00d3b7a4 | 62 | if (params->force && !(vlocation->vldb.vidmask & (1 << params->type))) |
1da177e4 LT |
63 | goto error; |
64 | ||
65 | srvtmask = 0; | |
66 | for (loop = 0; loop < vlocation->vldb.nservers; loop++) | |
67 | srvtmask |= vlocation->vldb.srvtmask[loop]; | |
68 | ||
00d3b7a4 DH |
69 | if (params->force) { |
70 | if (!(srvtmask & (1 << params->type))) | |
1da177e4 | 71 | goto error; |
ec26815a | 72 | } else if (srvtmask & AFS_VOL_VTM_RO) { |
00d3b7a4 | 73 | params->type = AFSVL_ROVOL; |
ec26815a | 74 | } else if (srvtmask & AFS_VOL_VTM_RW) { |
00d3b7a4 | 75 | params->type = AFSVL_RWVOL; |
ec26815a | 76 | } else { |
1da177e4 LT |
77 | goto error; |
78 | } | |
79 | ||
00d3b7a4 | 80 | down_write(¶ms->cell->vl_sem); |
1da177e4 LT |
81 | |
82 | /* is the volume already active? */ | |
00d3b7a4 | 83 | if (vlocation->vols[params->type]) { |
1da177e4 | 84 | /* yes - re-use it */ |
00d3b7a4 | 85 | volume = vlocation->vols[params->type]; |
1da177e4 LT |
86 | afs_get_volume(volume); |
87 | goto success; | |
88 | } | |
89 | ||
90 | /* create a new volume record */ | |
91 | _debug("creating new volume record"); | |
92 | ||
93 | ret = -ENOMEM; | |
f8314dc6 | 94 | volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL); |
1da177e4 LT |
95 | if (!volume) |
96 | goto error_up; | |
97 | ||
1da177e4 | 98 | atomic_set(&volume->usage, 1); |
00d3b7a4 DH |
99 | volume->type = params->type; |
100 | volume->type_force = params->force; | |
101 | volume->cell = params->cell; | |
102 | volume->vid = vlocation->vldb.vid[params->type]; | |
1da177e4 LT |
103 | |
104 | init_rwsem(&volume->server_sem); | |
105 | ||
106 | /* look up all the applicable server records */ | |
107 | for (loop = 0; loop < 8; loop++) { | |
108 | if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) { | |
08e0e7c8 DH |
109 | server = afs_lookup_server( |
110 | volume->cell, &vlocation->vldb.servers[loop]); | |
111 | if (IS_ERR(server)) { | |
112 | ret = PTR_ERR(server); | |
1da177e4 | 113 | goto error_discard; |
08e0e7c8 | 114 | } |
1da177e4 | 115 | |
08e0e7c8 | 116 | volume->servers[volume->nservers] = server; |
1da177e4 LT |
117 | volume->nservers++; |
118 | } | |
119 | } | |
120 | ||
121 | /* attach the cache and volume location */ | |
9b3f26c9 | 122 | #ifdef CONFIG_AFS_FSCACHE |
ad6a942a | 123 | volume->cache = fscache_acquire_cookie(volume->cell->cache, |
9b3f26c9 | 124 | &afs_volume_cache_index_def, |
94d30ae9 | 125 | volume, true); |
1da177e4 | 126 | #endif |
1da177e4 LT |
127 | afs_get_vlocation(vlocation); |
128 | volume->vlocation = vlocation; | |
129 | ||
00d3b7a4 | 130 | vlocation->vols[volume->type] = volume; |
1da177e4 | 131 | |
ec26815a | 132 | success: |
1da177e4 LT |
133 | _debug("kAFS selected %s volume %08x", |
134 | afs_voltypes[volume->type], volume->vid); | |
00d3b7a4 | 135 | up_write(¶ms->cell->vl_sem); |
f044c884 | 136 | afs_put_vlocation(params->net, vlocation); |
08e0e7c8 DH |
137 | _leave(" = %p", volume); |
138 | return volume; | |
1da177e4 LT |
139 | |
140 | /* clean up */ | |
ec26815a | 141 | error_up: |
00d3b7a4 | 142 | up_write(¶ms->cell->vl_sem); |
ec26815a | 143 | error: |
f044c884 | 144 | afs_put_vlocation(params->net, vlocation); |
08e0e7c8 DH |
145 | _leave(" = %d", ret); |
146 | return ERR_PTR(ret); | |
1da177e4 | 147 | |
ec26815a | 148 | error_discard: |
00d3b7a4 | 149 | up_write(¶ms->cell->vl_sem); |
1da177e4 | 150 | |
c435ee34 DH |
151 | for (loop = volume->nservers - 1; loop >= 0; loop--) { |
152 | afs_put_cb_interest(params->net, volume->cb_interests[loop]); | |
9ed900b1 | 153 | afs_put_server(params->net, volume->servers[loop]); |
c435ee34 | 154 | } |
1da177e4 LT |
155 | |
156 | kfree(volume); | |
157 | goto error; | |
ec26815a | 158 | } |
1da177e4 | 159 | |
1da177e4 LT |
160 | /* |
161 | * destroy a volume record | |
162 | */ | |
9ed900b1 | 163 | void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume) |
1da177e4 LT |
164 | { |
165 | struct afs_vlocation *vlocation; | |
166 | int loop; | |
167 | ||
168 | if (!volume) | |
169 | return; | |
170 | ||
171 | _enter("%p", volume); | |
172 | ||
08e0e7c8 | 173 | ASSERTCMP(atomic_read(&volume->usage), >, 0); |
1da177e4 | 174 | |
08e0e7c8 | 175 | vlocation = volume->vlocation; |
1da177e4 LT |
176 | |
177 | /* to prevent a race, the decrement and the dequeue must be effectively | |
178 | * atomic */ | |
9ed900b1 | 179 | down_write(&cell->vl_sem); |
1da177e4 LT |
180 | |
181 | if (likely(!atomic_dec_and_test(&volume->usage))) { | |
182 | up_write(&vlocation->cell->vl_sem); | |
183 | _leave(""); | |
184 | return; | |
185 | } | |
186 | ||
187 | vlocation->vols[volume->type] = NULL; | |
188 | ||
9ed900b1 | 189 | up_write(&cell->vl_sem); |
1da177e4 LT |
190 | |
191 | /* finish cleaning up the volume */ | |
9b3f26c9 DH |
192 | #ifdef CONFIG_AFS_FSCACHE |
193 | fscache_relinquish_cookie(volume->cache, 0); | |
1da177e4 | 194 | #endif |
9ed900b1 | 195 | afs_put_vlocation(cell->net, vlocation); |
1da177e4 | 196 | |
c435ee34 DH |
197 | for (loop = volume->nservers - 1; loop >= 0; loop--) { |
198 | afs_put_cb_interest(cell->net, volume->cb_interests[loop]); | |
9ed900b1 | 199 | afs_put_server(cell->net, volume->servers[loop]); |
c435ee34 | 200 | } |
1da177e4 LT |
201 | |
202 | kfree(volume); | |
203 | ||
204 | _leave(" [destroyed]"); | |
ec26815a | 205 | } |