xref: /kernel/linux/linux-5.10/fs/afs/volume.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* AFS volume management
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include "internal.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciunsigned __read_mostly afs_volume_gc_delay = 10;
138c2ecf20Sopenharmony_ciunsigned __read_mostly afs_volume_record_life = 60 * 60;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * Insert a volume into a cell.  If there's an existing volume record, that is
178c2ecf20Sopenharmony_ci * returned instead with a ref held.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_cistatic struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
208c2ecf20Sopenharmony_ci						      struct afs_volume *volume)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct afs_volume *p;
238c2ecf20Sopenharmony_ci	struct rb_node *parent = NULL, **pp;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	write_seqlock(&cell->volume_lock);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	pp = &cell->volumes.rb_node;
288c2ecf20Sopenharmony_ci	while (*pp) {
298c2ecf20Sopenharmony_ci		parent = *pp;
308c2ecf20Sopenharmony_ci		p = rb_entry(parent, struct afs_volume, cell_node);
318c2ecf20Sopenharmony_ci		if (p->vid < volume->vid) {
328c2ecf20Sopenharmony_ci			pp = &(*pp)->rb_left;
338c2ecf20Sopenharmony_ci		} else if (p->vid > volume->vid) {
348c2ecf20Sopenharmony_ci			pp = &(*pp)->rb_right;
358c2ecf20Sopenharmony_ci		} else {
368c2ecf20Sopenharmony_ci			volume = afs_get_volume(p, afs_volume_trace_get_cell_insert);
378c2ecf20Sopenharmony_ci			goto found;
388c2ecf20Sopenharmony_ci		}
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	rb_link_node_rcu(&volume->cell_node, parent, pp);
428c2ecf20Sopenharmony_ci	rb_insert_color(&volume->cell_node, &cell->volumes);
438c2ecf20Sopenharmony_ci	hlist_add_head_rcu(&volume->proc_link, &cell->proc_volumes);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cifound:
468c2ecf20Sopenharmony_ci	write_sequnlock(&cell->volume_lock);
478c2ecf20Sopenharmony_ci	return volume;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void afs_remove_volume_from_cell(struct afs_volume *volume)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct afs_cell *cell = volume->cell;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (!hlist_unhashed(&volume->proc_link)) {
568c2ecf20Sopenharmony_ci		trace_afs_volume(volume->vid, atomic_read(&volume->usage),
578c2ecf20Sopenharmony_ci				 afs_volume_trace_remove);
588c2ecf20Sopenharmony_ci		write_seqlock(&cell->volume_lock);
598c2ecf20Sopenharmony_ci		hlist_del_rcu(&volume->proc_link);
608c2ecf20Sopenharmony_ci		rb_erase(&volume->cell_node, &cell->volumes);
618c2ecf20Sopenharmony_ci		write_sequnlock(&cell->volume_lock);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * Allocate a volume record and load it up from a vldb record.
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_cistatic struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
698c2ecf20Sopenharmony_ci					   struct afs_vldb_entry *vldb,
708c2ecf20Sopenharmony_ci					   unsigned long type_mask)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct afs_server_list *slist;
738c2ecf20Sopenharmony_ci	struct afs_volume *volume;
748c2ecf20Sopenharmony_ci	int ret = -ENOMEM, nr_servers = 0, i;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	for (i = 0; i < vldb->nr_servers; i++)
778c2ecf20Sopenharmony_ci		if (vldb->fs_mask[i] & type_mask)
788c2ecf20Sopenharmony_ci			nr_servers++;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
818c2ecf20Sopenharmony_ci	if (!volume)
828c2ecf20Sopenharmony_ci		goto error_0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	volume->vid		= vldb->vid[params->type];
858c2ecf20Sopenharmony_ci	volume->update_at	= ktime_get_real_seconds() + afs_volume_record_life;
868c2ecf20Sopenharmony_ci	volume->cell		= afs_get_cell(params->cell, afs_cell_trace_get_vol);
878c2ecf20Sopenharmony_ci	volume->type		= params->type;
888c2ecf20Sopenharmony_ci	volume->type_force	= params->force;
898c2ecf20Sopenharmony_ci	volume->name_len	= vldb->name_len;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	atomic_set(&volume->usage, 1);
928c2ecf20Sopenharmony_ci	INIT_HLIST_NODE(&volume->proc_link);
938c2ecf20Sopenharmony_ci	rwlock_init(&volume->servers_lock);
948c2ecf20Sopenharmony_ci	rwlock_init(&volume->cb_v_break_lock);
958c2ecf20Sopenharmony_ci	memcpy(volume->name, vldb->name, vldb->name_len + 1);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	slist = afs_alloc_server_list(params->cell, params->key, vldb, type_mask);
988c2ecf20Sopenharmony_ci	if (IS_ERR(slist)) {
998c2ecf20Sopenharmony_ci		ret = PTR_ERR(slist);
1008c2ecf20Sopenharmony_ci		goto error_1;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	refcount_set(&slist->usage, 1);
1048c2ecf20Sopenharmony_ci	rcu_assign_pointer(volume->servers, slist);
1058c2ecf20Sopenharmony_ci	trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc);
1068c2ecf20Sopenharmony_ci	return volume;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cierror_1:
1098c2ecf20Sopenharmony_ci	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
1108c2ecf20Sopenharmony_ci	kfree(volume);
1118c2ecf20Sopenharmony_cierror_0:
1128c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * Look up or allocate a volume record.
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_cistatic struct afs_volume *afs_lookup_volume(struct afs_fs_context *params,
1198c2ecf20Sopenharmony_ci					    struct afs_vldb_entry *vldb,
1208c2ecf20Sopenharmony_ci					    unsigned long type_mask)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct afs_volume *candidate, *volume;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	candidate = afs_alloc_volume(params, vldb, type_mask);
1258c2ecf20Sopenharmony_ci	if (IS_ERR(candidate))
1268c2ecf20Sopenharmony_ci		return candidate;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	volume = afs_insert_volume_into_cell(params->cell, candidate);
1298c2ecf20Sopenharmony_ci	if (volume != candidate)
1308c2ecf20Sopenharmony_ci		afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup);
1318c2ecf20Sopenharmony_ci	return volume;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/*
1358c2ecf20Sopenharmony_ci * Look up a VLDB record for a volume.
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_cistatic struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
1388c2ecf20Sopenharmony_ci						 struct key *key,
1398c2ecf20Sopenharmony_ci						 const char *volname,
1408c2ecf20Sopenharmony_ci						 size_t volnamesz)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
1438c2ecf20Sopenharmony_ci	struct afs_vl_cursor vc;
1448c2ecf20Sopenharmony_ci	int ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (!afs_begin_vlserver_operation(&vc, cell, key))
1478c2ecf20Sopenharmony_ci		return ERR_PTR(-ERESTARTSYS);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	while (afs_select_vlserver(&vc)) {
1508c2ecf20Sopenharmony_ci		vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ret = afs_end_vlserver_operation(&vc);
1548c2ecf20Sopenharmony_ci	return ret < 0 ? ERR_PTR(ret) : vldb;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/*
1588c2ecf20Sopenharmony_ci * Look up a volume in the VL server and create a candidate volume record for
1598c2ecf20Sopenharmony_ci * it.
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci * The volume name can be one of the following:
1628c2ecf20Sopenharmony_ci *	"%[cell:]volume[.]"		R/W volume
1638c2ecf20Sopenharmony_ci *	"#[cell:]volume[.]"		R/O or R/W volume (rwparent=0),
1648c2ecf20Sopenharmony_ci *					 or R/W (rwparent=1) volume
1658c2ecf20Sopenharmony_ci *	"%[cell:]volume.readonly"	R/O volume
1668c2ecf20Sopenharmony_ci *	"#[cell:]volume.readonly"	R/O volume
1678c2ecf20Sopenharmony_ci *	"%[cell:]volume.backup"		Backup volume
1688c2ecf20Sopenharmony_ci *	"#[cell:]volume.backup"		Backup volume
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * The cell name is optional, and defaults to the current cell.
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
1738c2ecf20Sopenharmony_ci * Guide
1748c2ecf20Sopenharmony_ci * - Rule 1: Explicit type suffix forces access of that type or nothing
1758c2ecf20Sopenharmony_ci *           (no suffix, then use Rule 2 & 3)
1768c2ecf20Sopenharmony_ci * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
1778c2ecf20Sopenharmony_ci *           if not available
1788c2ecf20Sopenharmony_ci * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
1798c2ecf20Sopenharmony_ci *           explicitly told otherwise
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistruct afs_volume *afs_create_volume(struct afs_fs_context *params)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct afs_vldb_entry *vldb;
1848c2ecf20Sopenharmony_ci	struct afs_volume *volume;
1858c2ecf20Sopenharmony_ci	unsigned long type_mask = 1UL << params->type;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	vldb = afs_vl_lookup_vldb(params->cell, params->key,
1888c2ecf20Sopenharmony_ci				  params->volname, params->volnamesz);
1898c2ecf20Sopenharmony_ci	if (IS_ERR(vldb))
1908c2ecf20Sopenharmony_ci		return ERR_CAST(vldb);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
1938c2ecf20Sopenharmony_ci		volume = ERR_PTR(vldb->error);
1948c2ecf20Sopenharmony_ci		goto error;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Make the final decision on the type we want */
1988c2ecf20Sopenharmony_ci	volume = ERR_PTR(-ENOMEDIUM);
1998c2ecf20Sopenharmony_ci	if (params->force) {
2008c2ecf20Sopenharmony_ci		if (!(vldb->flags & type_mask))
2018c2ecf20Sopenharmony_ci			goto error;
2028c2ecf20Sopenharmony_ci	} else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
2038c2ecf20Sopenharmony_ci		params->type = AFSVL_ROVOL;
2048c2ecf20Sopenharmony_ci	} else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
2058c2ecf20Sopenharmony_ci		params->type = AFSVL_RWVOL;
2068c2ecf20Sopenharmony_ci	} else {
2078c2ecf20Sopenharmony_ci		goto error;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	type_mask = 1UL << params->type;
2118c2ecf20Sopenharmony_ci	volume = afs_lookup_volume(params, vldb, type_mask);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cierror:
2148c2ecf20Sopenharmony_ci	kfree(vldb);
2158c2ecf20Sopenharmony_ci	return volume;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/*
2198c2ecf20Sopenharmony_ci * Destroy a volume record
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cistatic void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	_enter("%p", volume);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
2268c2ecf20Sopenharmony_ci	ASSERTCMP(volume->cache, ==, NULL);
2278c2ecf20Sopenharmony_ci#endif
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	afs_remove_volume_from_cell(volume);
2308c2ecf20Sopenharmony_ci	afs_put_serverlist(net, rcu_access_pointer(volume->servers));
2318c2ecf20Sopenharmony_ci	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
2328c2ecf20Sopenharmony_ci	trace_afs_volume(volume->vid, atomic_read(&volume->usage),
2338c2ecf20Sopenharmony_ci			 afs_volume_trace_free);
2348c2ecf20Sopenharmony_ci	kfree_rcu(volume, rcu);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	_leave(" [destroyed]");
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/*
2408c2ecf20Sopenharmony_ci * Get a reference on a volume record.
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_cistruct afs_volume *afs_get_volume(struct afs_volume *volume,
2438c2ecf20Sopenharmony_ci				  enum afs_volume_trace reason)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	if (volume) {
2468c2ecf20Sopenharmony_ci		int u = atomic_inc_return(&volume->usage);
2478c2ecf20Sopenharmony_ci		trace_afs_volume(volume->vid, u, reason);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	return volume;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/*
2548c2ecf20Sopenharmony_ci * Drop a reference on a volume record.
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_civoid afs_put_volume(struct afs_net *net, struct afs_volume *volume,
2578c2ecf20Sopenharmony_ci		    enum afs_volume_trace reason)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	if (volume) {
2608c2ecf20Sopenharmony_ci		afs_volid_t vid = volume->vid;
2618c2ecf20Sopenharmony_ci		int u = atomic_dec_return(&volume->usage);
2628c2ecf20Sopenharmony_ci		trace_afs_volume(vid, u, reason);
2638c2ecf20Sopenharmony_ci		if (u == 0)
2648c2ecf20Sopenharmony_ci			afs_destroy_volume(net, volume);
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * Activate a volume.
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_civoid afs_activate_volume(struct afs_volume *volume)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
2748c2ecf20Sopenharmony_ci	volume->cache = fscache_acquire_cookie(volume->cell->cache,
2758c2ecf20Sopenharmony_ci					       &afs_volume_cache_index_def,
2768c2ecf20Sopenharmony_ci					       &volume->vid, sizeof(volume->vid),
2778c2ecf20Sopenharmony_ci					       NULL, 0,
2788c2ecf20Sopenharmony_ci					       volume, 0, true);
2798c2ecf20Sopenharmony_ci#endif
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/*
2838c2ecf20Sopenharmony_ci * Deactivate a volume.
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_civoid afs_deactivate_volume(struct afs_volume *volume)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	_enter("%s", volume->name);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
2908c2ecf20Sopenharmony_ci	fscache_relinquish_cookie(volume->cache, NULL,
2918c2ecf20Sopenharmony_ci				  test_bit(AFS_VOLUME_DELETED, &volume->flags));
2928c2ecf20Sopenharmony_ci	volume->cache = NULL;
2938c2ecf20Sopenharmony_ci#endif
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	_leave("");
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci/*
2998c2ecf20Sopenharmony_ci * Query the VL service to update the volume status.
3008c2ecf20Sopenharmony_ci */
3018c2ecf20Sopenharmony_cistatic int afs_update_volume_status(struct afs_volume *volume, struct key *key)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct afs_server_list *new, *old, *discard;
3048c2ecf20Sopenharmony_ci	struct afs_vldb_entry *vldb;
3058c2ecf20Sopenharmony_ci	char idbuf[16];
3068c2ecf20Sopenharmony_ci	int ret, idsz;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	_enter("");
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* We look up an ID by passing it as a decimal string in the
3118c2ecf20Sopenharmony_ci	 * operation's name parameter.
3128c2ecf20Sopenharmony_ci	 */
3138c2ecf20Sopenharmony_ci	idsz = sprintf(idbuf, "%llu", volume->vid);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
3168c2ecf20Sopenharmony_ci	if (IS_ERR(vldb)) {
3178c2ecf20Sopenharmony_ci		ret = PTR_ERR(vldb);
3188c2ecf20Sopenharmony_ci		goto error;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* See if the volume got renamed. */
3228c2ecf20Sopenharmony_ci	if (vldb->name_len != volume->name_len ||
3238c2ecf20Sopenharmony_ci	    memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
3248c2ecf20Sopenharmony_ci		/* TODO: Use RCU'd string. */
3258c2ecf20Sopenharmony_ci		memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
3268c2ecf20Sopenharmony_ci		volume->name_len = vldb->name_len;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* See if the volume's server list got updated. */
3308c2ecf20Sopenharmony_ci	new = afs_alloc_server_list(volume->cell, key,
3318c2ecf20Sopenharmony_ci				    vldb, (1 << volume->type));
3328c2ecf20Sopenharmony_ci	if (IS_ERR(new)) {
3338c2ecf20Sopenharmony_ci		ret = PTR_ERR(new);
3348c2ecf20Sopenharmony_ci		goto error_vldb;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	write_lock(&volume->servers_lock);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	discard = new;
3408c2ecf20Sopenharmony_ci	old = rcu_dereference_protected(volume->servers,
3418c2ecf20Sopenharmony_ci					lockdep_is_held(&volume->servers_lock));
3428c2ecf20Sopenharmony_ci	if (afs_annotate_server_list(new, old)) {
3438c2ecf20Sopenharmony_ci		new->seq = volume->servers_seq + 1;
3448c2ecf20Sopenharmony_ci		rcu_assign_pointer(volume->servers, new);
3458c2ecf20Sopenharmony_ci		smp_wmb();
3468c2ecf20Sopenharmony_ci		volume->servers_seq++;
3478c2ecf20Sopenharmony_ci		discard = old;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
3518c2ecf20Sopenharmony_ci	write_unlock(&volume->servers_lock);
3528c2ecf20Sopenharmony_ci	ret = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	afs_put_serverlist(volume->cell->net, discard);
3558c2ecf20Sopenharmony_cierror_vldb:
3568c2ecf20Sopenharmony_ci	kfree(vldb);
3578c2ecf20Sopenharmony_cierror:
3588c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
3598c2ecf20Sopenharmony_ci	return ret;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/*
3638c2ecf20Sopenharmony_ci * Make sure the volume record is up to date.
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_ciint afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	int ret, retries = 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	_enter("");
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ciretry:
3728c2ecf20Sopenharmony_ci	if (test_bit(AFS_VOLUME_WAIT, &volume->flags))
3738c2ecf20Sopenharmony_ci		goto wait;
3748c2ecf20Sopenharmony_ci	if (volume->update_at <= ktime_get_real_seconds() ||
3758c2ecf20Sopenharmony_ci	    test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags))
3768c2ecf20Sopenharmony_ci		goto update;
3778c2ecf20Sopenharmony_ci	_leave(" = 0");
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ciupdate:
3818c2ecf20Sopenharmony_ci	if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
3828c2ecf20Sopenharmony_ci		clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
3838c2ecf20Sopenharmony_ci		ret = afs_update_volume_status(volume, op->key);
3848c2ecf20Sopenharmony_ci		if (ret < 0)
3858c2ecf20Sopenharmony_ci			set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
3868c2ecf20Sopenharmony_ci		clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
3878c2ecf20Sopenharmony_ci		clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
3888c2ecf20Sopenharmony_ci		wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
3898c2ecf20Sopenharmony_ci		_leave(" = %d", ret);
3908c2ecf20Sopenharmony_ci		return ret;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ciwait:
3948c2ecf20Sopenharmony_ci	if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
3958c2ecf20Sopenharmony_ci		_leave(" = 0 [no wait]");
3968c2ecf20Sopenharmony_ci		return 0;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
4008c2ecf20Sopenharmony_ci			  (op->flags & AFS_OPERATION_UNINTR) ?
4018c2ecf20Sopenharmony_ci			  TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
4028c2ecf20Sopenharmony_ci	if (ret == -ERESTARTSYS) {
4038c2ecf20Sopenharmony_ci		_leave(" = %d", ret);
4048c2ecf20Sopenharmony_ci		return ret;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	retries++;
4088c2ecf20Sopenharmony_ci	if (retries == 4) {
4098c2ecf20Sopenharmony_ci		_leave(" = -ESTALE");
4108c2ecf20Sopenharmony_ci		return -ESTALE;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci	goto retry;
4138c2ecf20Sopenharmony_ci}
414