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