162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* AFS volume management
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include "internal.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic unsigned __read_mostly afs_volume_record_life = 60 * 60;
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * Insert a volume into a cell.  If there's an existing volume record, that is
1662306a36Sopenharmony_ci * returned instead with a ref held.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_cistatic struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
1962306a36Sopenharmony_ci						      struct afs_volume *volume)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct afs_volume *p;
2262306a36Sopenharmony_ci	struct rb_node *parent = NULL, **pp;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	write_seqlock(&cell->volume_lock);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	pp = &cell->volumes.rb_node;
2762306a36Sopenharmony_ci	while (*pp) {
2862306a36Sopenharmony_ci		parent = *pp;
2962306a36Sopenharmony_ci		p = rb_entry(parent, struct afs_volume, cell_node);
3062306a36Sopenharmony_ci		if (p->vid < volume->vid) {
3162306a36Sopenharmony_ci			pp = &(*pp)->rb_left;
3262306a36Sopenharmony_ci		} else if (p->vid > volume->vid) {
3362306a36Sopenharmony_ci			pp = &(*pp)->rb_right;
3462306a36Sopenharmony_ci		} else {
3562306a36Sopenharmony_ci			if (afs_try_get_volume(p, afs_volume_trace_get_cell_insert)) {
3662306a36Sopenharmony_ci				volume = p;
3762306a36Sopenharmony_ci				goto found;
3862306a36Sopenharmony_ci			}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci			set_bit(AFS_VOLUME_RM_TREE, &volume->flags);
4162306a36Sopenharmony_ci			rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes);
4262306a36Sopenharmony_ci		}
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	rb_link_node_rcu(&volume->cell_node, parent, pp);
4662306a36Sopenharmony_ci	rb_insert_color(&volume->cell_node, &cell->volumes);
4762306a36Sopenharmony_ci	hlist_add_head_rcu(&volume->proc_link, &cell->proc_volumes);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cifound:
5062306a36Sopenharmony_ci	write_sequnlock(&cell->volume_lock);
5162306a36Sopenharmony_ci	return volume;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void afs_remove_volume_from_cell(struct afs_volume *volume)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct afs_cell *cell = volume->cell;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (!hlist_unhashed(&volume->proc_link)) {
6062306a36Sopenharmony_ci		trace_afs_volume(volume->vid, refcount_read(&cell->ref),
6162306a36Sopenharmony_ci				 afs_volume_trace_remove);
6262306a36Sopenharmony_ci		write_seqlock(&cell->volume_lock);
6362306a36Sopenharmony_ci		hlist_del_rcu(&volume->proc_link);
6462306a36Sopenharmony_ci		if (!test_and_set_bit(AFS_VOLUME_RM_TREE, &volume->flags))
6562306a36Sopenharmony_ci			rb_erase(&volume->cell_node, &cell->volumes);
6662306a36Sopenharmony_ci		write_sequnlock(&cell->volume_lock);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * Allocate a volume record and load it up from a vldb record.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
7462306a36Sopenharmony_ci					   struct afs_vldb_entry *vldb,
7562306a36Sopenharmony_ci					   unsigned long type_mask)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct afs_server_list *slist;
7862306a36Sopenharmony_ci	struct afs_volume *volume;
7962306a36Sopenharmony_ci	int ret = -ENOMEM;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
8262306a36Sopenharmony_ci	if (!volume)
8362306a36Sopenharmony_ci		goto error_0;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	volume->vid		= vldb->vid[params->type];
8662306a36Sopenharmony_ci	volume->update_at	= ktime_get_real_seconds() + afs_volume_record_life;
8762306a36Sopenharmony_ci	volume->cell		= afs_get_cell(params->cell, afs_cell_trace_get_vol);
8862306a36Sopenharmony_ci	volume->type		= params->type;
8962306a36Sopenharmony_ci	volume->type_force	= params->force;
9062306a36Sopenharmony_ci	volume->name_len	= vldb->name_len;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	refcount_set(&volume->ref, 1);
9362306a36Sopenharmony_ci	INIT_HLIST_NODE(&volume->proc_link);
9462306a36Sopenharmony_ci	rwlock_init(&volume->servers_lock);
9562306a36Sopenharmony_ci	rwlock_init(&volume->cb_v_break_lock);
9662306a36Sopenharmony_ci	memcpy(volume->name, vldb->name, vldb->name_len + 1);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	slist = afs_alloc_server_list(params->cell, params->key, vldb, type_mask);
9962306a36Sopenharmony_ci	if (IS_ERR(slist)) {
10062306a36Sopenharmony_ci		ret = PTR_ERR(slist);
10162306a36Sopenharmony_ci		goto error_1;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	refcount_set(&slist->usage, 1);
10562306a36Sopenharmony_ci	rcu_assign_pointer(volume->servers, slist);
10662306a36Sopenharmony_ci	trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc);
10762306a36Sopenharmony_ci	return volume;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cierror_1:
11062306a36Sopenharmony_ci	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
11162306a36Sopenharmony_ci	kfree(volume);
11262306a36Sopenharmony_cierror_0:
11362306a36Sopenharmony_ci	return ERR_PTR(ret);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/*
11762306a36Sopenharmony_ci * Look up or allocate a volume record.
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_cistatic struct afs_volume *afs_lookup_volume(struct afs_fs_context *params,
12062306a36Sopenharmony_ci					    struct afs_vldb_entry *vldb,
12162306a36Sopenharmony_ci					    unsigned long type_mask)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct afs_volume *candidate, *volume;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	candidate = afs_alloc_volume(params, vldb, type_mask);
12662306a36Sopenharmony_ci	if (IS_ERR(candidate))
12762306a36Sopenharmony_ci		return candidate;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	volume = afs_insert_volume_into_cell(params->cell, candidate);
13062306a36Sopenharmony_ci	if (volume != candidate)
13162306a36Sopenharmony_ci		afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup);
13262306a36Sopenharmony_ci	return volume;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/*
13662306a36Sopenharmony_ci * Look up a VLDB record for a volume.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
13962306a36Sopenharmony_ci						 struct key *key,
14062306a36Sopenharmony_ci						 const char *volname,
14162306a36Sopenharmony_ci						 size_t volnamesz)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
14462306a36Sopenharmony_ci	struct afs_vl_cursor vc;
14562306a36Sopenharmony_ci	int ret;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (!afs_begin_vlserver_operation(&vc, cell, key))
14862306a36Sopenharmony_ci		return ERR_PTR(-ERESTARTSYS);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	while (afs_select_vlserver(&vc)) {
15162306a36Sopenharmony_ci		vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	ret = afs_end_vlserver_operation(&vc);
15562306a36Sopenharmony_ci	return ret < 0 ? ERR_PTR(ret) : vldb;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * Look up a volume in the VL server and create a candidate volume record for
16062306a36Sopenharmony_ci * it.
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * The volume name can be one of the following:
16362306a36Sopenharmony_ci *	"%[cell:]volume[.]"		R/W volume
16462306a36Sopenharmony_ci *	"#[cell:]volume[.]"		R/O or R/W volume (rwparent=0),
16562306a36Sopenharmony_ci *					 or R/W (rwparent=1) volume
16662306a36Sopenharmony_ci *	"%[cell:]volume.readonly"	R/O volume
16762306a36Sopenharmony_ci *	"#[cell:]volume.readonly"	R/O volume
16862306a36Sopenharmony_ci *	"%[cell:]volume.backup"		Backup volume
16962306a36Sopenharmony_ci *	"#[cell:]volume.backup"		Backup volume
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci * The cell name is optional, and defaults to the current cell.
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
17462306a36Sopenharmony_ci * Guide
17562306a36Sopenharmony_ci * - Rule 1: Explicit type suffix forces access of that type or nothing
17662306a36Sopenharmony_ci *           (no suffix, then use Rule 2 & 3)
17762306a36Sopenharmony_ci * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
17862306a36Sopenharmony_ci *           if not available
17962306a36Sopenharmony_ci * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
18062306a36Sopenharmony_ci *           explicitly told otherwise
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_cistruct afs_volume *afs_create_volume(struct afs_fs_context *params)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct afs_vldb_entry *vldb;
18562306a36Sopenharmony_ci	struct afs_volume *volume;
18662306a36Sopenharmony_ci	unsigned long type_mask = 1UL << params->type;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	vldb = afs_vl_lookup_vldb(params->cell, params->key,
18962306a36Sopenharmony_ci				  params->volname, params->volnamesz);
19062306a36Sopenharmony_ci	if (IS_ERR(vldb))
19162306a36Sopenharmony_ci		return ERR_CAST(vldb);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
19462306a36Sopenharmony_ci		volume = ERR_PTR(vldb->error);
19562306a36Sopenharmony_ci		goto error;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* Make the final decision on the type we want */
19962306a36Sopenharmony_ci	volume = ERR_PTR(-ENOMEDIUM);
20062306a36Sopenharmony_ci	if (params->force) {
20162306a36Sopenharmony_ci		if (!(vldb->flags & type_mask))
20262306a36Sopenharmony_ci			goto error;
20362306a36Sopenharmony_ci	} else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
20462306a36Sopenharmony_ci		params->type = AFSVL_ROVOL;
20562306a36Sopenharmony_ci	} else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
20662306a36Sopenharmony_ci		params->type = AFSVL_RWVOL;
20762306a36Sopenharmony_ci	} else {
20862306a36Sopenharmony_ci		goto error;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	type_mask = 1UL << params->type;
21262306a36Sopenharmony_ci	volume = afs_lookup_volume(params, vldb, type_mask);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cierror:
21562306a36Sopenharmony_ci	kfree(vldb);
21662306a36Sopenharmony_ci	return volume;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/*
22062306a36Sopenharmony_ci * Destroy a volume record
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_cistatic void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	_enter("%p", volume);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
22762306a36Sopenharmony_ci	ASSERTCMP(volume->cache, ==, NULL);
22862306a36Sopenharmony_ci#endif
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	afs_remove_volume_from_cell(volume);
23162306a36Sopenharmony_ci	afs_put_serverlist(net, rcu_access_pointer(volume->servers));
23262306a36Sopenharmony_ci	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
23362306a36Sopenharmony_ci	trace_afs_volume(volume->vid, refcount_read(&volume->ref),
23462306a36Sopenharmony_ci			 afs_volume_trace_free);
23562306a36Sopenharmony_ci	kfree_rcu(volume, rcu);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	_leave(" [destroyed]");
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * Try to get a reference on a volume record.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cibool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	int r;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (__refcount_inc_not_zero(&volume->ref, &r)) {
24862306a36Sopenharmony_ci		trace_afs_volume(volume->vid, r + 1, reason);
24962306a36Sopenharmony_ci		return true;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci	return false;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/*
25562306a36Sopenharmony_ci * Get a reference on a volume record.
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_cistruct afs_volume *afs_get_volume(struct afs_volume *volume,
25862306a36Sopenharmony_ci				  enum afs_volume_trace reason)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	if (volume) {
26162306a36Sopenharmony_ci		int r;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		__refcount_inc(&volume->ref, &r);
26462306a36Sopenharmony_ci		trace_afs_volume(volume->vid, r + 1, reason);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	return volume;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/*
27162306a36Sopenharmony_ci * Drop a reference on a volume record.
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_civoid afs_put_volume(struct afs_net *net, struct afs_volume *volume,
27462306a36Sopenharmony_ci		    enum afs_volume_trace reason)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	if (volume) {
27762306a36Sopenharmony_ci		afs_volid_t vid = volume->vid;
27862306a36Sopenharmony_ci		bool zero;
27962306a36Sopenharmony_ci		int r;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		zero = __refcount_dec_and_test(&volume->ref, &r);
28262306a36Sopenharmony_ci		trace_afs_volume(vid, r - 1, reason);
28362306a36Sopenharmony_ci		if (zero)
28462306a36Sopenharmony_ci			afs_destroy_volume(net, volume);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/*
28962306a36Sopenharmony_ci * Activate a volume.
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_ciint afs_activate_volume(struct afs_volume *volume)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
29462306a36Sopenharmony_ci	struct fscache_volume *vcookie;
29562306a36Sopenharmony_ci	char *name;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	name = kasprintf(GFP_KERNEL, "afs,%s,%llx",
29862306a36Sopenharmony_ci			 volume->cell->name, volume->vid);
29962306a36Sopenharmony_ci	if (!name)
30062306a36Sopenharmony_ci		return -ENOMEM;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	vcookie = fscache_acquire_volume(name, NULL, NULL, 0);
30362306a36Sopenharmony_ci	if (IS_ERR(vcookie)) {
30462306a36Sopenharmony_ci		if (vcookie != ERR_PTR(-EBUSY)) {
30562306a36Sopenharmony_ci			kfree(name);
30662306a36Sopenharmony_ci			return PTR_ERR(vcookie);
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci		pr_err("AFS: Cache volume key already in use (%s)\n", name);
30962306a36Sopenharmony_ci		vcookie = NULL;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci	volume->cache = vcookie;
31262306a36Sopenharmony_ci	kfree(name);
31362306a36Sopenharmony_ci#endif
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/*
31862306a36Sopenharmony_ci * Deactivate a volume.
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_civoid afs_deactivate_volume(struct afs_volume *volume)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	_enter("%s", volume->name);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
32562306a36Sopenharmony_ci	fscache_relinquish_volume(volume->cache, NULL,
32662306a36Sopenharmony_ci				  test_bit(AFS_VOLUME_DELETED, &volume->flags));
32762306a36Sopenharmony_ci	volume->cache = NULL;
32862306a36Sopenharmony_ci#endif
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	_leave("");
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/*
33462306a36Sopenharmony_ci * Query the VL service to update the volume status.
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_cistatic int afs_update_volume_status(struct afs_volume *volume, struct key *key)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct afs_server_list *new, *old, *discard;
33962306a36Sopenharmony_ci	struct afs_vldb_entry *vldb;
34062306a36Sopenharmony_ci	char idbuf[24];
34162306a36Sopenharmony_ci	int ret, idsz;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	_enter("");
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* We look up an ID by passing it as a decimal string in the
34662306a36Sopenharmony_ci	 * operation's name parameter.
34762306a36Sopenharmony_ci	 */
34862306a36Sopenharmony_ci	idsz = snprintf(idbuf, sizeof(idbuf), "%llu", volume->vid);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
35162306a36Sopenharmony_ci	if (IS_ERR(vldb)) {
35262306a36Sopenharmony_ci		ret = PTR_ERR(vldb);
35362306a36Sopenharmony_ci		goto error;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* See if the volume got renamed. */
35762306a36Sopenharmony_ci	if (vldb->name_len != volume->name_len ||
35862306a36Sopenharmony_ci	    memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
35962306a36Sopenharmony_ci		/* TODO: Use RCU'd string. */
36062306a36Sopenharmony_ci		memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
36162306a36Sopenharmony_ci		volume->name_len = vldb->name_len;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* See if the volume's server list got updated. */
36562306a36Sopenharmony_ci	new = afs_alloc_server_list(volume->cell, key,
36662306a36Sopenharmony_ci				    vldb, (1 << volume->type));
36762306a36Sopenharmony_ci	if (IS_ERR(new)) {
36862306a36Sopenharmony_ci		ret = PTR_ERR(new);
36962306a36Sopenharmony_ci		goto error_vldb;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	write_lock(&volume->servers_lock);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	discard = new;
37562306a36Sopenharmony_ci	old = rcu_dereference_protected(volume->servers,
37662306a36Sopenharmony_ci					lockdep_is_held(&volume->servers_lock));
37762306a36Sopenharmony_ci	if (afs_annotate_server_list(new, old)) {
37862306a36Sopenharmony_ci		new->seq = volume->servers_seq + 1;
37962306a36Sopenharmony_ci		rcu_assign_pointer(volume->servers, new);
38062306a36Sopenharmony_ci		smp_wmb();
38162306a36Sopenharmony_ci		volume->servers_seq++;
38262306a36Sopenharmony_ci		discard = old;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
38662306a36Sopenharmony_ci	write_unlock(&volume->servers_lock);
38762306a36Sopenharmony_ci	ret = 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	afs_put_serverlist(volume->cell->net, discard);
39062306a36Sopenharmony_cierror_vldb:
39162306a36Sopenharmony_ci	kfree(vldb);
39262306a36Sopenharmony_cierror:
39362306a36Sopenharmony_ci	_leave(" = %d", ret);
39462306a36Sopenharmony_ci	return ret;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/*
39862306a36Sopenharmony_ci * Make sure the volume record is up to date.
39962306a36Sopenharmony_ci */
40062306a36Sopenharmony_ciint afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	int ret, retries = 0;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	_enter("");
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ciretry:
40762306a36Sopenharmony_ci	if (test_bit(AFS_VOLUME_WAIT, &volume->flags))
40862306a36Sopenharmony_ci		goto wait;
40962306a36Sopenharmony_ci	if (volume->update_at <= ktime_get_real_seconds() ||
41062306a36Sopenharmony_ci	    test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags))
41162306a36Sopenharmony_ci		goto update;
41262306a36Sopenharmony_ci	_leave(" = 0");
41362306a36Sopenharmony_ci	return 0;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ciupdate:
41662306a36Sopenharmony_ci	if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
41762306a36Sopenharmony_ci		clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
41862306a36Sopenharmony_ci		ret = afs_update_volume_status(volume, op->key);
41962306a36Sopenharmony_ci		if (ret < 0)
42062306a36Sopenharmony_ci			set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
42162306a36Sopenharmony_ci		clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
42262306a36Sopenharmony_ci		clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
42362306a36Sopenharmony_ci		wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
42462306a36Sopenharmony_ci		_leave(" = %d", ret);
42562306a36Sopenharmony_ci		return ret;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ciwait:
42962306a36Sopenharmony_ci	if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
43062306a36Sopenharmony_ci		_leave(" = 0 [no wait]");
43162306a36Sopenharmony_ci		return 0;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
43562306a36Sopenharmony_ci			  (op->flags & AFS_OPERATION_UNINTR) ?
43662306a36Sopenharmony_ci			  TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
43762306a36Sopenharmony_ci	if (ret == -ERESTARTSYS) {
43862306a36Sopenharmony_ci		_leave(" = %d", ret);
43962306a36Sopenharmony_ci		return ret;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	retries++;
44362306a36Sopenharmony_ci	if (retries == 4) {
44462306a36Sopenharmony_ci		_leave(" = -ESTALE");
44562306a36Sopenharmony_ci		return -ESTALE;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci	goto retry;
44862306a36Sopenharmony_ci}
449