12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ec26815aSDavid Howells /* AFS volume management
31da177e4SLinus Torvalds *
408e0e7c8SDavid Howells * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
51da177e4SLinus Torvalds * Written by David Howells (dhowells@redhat.com)
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds #include <linux/kernel.h>
91da177e4SLinus Torvalds #include <linux/slab.h>
101da177e4SLinus Torvalds #include "internal.h"
111da177e4SLinus Torvalds
12102d8410SDavid Howells static unsigned __read_mostly afs_volume_record_life = 60 * 60;
13d2ddc776SDavid Howells
141da177e4SLinus Torvalds /*
1520325960SDavid Howells * Insert a volume into a cell. If there's an existing volume record, that is
1620325960SDavid Howells * returned instead with a ref held.
1720325960SDavid Howells */
afs_insert_volume_into_cell(struct afs_cell * cell,struct afs_volume * volume)1820325960SDavid Howells static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
1920325960SDavid Howells struct afs_volume *volume)
2020325960SDavid Howells {
2120325960SDavid Howells struct afs_volume *p;
2220325960SDavid Howells struct rb_node *parent = NULL, **pp;
2320325960SDavid Howells
2420325960SDavid Howells write_seqlock(&cell->volume_lock);
2520325960SDavid Howells
2620325960SDavid Howells pp = &cell->volumes.rb_node;
2720325960SDavid Howells while (*pp) {
2820325960SDavid Howells parent = *pp;
2920325960SDavid Howells p = rb_entry(parent, struct afs_volume, cell_node);
3020325960SDavid Howells if (p->vid < volume->vid) {
3120325960SDavid Howells pp = &(*pp)->rb_left;
3220325960SDavid Howells } else if (p->vid > volume->vid) {
3320325960SDavid Howells pp = &(*pp)->rb_right;
3420325960SDavid Howells } else {
35c3215484SDavid Howells if (afs_try_get_volume(p, afs_volume_trace_get_cell_insert)) {
36c3215484SDavid Howells volume = p;
3720325960SDavid Howells goto found;
3820325960SDavid Howells }
39c3215484SDavid Howells
40c3215484SDavid Howells set_bit(AFS_VOLUME_RM_TREE, &volume->flags);
41c3215484SDavid Howells rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes);
42c3215484SDavid Howells }
4320325960SDavid Howells }
4420325960SDavid Howells
4520325960SDavid Howells rb_link_node_rcu(&volume->cell_node, parent, pp);
4620325960SDavid Howells rb_insert_color(&volume->cell_node, &cell->volumes);
4720325960SDavid Howells hlist_add_head_rcu(&volume->proc_link, &cell->proc_volumes);
4820325960SDavid Howells
4920325960SDavid Howells found:
5020325960SDavid Howells write_sequnlock(&cell->volume_lock);
5120325960SDavid Howells return volume;
5220325960SDavid Howells
5320325960SDavid Howells }
5420325960SDavid Howells
afs_remove_volume_from_cell(struct afs_volume * volume)5520325960SDavid Howells static void afs_remove_volume_from_cell(struct afs_volume *volume)
5620325960SDavid Howells {
5720325960SDavid Howells struct afs_cell *cell = volume->cell;
5820325960SDavid Howells
5920325960SDavid Howells if (!hlist_unhashed(&volume->proc_link)) {
60c56f9ec8SDavid Howells trace_afs_volume(volume->vid, refcount_read(&cell->ref),
6120325960SDavid Howells afs_volume_trace_remove);
6220325960SDavid Howells write_seqlock(&cell->volume_lock);
6320325960SDavid Howells hlist_del_rcu(&volume->proc_link);
64c3215484SDavid Howells if (!test_and_set_bit(AFS_VOLUME_RM_TREE, &volume->flags))
6520325960SDavid Howells rb_erase(&volume->cell_node, &cell->volumes);
6620325960SDavid Howells write_sequnlock(&cell->volume_lock);
6720325960SDavid Howells }
6820325960SDavid Howells }
6920325960SDavid Howells
7020325960SDavid Howells /*
71d2ddc776SDavid Howells * Allocate a volume record and load it up from a vldb record.
72d2ddc776SDavid Howells */
afs_alloc_volume(struct afs_fs_context * params,struct afs_vldb_entry * vldb,struct afs_server_list ** _slist)7313fcc683SDavid Howells static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
74d2ddc776SDavid Howells struct afs_vldb_entry *vldb,
75*0b2bf6fbSDavid Howells struct afs_server_list **_slist)
76d2ddc776SDavid Howells {
77d2ddc776SDavid Howells struct afs_server_list *slist;
78d2ddc776SDavid Howells struct afs_volume *volume;
79*0b2bf6fbSDavid Howells int ret = -ENOMEM, i;
80d2ddc776SDavid Howells
81d2ddc776SDavid Howells volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
82d2ddc776SDavid Howells if (!volume)
83d2ddc776SDavid Howells goto error_0;
84d2ddc776SDavid Howells
85d2ddc776SDavid Howells volume->vid = vldb->vid[params->type];
86d2ddc776SDavid Howells volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
87dca54a7bSDavid Howells volume->cell = afs_get_cell(params->cell, afs_cell_trace_get_vol);
88d2ddc776SDavid Howells volume->type = params->type;
89d2ddc776SDavid Howells volume->type_force = params->force;
90d2ddc776SDavid Howells volume->name_len = vldb->name_len;
91d2ddc776SDavid Howells
92c56f9ec8SDavid Howells refcount_set(&volume->ref, 1);
9320325960SDavid Howells INIT_HLIST_NODE(&volume->proc_link);
94d2ddc776SDavid Howells rwlock_init(&volume->servers_lock);
9590fa9b64SDavid Howells rwlock_init(&volume->cb_v_break_lock);
96d2ddc776SDavid Howells memcpy(volume->name, vldb->name, vldb->name_len + 1);
97d2ddc776SDavid Howells
98*0b2bf6fbSDavid Howells for (i = 0; i < AFS_MAXTYPES; i++)
99*0b2bf6fbSDavid Howells volume->vids[i] = vldb->vid[i];
100*0b2bf6fbSDavid Howells
101*0b2bf6fbSDavid Howells slist = afs_alloc_server_list(volume, params->key, vldb);
102d2ddc776SDavid Howells if (IS_ERR(slist)) {
103d2ddc776SDavid Howells ret = PTR_ERR(slist);
104d2ddc776SDavid Howells goto error_1;
105d2ddc776SDavid Howells }
106d2ddc776SDavid Howells
107*0b2bf6fbSDavid Howells *_slist = slist;
1088a070a96SDavid Howells rcu_assign_pointer(volume->servers, slist);
109cca37d45SDavid Howells trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc);
110d2ddc776SDavid Howells return volume;
111d2ddc776SDavid Howells
112d2ddc776SDavid Howells error_1:
113dca54a7bSDavid Howells afs_put_cell(volume->cell, afs_cell_trace_put_vol);
114d2ddc776SDavid Howells kfree(volume);
115d2ddc776SDavid Howells error_0:
116d2ddc776SDavid Howells return ERR_PTR(ret);
117d2ddc776SDavid Howells }
118d2ddc776SDavid Howells
119d2ddc776SDavid Howells /*
12020325960SDavid Howells * Look up or allocate a volume record.
12120325960SDavid Howells */
afs_lookup_volume(struct afs_fs_context * params,struct afs_vldb_entry * vldb)12220325960SDavid Howells static struct afs_volume *afs_lookup_volume(struct afs_fs_context *params,
123*0b2bf6fbSDavid Howells struct afs_vldb_entry *vldb)
12420325960SDavid Howells {
125*0b2bf6fbSDavid Howells struct afs_server_list *slist;
12620325960SDavid Howells struct afs_volume *candidate, *volume;
12720325960SDavid Howells
128*0b2bf6fbSDavid Howells candidate = afs_alloc_volume(params, vldb, &slist);
12920325960SDavid Howells if (IS_ERR(candidate))
13020325960SDavid Howells return candidate;
13120325960SDavid Howells
13220325960SDavid Howells volume = afs_insert_volume_into_cell(params->cell, candidate);
133*0b2bf6fbSDavid Howells if (volume == candidate)
134*0b2bf6fbSDavid Howells afs_attach_volume_to_servers(volume, slist);
135*0b2bf6fbSDavid Howells else
13620325960SDavid Howells afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup);
13720325960SDavid Howells return volume;
13820325960SDavid Howells }
13920325960SDavid Howells
14020325960SDavid Howells /*
141d2ddc776SDavid Howells * Look up a VLDB record for a volume.
142d2ddc776SDavid Howells */
afs_vl_lookup_vldb(struct afs_cell * cell,struct key * key,const char * volname,size_t volnamesz)143d2ddc776SDavid Howells static struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
144d2ddc776SDavid Howells struct key *key,
145d2ddc776SDavid Howells const char *volname,
146d2ddc776SDavid Howells size_t volnamesz)
147d2ddc776SDavid Howells {
1480a5143f2SDavid Howells struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
1490a5143f2SDavid Howells struct afs_vl_cursor vc;
150d2ddc776SDavid Howells int ret;
151d2ddc776SDavid Howells
1520a5143f2SDavid Howells if (!afs_begin_vlserver_operation(&vc, cell, key))
1530a5143f2SDavid Howells return ERR_PTR(-ERESTARTSYS);
154d2ddc776SDavid Howells
1550a5143f2SDavid Howells while (afs_select_vlserver(&vc)) {
1560a5143f2SDavid Howells vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
157d2ddc776SDavid Howells }
158d2ddc776SDavid Howells
1590a5143f2SDavid Howells ret = afs_end_vlserver_operation(&vc);
1600a5143f2SDavid Howells return ret < 0 ? ERR_PTR(ret) : vldb;
161d2ddc776SDavid Howells }
162d2ddc776SDavid Howells
163d2ddc776SDavid Howells /*
164d2ddc776SDavid Howells * Look up a volume in the VL server and create a candidate volume record for
165d2ddc776SDavid Howells * it.
166d2ddc776SDavid Howells *
167d2ddc776SDavid Howells * The volume name can be one of the following:
1681da177e4SLinus Torvalds * "%[cell:]volume[.]" R/W volume
1691da177e4SLinus Torvalds * "#[cell:]volume[.]" R/O or R/W volume (rwparent=0),
1701da177e4SLinus Torvalds * or R/W (rwparent=1) volume
1711da177e4SLinus Torvalds * "%[cell:]volume.readonly" R/O volume
1721da177e4SLinus Torvalds * "#[cell:]volume.readonly" R/O volume
1731da177e4SLinus Torvalds * "%[cell:]volume.backup" Backup volume
1741da177e4SLinus Torvalds * "#[cell:]volume.backup" Backup volume
1751da177e4SLinus Torvalds *
1761da177e4SLinus Torvalds * The cell name is optional, and defaults to the current cell.
1771da177e4SLinus Torvalds *
1781da177e4SLinus Torvalds * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
1791da177e4SLinus Torvalds * Guide
1801da177e4SLinus Torvalds * - Rule 1: Explicit type suffix forces access of that type or nothing
1811da177e4SLinus Torvalds * (no suffix, then use Rule 2 & 3)
1821da177e4SLinus Torvalds * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
1831da177e4SLinus Torvalds * if not available
1841da177e4SLinus Torvalds * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
1851da177e4SLinus Torvalds * explicitly told otherwise
1861da177e4SLinus Torvalds */
afs_create_volume(struct afs_fs_context * params)18713fcc683SDavid Howells struct afs_volume *afs_create_volume(struct afs_fs_context *params)
1881da177e4SLinus Torvalds {
189d2ddc776SDavid Howells struct afs_vldb_entry *vldb;
190d2ddc776SDavid Howells struct afs_volume *volume;
191d2ddc776SDavid Howells unsigned long type_mask = 1UL << params->type;
1921da177e4SLinus Torvalds
193d2ddc776SDavid Howells vldb = afs_vl_lookup_vldb(params->cell, params->key,
19400d3b7a4SDavid Howells params->volname, params->volnamesz);
195d2ddc776SDavid Howells if (IS_ERR(vldb))
196d2ddc776SDavid Howells return ERR_CAST(vldb);
197d2ddc776SDavid Howells
198d2ddc776SDavid Howells if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
199d2ddc776SDavid Howells volume = ERR_PTR(vldb->error);
2001da177e4SLinus Torvalds goto error;
20108e0e7c8SDavid Howells }
2021da177e4SLinus Torvalds
203d2ddc776SDavid Howells /* Make the final decision on the type we want */
204d2ddc776SDavid Howells volume = ERR_PTR(-ENOMEDIUM);
20500d3b7a4SDavid Howells if (params->force) {
206d2ddc776SDavid Howells if (!(vldb->flags & type_mask))
2071da177e4SLinus Torvalds goto error;
208d2ddc776SDavid Howells } else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
20900d3b7a4SDavid Howells params->type = AFSVL_ROVOL;
210d2ddc776SDavid Howells } else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
21100d3b7a4SDavid Howells params->type = AFSVL_RWVOL;
212ec26815aSDavid Howells } else {
2131da177e4SLinus Torvalds goto error;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds
216*0b2bf6fbSDavid Howells volume = afs_lookup_volume(params, vldb);
2171da177e4SLinus Torvalds
218d2ddc776SDavid Howells error:
219d2ddc776SDavid Howells kfree(vldb);
220d2ddc776SDavid Howells return volume;
2211da177e4SLinus Torvalds }
2221da177e4SLinus Torvalds
223d2ddc776SDavid Howells /*
224d2ddc776SDavid Howells * Destroy a volume record
225d2ddc776SDavid Howells */
afs_destroy_volume(struct afs_net * net,struct afs_volume * volume)226d2ddc776SDavid Howells static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
227d2ddc776SDavid Howells {
228*0b2bf6fbSDavid Howells struct afs_server_list *slist = rcu_access_pointer(volume->servers);
229*0b2bf6fbSDavid Howells
230d2ddc776SDavid Howells _enter("%p", volume);
2311da177e4SLinus Torvalds
232d2ddc776SDavid Howells #ifdef CONFIG_AFS_FSCACHE
233d2ddc776SDavid Howells ASSERTCMP(volume->cache, ==, NULL);
234d2ddc776SDavid Howells #endif
2351da177e4SLinus Torvalds
236*0b2bf6fbSDavid Howells afs_detach_volume_from_servers(volume, slist);
23720325960SDavid Howells afs_remove_volume_from_cell(volume);
238*0b2bf6fbSDavid Howells afs_put_serverlist(net, slist);
239dca54a7bSDavid Howells afs_put_cell(volume->cell, afs_cell_trace_put_vol);
240c56f9ec8SDavid Howells trace_afs_volume(volume->vid, refcount_read(&volume->ref),
241cca37d45SDavid Howells afs_volume_trace_free);
24220325960SDavid Howells kfree_rcu(volume, rcu);
2431da177e4SLinus Torvalds
244d2ddc776SDavid Howells _leave(" [destroyed]");
24508e0e7c8SDavid Howells }
2461da177e4SLinus Torvalds
247d2ddc776SDavid Howells /*
248c3215484SDavid Howells * Try to get a reference on a volume record.
249c3215484SDavid Howells */
afs_try_get_volume(struct afs_volume * volume,enum afs_volume_trace reason)250c3215484SDavid Howells bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason)
251c3215484SDavid Howells {
252c3215484SDavid Howells int r;
253c3215484SDavid Howells
254c3215484SDavid Howells if (__refcount_inc_not_zero(&volume->ref, &r)) {
255c3215484SDavid Howells trace_afs_volume(volume->vid, r + 1, reason);
256c3215484SDavid Howells return true;
257c3215484SDavid Howells }
258c3215484SDavid Howells return false;
259c3215484SDavid Howells }
260c3215484SDavid Howells
261c3215484SDavid Howells /*
262cca37d45SDavid Howells * Get a reference on a volume record.
263d2ddc776SDavid Howells */
afs_get_volume(struct afs_volume * volume,enum afs_volume_trace reason)264cca37d45SDavid Howells struct afs_volume *afs_get_volume(struct afs_volume *volume,
265cca37d45SDavid Howells enum afs_volume_trace reason)
266d2ddc776SDavid Howells {
267d2ddc776SDavid Howells if (volume) {
268c56f9ec8SDavid Howells int r;
269c56f9ec8SDavid Howells
270c56f9ec8SDavid Howells __refcount_inc(&volume->ref, &r);
271c56f9ec8SDavid Howells trace_afs_volume(volume->vid, r + 1, reason);
272cca37d45SDavid Howells }
273cca37d45SDavid Howells return volume;
274cca37d45SDavid Howells }
275d2ddc776SDavid Howells
276cca37d45SDavid Howells
277cca37d45SDavid Howells /*
278cca37d45SDavid Howells * Drop a reference on a volume record.
279cca37d45SDavid Howells */
afs_put_volume(struct afs_net * net,struct afs_volume * volume,enum afs_volume_trace reason)280cca37d45SDavid Howells void afs_put_volume(struct afs_net *net, struct afs_volume *volume,
281cca37d45SDavid Howells enum afs_volume_trace reason)
282cca37d45SDavid Howells {
283cca37d45SDavid Howells if (volume) {
284cca37d45SDavid Howells afs_volid_t vid = volume->vid;
285c56f9ec8SDavid Howells bool zero;
286c56f9ec8SDavid Howells int r;
287c56f9ec8SDavid Howells
288c56f9ec8SDavid Howells zero = __refcount_dec_and_test(&volume->ref, &r);
289c56f9ec8SDavid Howells trace_afs_volume(vid, r - 1, reason);
290c56f9ec8SDavid Howells if (zero)
291e49c7b2fSDavid Howells afs_destroy_volume(net, volume);
2921da177e4SLinus Torvalds }
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds
295d2ddc776SDavid Howells /*
296d2ddc776SDavid Howells * Activate a volume.
297d2ddc776SDavid Howells */
afs_activate_volume(struct afs_volume * volume)298523d27cdSDavid Howells int afs_activate_volume(struct afs_volume *volume)
299d2ddc776SDavid Howells {
3009b3f26c9SDavid Howells #ifdef CONFIG_AFS_FSCACHE
301523d27cdSDavid Howells struct fscache_volume *vcookie;
302523d27cdSDavid Howells char *name;
303523d27cdSDavid Howells
304523d27cdSDavid Howells name = kasprintf(GFP_KERNEL, "afs,%s,%llx",
305523d27cdSDavid Howells volume->cell->name, volume->vid);
306523d27cdSDavid Howells if (!name)
307523d27cdSDavid Howells return -ENOMEM;
308523d27cdSDavid Howells
309523d27cdSDavid Howells vcookie = fscache_acquire_volume(name, NULL, NULL, 0);
310523d27cdSDavid Howells if (IS_ERR(vcookie)) {
311523d27cdSDavid Howells if (vcookie != ERR_PTR(-EBUSY)) {
312523d27cdSDavid Howells kfree(name);
313523d27cdSDavid Howells return PTR_ERR(vcookie);
314523d27cdSDavid Howells }
315523d27cdSDavid Howells pr_err("AFS: Cache volume key already in use (%s)\n", name);
316523d27cdSDavid Howells vcookie = NULL;
317523d27cdSDavid Howells }
318523d27cdSDavid Howells volume->cache = vcookie;
319523d27cdSDavid Howells kfree(name);
3201da177e4SLinus Torvalds #endif
321523d27cdSDavid Howells return 0;
322ec26815aSDavid Howells }
3231da177e4SLinus Torvalds
3241da177e4SLinus Torvalds /*
325d2ddc776SDavid Howells * Deactivate a volume.
3261da177e4SLinus Torvalds */
afs_deactivate_volume(struct afs_volume * volume)327d2ddc776SDavid Howells void afs_deactivate_volume(struct afs_volume *volume)
3281da177e4SLinus Torvalds {
329d2ddc776SDavid Howells _enter("%s", volume->name);
3301da177e4SLinus Torvalds
3319b3f26c9SDavid Howells #ifdef CONFIG_AFS_FSCACHE
332523d27cdSDavid Howells fscache_relinquish_volume(volume->cache, NULL,
333d2ddc776SDavid Howells test_bit(AFS_VOLUME_DELETED, &volume->flags));
334d2ddc776SDavid Howells volume->cache = NULL;
3351da177e4SLinus Torvalds #endif
3361da177e4SLinus Torvalds
337d2ddc776SDavid Howells _leave("");
338c435ee34SDavid Howells }
3391da177e4SLinus Torvalds
340d2ddc776SDavid Howells /*
341d2ddc776SDavid Howells * Query the VL service to update the volume status.
342d2ddc776SDavid Howells */
afs_update_volume_status(struct afs_volume * volume,struct key * key)343d2ddc776SDavid Howells static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
344d2ddc776SDavid Howells {
345d2ddc776SDavid Howells struct afs_server_list *new, *old, *discard;
346d2ddc776SDavid Howells struct afs_vldb_entry *vldb;
3476e6065ddSDaniil Dulov char idbuf[24];
348d2ddc776SDavid Howells int ret, idsz;
3491da177e4SLinus Torvalds
350d2ddc776SDavid Howells _enter("");
351d2ddc776SDavid Howells
352d2ddc776SDavid Howells /* We look up an ID by passing it as a decimal string in the
353d2ddc776SDavid Howells * operation's name parameter.
354d2ddc776SDavid Howells */
3556e6065ddSDaniil Dulov idsz = snprintf(idbuf, sizeof(idbuf), "%llu", volume->vid);
356d2ddc776SDavid Howells
357d2ddc776SDavid Howells vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
358d2ddc776SDavid Howells if (IS_ERR(vldb)) {
359d2ddc776SDavid Howells ret = PTR_ERR(vldb);
360d2ddc776SDavid Howells goto error;
361d2ddc776SDavid Howells }
362d2ddc776SDavid Howells
363d2ddc776SDavid Howells /* See if the volume got renamed. */
364d2ddc776SDavid Howells if (vldb->name_len != volume->name_len ||
365d2ddc776SDavid Howells memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
366d2ddc776SDavid Howells /* TODO: Use RCU'd string. */
367d2ddc776SDavid Howells memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
368d2ddc776SDavid Howells volume->name_len = vldb->name_len;
369d2ddc776SDavid Howells }
370d2ddc776SDavid Howells
371d2ddc776SDavid Howells /* See if the volume's server list got updated. */
372*0b2bf6fbSDavid Howells new = afs_alloc_server_list(volume, key, vldb);
373d2ddc776SDavid Howells if (IS_ERR(new)) {
374d2ddc776SDavid Howells ret = PTR_ERR(new);
375d2ddc776SDavid Howells goto error_vldb;
376d2ddc776SDavid Howells }
377d2ddc776SDavid Howells
378d2ddc776SDavid Howells write_lock(&volume->servers_lock);
379d2ddc776SDavid Howells
380d2ddc776SDavid Howells discard = new;
3818a070a96SDavid Howells old = rcu_dereference_protected(volume->servers,
3828a070a96SDavid Howells lockdep_is_held(&volume->servers_lock));
383d2ddc776SDavid Howells if (afs_annotate_server_list(new, old)) {
384d2ddc776SDavid Howells new->seq = volume->servers_seq + 1;
3858a070a96SDavid Howells rcu_assign_pointer(volume->servers, new);
386d2ddc776SDavid Howells smp_wmb();
387d2ddc776SDavid Howells volume->servers_seq++;
388d2ddc776SDavid Howells discard = old;
389d2ddc776SDavid Howells }
390d2ddc776SDavid Howells
391d2ddc776SDavid Howells volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
392d2ddc776SDavid Howells write_unlock(&volume->servers_lock);
393d2ddc776SDavid Howells
394*0b2bf6fbSDavid Howells if (discard == old)
395*0b2bf6fbSDavid Howells afs_reattach_volume_to_servers(volume, new, old);
396d2ddc776SDavid Howells afs_put_serverlist(volume->cell->net, discard);
397*0b2bf6fbSDavid Howells ret = 0;
398d2ddc776SDavid Howells error_vldb:
399d2ddc776SDavid Howells kfree(vldb);
400d2ddc776SDavid Howells error:
401d2ddc776SDavid Howells _leave(" = %d", ret);
402d2ddc776SDavid Howells return ret;
403d2ddc776SDavid Howells }
404d2ddc776SDavid Howells
405d2ddc776SDavid Howells /*
406d2ddc776SDavid Howells * Make sure the volume record is up to date.
407d2ddc776SDavid Howells */
afs_check_volume_status(struct afs_volume * volume,struct afs_operation * op)408e49c7b2fSDavid Howells int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
409d2ddc776SDavid Howells {
410d2ddc776SDavid Howells int ret, retries = 0;
411d2ddc776SDavid Howells
412d2ddc776SDavid Howells _enter("");
413d2ddc776SDavid Howells
414d2ddc776SDavid Howells retry:
415f6cbb368SDavid Howells if (test_bit(AFS_VOLUME_WAIT, &volume->flags))
416f6cbb368SDavid Howells goto wait;
417f6cbb368SDavid Howells if (volume->update_at <= ktime_get_real_seconds() ||
418f6cbb368SDavid Howells test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags))
419f6cbb368SDavid Howells goto update;
420d2ddc776SDavid Howells _leave(" = 0");
421d2ddc776SDavid Howells return 0;
422d2ddc776SDavid Howells
423f6cbb368SDavid Howells update:
424d2ddc776SDavid Howells if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
425f6cbb368SDavid Howells clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
426e49c7b2fSDavid Howells ret = afs_update_volume_status(volume, op->key);
427f6cbb368SDavid Howells if (ret < 0)
428f6cbb368SDavid Howells set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
429d2ddc776SDavid Howells clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
430d2ddc776SDavid Howells clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
431d2ddc776SDavid Howells wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
432d2ddc776SDavid Howells _leave(" = %d", ret);
433d2ddc776SDavid Howells return ret;
434d2ddc776SDavid Howells }
435d2ddc776SDavid Howells
436f6cbb368SDavid Howells wait:
437d2ddc776SDavid Howells if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
438d2ddc776SDavid Howells _leave(" = 0 [no wait]");
439d2ddc776SDavid Howells return 0;
440d2ddc776SDavid Howells }
441d2ddc776SDavid Howells
442c4bfda16SDavid Howells ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
443e49c7b2fSDavid Howells (op->flags & AFS_OPERATION_UNINTR) ?
444e49c7b2fSDavid Howells TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
445d2ddc776SDavid Howells if (ret == -ERESTARTSYS) {
446d2ddc776SDavid Howells _leave(" = %d", ret);
447d2ddc776SDavid Howells return ret;
448d2ddc776SDavid Howells }
449d2ddc776SDavid Howells
450d2ddc776SDavid Howells retries++;
451d2ddc776SDavid Howells if (retries == 4) {
452d2ddc776SDavid Howells _leave(" = -ESTALE");
453d2ddc776SDavid Howells return -ESTALE;
454d2ddc776SDavid Howells }
455d2ddc776SDavid Howells goto retry;
456ec26815aSDavid Howells }
457