// SPDX-License-Identifier: GPL-2.0-or-later /* AFS volume management * * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Insert a volume into a cell. If there's an existing volume record, that is * returned instead with a ref held.
*/ staticstruct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell, struct afs_volume *volume)
{ struct afs_volume *p; struct rb_node *parent = NULL, **pp;
if (!hlist_unhashed(&volume->proc_link)) {
trace_afs_volume(volume->debug_id, volume->vid, refcount_read(&volume->ref),
afs_volume_trace_remove);
write_seqlock(&cell->volume_lock);
hlist_del_rcu(&volume->proc_link); if (!test_and_set_bit(AFS_VOLUME_RM_TREE, &volume->flags))
rb_erase(&volume->cell_node, &cell->volumes);
write_sequnlock(&cell->volume_lock);
}
}
/* * Allocate a volume record and load it up from a vldb record.
*/ staticstruct afs_volume *afs_alloc_volume(struct afs_fs_context *params, struct afs_vldb_entry *vldb, struct afs_server_list **_slist)
{ struct afs_server_list *slist; struct afs_volume *volume; int ret = -ENOMEM, i;
volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL); if (!volume) goto error_0;
/* * Look up a VLDB record for a volume.
*/ staticstruct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell, struct key *key, constchar *volname,
size_t volnamesz)
{ struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ); struct afs_vl_cursor vc; int ret;
if (!afs_begin_vlserver_operation(&vc, cell, key)) return ERR_PTR(-ERESTARTSYS);
while (afs_select_vlserver(&vc)) {
vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
}
ret = afs_end_vlserver_operation(&vc); return ret < 0 ? ERR_PTR(ret) : vldb;
}
/* * Look up a volume in the VL server and create a candidate volume record for * it. * * The volume name can be one of the following: * "%[cell:]volume[.]" R/W volume * "#[cell:]volume[.]" R/O or R/W volume (rwparent=0), * or R/W (rwparent=1) volume * "%[cell:]volume.readonly" R/O volume * "#[cell:]volume.readonly" R/O volume * "%[cell:]volume.backup" Backup volume * "#[cell:]volume.backup" Backup volume * * The cell name is optional, and defaults to the current cell. * * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin * Guide * - Rule 1: Explicit type suffix forces access of that type or nothing * (no suffix, then use Rule 2 & 3) * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W * if not available * - Rule 3: If parent volume is R/W, then only mount R/W volume unless * explicitly told otherwise
*/ struct afs_volume *afs_create_volume(struct afs_fs_context *params)
{ struct afs_vldb_entry *vldb; struct afs_volume *volume; unsignedlong type_mask = 1UL << params->type;
vldb = afs_vl_lookup_vldb(params->cell, params->key,
params->volname, params->volnamesz); if (IS_ERR(vldb)) return ERR_CAST(vldb);
if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
volume = ERR_PTR(vldb->error); goto error;
}
/* Make the final decision on the type we want */
volume = ERR_PTR(-ENOMEDIUM); if (params->force) { if (!(vldb->flags & type_mask)) goto error;
} elseif (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
params->type = AFSVL_ROVOL;
} elseif (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
params->type = AFSVL_RWVOL;
} else { goto error;
}
/* * Try to get a reference on a volume record.
*/ bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason)
{ int r;
if (__refcount_inc_not_zero(&volume->ref, &r)) {
trace_afs_volume(volume->debug_id, volume->vid, r + 1, reason); returntrue;
} returnfalse;
}
/* * Get a reference on a volume record.
*/ struct afs_volume *afs_get_volume(struct afs_volume *volume, enum afs_volume_trace reason)
{ if (volume) { int r;
/* * Drop a reference on a volume record.
*/ void afs_put_volume(struct afs_volume *volume, enum afs_volume_trace reason)
{ if (volume) { unsignedint debug_id = volume->debug_id;
afs_volid_t vid = volume->vid; bool zero; int r;
zero = __refcount_dec_and_test(&volume->ref, &r);
trace_afs_volume(debug_id, vid, r - 1, reason); if (zero)
schedule_work(&volume->destructor);
}
}
/* * Activate a volume.
*/ int afs_activate_volume(struct afs_volume *volume)
{ #ifdef CONFIG_AFS_FSCACHE struct fscache_volume *vcookie; char *name;
name = kasprintf(GFP_KERNEL, "afs,%s,%llx",
volume->cell->name, volume->vid); if (!name) return -ENOMEM;
vcookie = fscache_acquire_volume(name, NULL, NULL, 0); if (IS_ERR(vcookie)) { if (vcookie != ERR_PTR(-EBUSY)) {
kfree(name); return PTR_ERR(vcookie);
}
pr_err("AFS: Cache volume key already in use (%s)\n", name);
vcookie = NULL;
}
volume->cache = vcookie;
kfree(name); #endif return 0;
}
/* * Query the VL service to update the volume status.
*/ staticint afs_update_volume_status(struct afs_volume *volume, struct key *key)
{ struct afs_server_list *new, *old, *discard; struct afs_vldb_entry *vldb; char idbuf[24]; int ret, idsz;
_enter("");
/* We look up an ID by passing it as a decimal string in the * operation's name parameter.
*/
idsz = snprintf(idbuf, sizeof(idbuf), "%llu", volume->vid);
vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz); if (IS_ERR(vldb)) {
ret = PTR_ERR(vldb); goto error;
}
/* See if the volume got renamed. */ if (vldb->name_len != volume->name_len ||
memcmp(vldb->name, volume->name, vldb->name_len) != 0) { /* TODO: Use RCU'd string. */
memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
volume->name_len = vldb->name_len;
}
/* See if the volume's server list got updated. */ new = afs_alloc_server_list(volume, key, vldb); if (IS_ERR(new)) {
ret = PTR_ERR(new); goto error_vldb;
}
/* Check more often if replication is ongoing. */ if (new->ro_replicating)
volume->update_at = ktime_get_real_seconds() + 10 * 60; else
volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
write_unlock(&volume->servers_lock);
/* * Make sure the volume record is up to date.
*/ int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
{ int ret, retries = 0;
_enter("");
retry: if (test_bit(AFS_VOLUME_WAIT, &volume->flags)) goto wait; if (volume->update_at <= ktime_get_real_seconds() ||
test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags)) goto update;
_leave(" = 0"); return 0;
update: if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
ret = afs_update_volume_status(volume, op->key); if (ret < 0)
set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
_leave(" = %d", ret); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.