blob: 0393f4910a927e988803e77c3ffeb565ac1fa973 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
David Howellsec268152007-04-26 15:49:28 -07002/* AFS volume management
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
David Howells08e0e7c2007-04-26 15:55:03 -07004 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Written by David Howells (dhowells@redhat.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 */
7
8#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include "internal.h"
11
David Howellsd2ddc772017-11-02 15:27:50 +000012unsigned __read_mostly afs_volume_gc_delay = 10;
13unsigned __read_mostly afs_volume_record_life = 60 * 60;
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015/*
David Howellsd2ddc772017-11-02 15:27:50 +000016 * Allocate a volume record and load it up from a vldb record.
17 */
David Howells13fcc682018-11-01 23:07:27 +000018static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
David Howellsd2ddc772017-11-02 15:27:50 +000019 struct afs_vldb_entry *vldb,
20 unsigned long type_mask)
21{
22 struct afs_server_list *slist;
David Howellsd2ddc772017-11-02 15:27:50 +000023 struct afs_volume *volume;
David Howells45df8462018-02-06 14:12:32 +000024 int ret = -ENOMEM, nr_servers = 0, i;
David Howellsd2ddc772017-11-02 15:27:50 +000025
26 for (i = 0; i < vldb->nr_servers; i++)
27 if (vldb->fs_mask[i] & type_mask)
28 nr_servers++;
29
30 volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
31 if (!volume)
32 goto error_0;
33
34 volume->vid = vldb->vid[params->type];
35 volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
36 volume->cell = afs_get_cell(params->cell);
37 volume->type = params->type;
38 volume->type_force = params->force;
39 volume->name_len = vldb->name_len;
40
41 atomic_set(&volume->usage, 1);
42 INIT_LIST_HEAD(&volume->proc_link);
43 rwlock_init(&volume->servers_lock);
David Howells90fa9b62019-06-20 16:49:35 +010044 rwlock_init(&volume->cb_v_break_lock);
David Howellsd2ddc772017-11-02 15:27:50 +000045 memcpy(volume->name, vldb->name, vldb->name_len + 1);
46
47 slist = afs_alloc_server_list(params->cell, params->key, vldb, type_mask);
48 if (IS_ERR(slist)) {
49 ret = PTR_ERR(slist);
50 goto error_1;
51 }
52
53 refcount_set(&slist->usage, 1);
David Howells8a070a92020-04-25 10:26:02 +010054 rcu_assign_pointer(volume->servers, slist);
David Howellscca37d42020-04-29 17:02:04 +010055 trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc);
David Howellsd2ddc772017-11-02 15:27:50 +000056 return volume;
57
David Howellsd2ddc772017-11-02 15:27:50 +000058error_1:
David Howellse4415012018-02-06 09:26:27 +000059 afs_put_cell(params->net, volume->cell);
David Howellsd2ddc772017-11-02 15:27:50 +000060 kfree(volume);
61error_0:
62 return ERR_PTR(ret);
63}
64
65/*
66 * Look up a VLDB record for a volume.
67 */
68static struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
69 struct key *key,
70 const char *volname,
71 size_t volnamesz)
72{
David Howells0a5143f2018-10-20 00:57:57 +010073 struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
74 struct afs_vl_cursor vc;
David Howellsd2ddc772017-11-02 15:27:50 +000075 int ret;
76
David Howells0a5143f2018-10-20 00:57:57 +010077 if (!afs_begin_vlserver_operation(&vc, cell, key))
78 return ERR_PTR(-ERESTARTSYS);
David Howellsd2ddc772017-11-02 15:27:50 +000079
David Howells0a5143f2018-10-20 00:57:57 +010080 while (afs_select_vlserver(&vc)) {
David Howells0a5143f2018-10-20 00:57:57 +010081 vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
David Howellsd2ddc772017-11-02 15:27:50 +000082 }
83
David Howells0a5143f2018-10-20 00:57:57 +010084 ret = afs_end_vlserver_operation(&vc);
85 return ret < 0 ? ERR_PTR(ret) : vldb;
David Howellsd2ddc772017-11-02 15:27:50 +000086}
87
88/*
89 * Look up a volume in the VL server and create a candidate volume record for
90 * it.
91 *
92 * The volume name can be one of the following:
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 * "%[cell:]volume[.]" R/W volume
94 * "#[cell:]volume[.]" R/O or R/W volume (rwparent=0),
95 * or R/W (rwparent=1) volume
96 * "%[cell:]volume.readonly" R/O volume
97 * "#[cell:]volume.readonly" R/O volume
98 * "%[cell:]volume.backup" Backup volume
99 * "#[cell:]volume.backup" Backup volume
100 *
101 * The cell name is optional, and defaults to the current cell.
102 *
103 * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
104 * Guide
105 * - Rule 1: Explicit type suffix forces access of that type or nothing
106 * (no suffix, then use Rule 2 & 3)
107 * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
108 * if not available
109 * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
110 * explicitly told otherwise
111 */
David Howells13fcc682018-11-01 23:07:27 +0000112struct afs_volume *afs_create_volume(struct afs_fs_context *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113{
David Howellsd2ddc772017-11-02 15:27:50 +0000114 struct afs_vldb_entry *vldb;
115 struct afs_volume *volume;
116 unsigned long type_mask = 1UL << params->type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
David Howellsd2ddc772017-11-02 15:27:50 +0000118 vldb = afs_vl_lookup_vldb(params->cell, params->key,
119 params->volname, params->volnamesz);
120 if (IS_ERR(vldb))
121 return ERR_CAST(vldb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
David Howellsd2ddc772017-11-02 15:27:50 +0000123 if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
124 volume = ERR_PTR(vldb->error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 goto error;
David Howells08e0e7c2007-04-26 15:55:03 -0700126 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
David Howellsd2ddc772017-11-02 15:27:50 +0000128 /* Make the final decision on the type we want */
129 volume = ERR_PTR(-ENOMEDIUM);
David Howells00d3b7a2007-04-26 15:57:07 -0700130 if (params->force) {
David Howellsd2ddc772017-11-02 15:27:50 +0000131 if (!(vldb->flags & type_mask))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 goto error;
David Howellsd2ddc772017-11-02 15:27:50 +0000133 } else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
David Howells00d3b7a2007-04-26 15:57:07 -0700134 params->type = AFSVL_ROVOL;
David Howellsd2ddc772017-11-02 15:27:50 +0000135 } else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
David Howells00d3b7a2007-04-26 15:57:07 -0700136 params->type = AFSVL_RWVOL;
David Howellsec268152007-04-26 15:49:28 -0700137 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 goto error;
139 }
140
David Howellsd2ddc772017-11-02 15:27:50 +0000141 type_mask = 1UL << params->type;
142 volume = afs_alloc_volume(params, vldb, type_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
David Howellsd2ddc772017-11-02 15:27:50 +0000144error:
145 kfree(vldb);
146 return volume;
147}
148
149/*
150 * Destroy a volume record
151 */
152static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
153{
154 _enter("%p", volume);
155
156#ifdef CONFIG_AFS_FSCACHE
157 ASSERTCMP(volume->cache, ==, NULL);
158#endif
159
David Howells8a070a92020-04-25 10:26:02 +0100160 afs_put_serverlist(net, rcu_access_pointer(volume->servers));
David Howellsd2ddc772017-11-02 15:27:50 +0000161 afs_put_cell(net, volume->cell);
David Howellscca37d42020-04-29 17:02:04 +0100162 trace_afs_volume(volume->vid, atomic_read(&volume->usage),
163 afs_volume_trace_free);
David Howellsd2ddc772017-11-02 15:27:50 +0000164 kfree(volume);
165
166 _leave(" [destroyed]");
167}
168
169/*
David Howellscca37d42020-04-29 17:02:04 +0100170 * Get a reference on a volume record.
David Howellsd2ddc772017-11-02 15:27:50 +0000171 */
David Howellscca37d42020-04-29 17:02:04 +0100172struct afs_volume *afs_get_volume(struct afs_volume *volume,
173 enum afs_volume_trace reason)
David Howellsd2ddc772017-11-02 15:27:50 +0000174{
175 if (volume) {
David Howellscca37d42020-04-29 17:02:04 +0100176 int u = atomic_inc_return(&volume->usage);
177 trace_afs_volume(volume->vid, u, reason);
178 }
179 return volume;
180}
David Howellsd2ddc772017-11-02 15:27:50 +0000181
David Howellscca37d42020-04-29 17:02:04 +0100182
183/*
184 * Drop a reference on a volume record.
185 */
186void afs_put_volume(struct afs_net *net, struct afs_volume *volume,
187 enum afs_volume_trace reason)
188{
189 if (volume) {
190 afs_volid_t vid = volume->vid;
191 int u = atomic_dec_return(&volume->usage);
192 trace_afs_volume(vid, u, reason);
193 if (u == 0)
David Howellse49c7b22020-04-10 20:51:51 +0100194 afs_destroy_volume(net, volume);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
David Howellsd2ddc772017-11-02 15:27:50 +0000196}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
David Howellsd2ddc772017-11-02 15:27:50 +0000198/*
199 * Activate a volume.
200 */
201void afs_activate_volume(struct afs_volume *volume)
202{
David Howells9b3f26c2009-04-03 16:42:41 +0100203#ifdef CONFIG_AFS_FSCACHE
David Howellsad6a9422017-11-02 15:27:47 +0000204 volume->cache = fscache_acquire_cookie(volume->cell->cache,
David Howells9b3f26c2009-04-03 16:42:41 +0100205 &afs_volume_cache_index_def,
David Howells402cb8d2018-04-04 13:41:28 +0100206 &volume->vid, sizeof(volume->vid),
207 NULL, 0,
David Howellsee1235a2018-04-04 13:41:28 +0100208 volume, 0, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
David Howellsd2ddc772017-11-02 15:27:50 +0000211 write_lock(&volume->cell->proc_lock);
212 list_add_tail(&volume->proc_link, &volume->cell->proc_volumes);
213 write_unlock(&volume->cell->proc_lock);
David Howellsec268152007-04-26 15:49:28 -0700214}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216/*
David Howellsd2ddc772017-11-02 15:27:50 +0000217 * Deactivate a volume.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 */
David Howellsd2ddc772017-11-02 15:27:50 +0000219void afs_deactivate_volume(struct afs_volume *volume)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220{
David Howellsd2ddc772017-11-02 15:27:50 +0000221 _enter("%s", volume->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222
David Howellsd2ddc772017-11-02 15:27:50 +0000223 write_lock(&volume->cell->proc_lock);
224 list_del_init(&volume->proc_link);
225 write_unlock(&volume->cell->proc_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
David Howells9b3f26c2009-04-03 16:42:41 +0100227#ifdef CONFIG_AFS_FSCACHE
David Howells402cb8d2018-04-04 13:41:28 +0100228 fscache_relinquish_cookie(volume->cache, NULL,
David Howellsd2ddc772017-11-02 15:27:50 +0000229 test_bit(AFS_VOLUME_DELETED, &volume->flags));
230 volume->cache = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
David Howellsd2ddc772017-11-02 15:27:50 +0000233 _leave("");
234}
235
236/*
237 * Query the VL service to update the volume status.
238 */
239static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
240{
241 struct afs_server_list *new, *old, *discard;
242 struct afs_vldb_entry *vldb;
243 char idbuf[16];
244 int ret, idsz;
245
246 _enter("");
247
248 /* We look up an ID by passing it as a decimal string in the
249 * operation's name parameter.
250 */
David Howells3b6492d2018-10-20 00:57:57 +0100251 idsz = sprintf(idbuf, "%llu", volume->vid);
David Howellsd2ddc772017-11-02 15:27:50 +0000252
253 vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
254 if (IS_ERR(vldb)) {
255 ret = PTR_ERR(vldb);
256 goto error;
David Howellsc435ee32017-11-02 15:27:49 +0000257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
David Howellsd2ddc772017-11-02 15:27:50 +0000259 /* See if the volume got renamed. */
260 if (vldb->name_len != volume->name_len ||
261 memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
262 /* TODO: Use RCU'd string. */
263 memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
264 volume->name_len = vldb->name_len;
265 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
David Howellsd2ddc772017-11-02 15:27:50 +0000267 /* See if the volume's server list got updated. */
268 new = afs_alloc_server_list(volume->cell, key,
David Howells45df8462018-02-06 14:12:32 +0000269 vldb, (1 << volume->type));
David Howellsd2ddc772017-11-02 15:27:50 +0000270 if (IS_ERR(new)) {
271 ret = PTR_ERR(new);
272 goto error_vldb;
273 }
274
275 write_lock(&volume->servers_lock);
276
277 discard = new;
David Howells8a070a92020-04-25 10:26:02 +0100278 old = rcu_dereference_protected(volume->servers,
279 lockdep_is_held(&volume->servers_lock));
David Howellsd2ddc772017-11-02 15:27:50 +0000280 if (afs_annotate_server_list(new, old)) {
281 new->seq = volume->servers_seq + 1;
David Howells8a070a92020-04-25 10:26:02 +0100282 rcu_assign_pointer(volume->servers, new);
David Howellsd2ddc772017-11-02 15:27:50 +0000283 smp_wmb();
284 volume->servers_seq++;
285 discard = old;
286 }
287
288 volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
David Howellsd2ddc772017-11-02 15:27:50 +0000289 write_unlock(&volume->servers_lock);
290 ret = 0;
291
292 afs_put_serverlist(volume->cell->net, discard);
293error_vldb:
294 kfree(vldb);
295error:
296 _leave(" = %d", ret);
297 return ret;
298}
299
300/*
301 * Make sure the volume record is up to date.
302 */
David Howellse49c7b22020-04-10 20:51:51 +0100303int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
David Howellsd2ddc772017-11-02 15:27:50 +0000304{
David Howellsd2ddc772017-11-02 15:27:50 +0000305 int ret, retries = 0;
306
307 _enter("");
308
David Howellsd2ddc772017-11-02 15:27:50 +0000309retry:
David Howellsf6cbb362020-04-24 15:10:00 +0100310 if (test_bit(AFS_VOLUME_WAIT, &volume->flags))
311 goto wait;
312 if (volume->update_at <= ktime_get_real_seconds() ||
313 test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags))
314 goto update;
315 _leave(" = 0");
316 return 0;
David Howellsd2ddc772017-11-02 15:27:50 +0000317
David Howellsf6cbb362020-04-24 15:10:00 +0100318update:
David Howellsd2ddc772017-11-02 15:27:50 +0000319 if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
David Howellsf6cbb362020-04-24 15:10:00 +0100320 clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
David Howellse49c7b22020-04-10 20:51:51 +0100321 ret = afs_update_volume_status(volume, op->key);
David Howellsf6cbb362020-04-24 15:10:00 +0100322 if (ret < 0)
323 set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
David Howellsd2ddc772017-11-02 15:27:50 +0000324 clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
325 clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
326 wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
327 _leave(" = %d", ret);
328 return ret;
329 }
330
David Howellsf6cbb362020-04-24 15:10:00 +0100331wait:
David Howellsd2ddc772017-11-02 15:27:50 +0000332 if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
333 _leave(" = 0 [no wait]");
334 return 0;
335 }
336
David Howellsc4bfda12020-04-16 18:17:13 +0100337 ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
David Howellse49c7b22020-04-10 20:51:51 +0100338 (op->flags & AFS_OPERATION_UNINTR) ?
339 TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
David Howellsd2ddc772017-11-02 15:27:50 +0000340 if (ret == -ERESTARTSYS) {
341 _leave(" = %d", ret);
342 return ret;
343 }
344
345 retries++;
346 if (retries == 4) {
347 _leave(" = -ESTALE");
348 return -ESTALE;
349 }
350 goto retry;
David Howellsec268152007-04-26 15:49:28 -0700351}