xref: /openbmc/linux/fs/afs/volume.c (revision 0f9b4c3ca5fdf3e177266ef994071b1a03f07318)
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