// SPDX-License-Identifier: GPL-2.0-or-later /* AFS cell and server record management * * Copyright (C) 2002, 2017 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
staticvoid afs_dec_cells_outstanding(struct afs_net *net)
{ if (atomic_dec_and_test(&net->cells_outstanding))
wake_up_var(&net->cells_outstanding);
}
staticvoid afs_set_cell_state(struct afs_cell *cell, enum afs_cell_state state)
{
smp_store_release(&cell->state, state); /* Commit cell changes before state */
smp_wmb(); /* Set cell state before task state */
wake_up_var(&cell->state);
}
/* * Look up and get an activation reference on a cell record. The caller must * hold net->cells_lock at least read-locked.
*/ staticstruct afs_cell *afs_find_cell_locked(struct afs_net *net, constchar *name, unsignedint namesz, enum afs_cell_trace reason)
{ struct afs_cell *cell = NULL; struct rb_node *p; int n;
_enter("%*.*s", namesz, namesz, name);
if (name && namesz == 0) return ERR_PTR(-EINVAL); if (namesz > AFS_MAXCELLNAME) return ERR_PTR(-ENAMETOOLONG);
if (!name) {
cell = rcu_dereference_protected(net->ws_cell,
lockdep_is_held(&net->cells_lock)); if (!cell) return ERR_PTR(-EDESTADDRREQ); goto found;
}
p = net->cells.rb_node; while (p) {
cell = rb_entry(p, struct afs_cell, net_node);
n = strncasecmp(cell->name, name,
min_t(size_t, cell->name_len, namesz)); if (n == 0)
n = cell->name_len - namesz; if (n < 0)
p = p->rb_left; elseif (n > 0)
p = p->rb_right; else goto found;
}
return ERR_PTR(-ENOENT);
found: return afs_use_cell(cell, reason);
}
/* * Look up and get an activation reference on a cell record.
*/ struct afs_cell *afs_find_cell(struct afs_net *net, constchar *name, unsignedint namesz, enum afs_cell_trace reason)
{ struct afs_cell *cell;
/* * Set up a cell record and fill in its name, VL server address list and * allocate an anonymous key
*/ staticstruct afs_cell *afs_alloc_cell(struct afs_net *net, constchar *name, unsignedint namelen, constchar *addresses)
{ struct afs_vlserver_list *vllist = NULL; struct afs_cell *cell; int i, ret;
ASSERT(name); if (namelen == 0) return ERR_PTR(-EINVAL); if (namelen > AFS_MAXCELLNAME) {
_leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG);
}
/* Prohibit cell names that contain unprintable chars, '/' and '@' or * that begin with a dot. This also precludes "@cell".
*/ if (name[0] == '.') return ERR_PTR(-EINVAL); for (i = 0; i < namelen; i++) { char ch = name[i]; if (!isprint(ch) || ch == '/' || ch == '@') return ERR_PTR(-EINVAL);
}
/* Provide a VL server list, filling it in if we were given a list of * addresses to use.
*/ if (addresses) {
vllist = afs_parse_text_addrs(net,
addresses, strlen(addresses), ':',
VL_SERVICE, AFS_VL_PORT); if (IS_ERR(vllist)) {
ret = PTR_ERR(vllist);
vllist = NULL; goto parse_failed;
}
parse_failed: if (ret == -EINVAL)
printk(KERN_ERR "kAFS: bad VL server IP address\n");
error:
afs_put_vlserverlist(cell->net, vllist);
kfree(cell->name - 1);
kfree(cell);
_leave(" = %d", ret); return ERR_PTR(ret);
}
/* * afs_lookup_cell - Look up or create a cell record. * @net: The network namespace * @name: The name of the cell. * @namesz: The strlen of the cell name. * @vllist: A colon/comma separated list of numeric IP addresses or NULL. * @reason: The reason we're doing the lookup * @trace: The reason to be logged if the lookup is successful. * * Look up a cell record by name and query the DNS for VL server addresses if * needed. Note that that actual DNS query is punted off to the manager thread * so that this function can return immediately if interrupted whilst allowing * cell records to be shared even if not yet fully constructed.
*/ struct afs_cell *afs_lookup_cell(struct afs_net *net, constchar *name, unsignedint namesz, constchar *vllist, enum afs_lookup_cell_for reason, enum afs_cell_trace trace)
{ struct afs_cell *cell, *candidate, *cursor; struct rb_node *parent, **pp; enum afs_cell_state state; int ret, n;
_enter("%s,%s,%u", name, vllist, reason);
if (reason != AFS_LOOKUP_CELL_PRELOAD) {
cell = afs_find_cell(net, name, namesz, trace); if (!IS_ERR(cell)) { if (reason == AFS_LOOKUP_CELL_DYNROOT) goto no_wait; if (cell->state == AFS_CELL_SETTING_UP ||
cell->state == AFS_CELL_UNLOOKED) goto lookup_cell; goto wait_for_cell;
}
}
/* Assume we're probably going to create a cell and preallocate and * mostly set up a candidate record. We can then use this to stash the * name, the net namespace and VL server addresses. * * We also want to do this before we hold any locks as it may involve * upcalling to userspace to make DNS queries.
*/
candidate = afs_alloc_cell(net, name, namesz, vllist); if (IS_ERR(candidate)) {
_leave(" = %ld", PTR_ERR(candidate)); return candidate;
}
/* Find the insertion point and check to see if someone else added a * cell whilst we were allocating.
*/
down_write(&net->cells_lock);
wait_for_cell:
state = smp_load_acquire(&cell->state); /* vs error */ switch (state) { case AFS_CELL_ACTIVE: case AFS_CELL_DEAD: break; case AFS_CELL_UNLOOKED: default: if (reason == AFS_LOOKUP_CELL_PRELOAD ||
reason == AFS_LOOKUP_CELL_ROOTCELL) break;
_debug("wait_for_cell");
afs_see_cell(cell, afs_cell_trace_wait);
wait_var_event(&cell->state,
({
state = smp_load_acquire(&cell->state); /* vs error */
state == AFS_CELL_ACTIVE || state == AFS_CELL_DEAD;
}));
_debug("waited_for_cell %d %d", cell->state, cell->error);
}
no_wait: /* Check the state obtained from the wait check. */
state = smp_load_acquire(&cell->state); /* vs error */ if (state == AFS_CELL_DEAD) {
ret = cell->error; goto error;
} if (state == AFS_CELL_ACTIVE) { switch (cell->dns_status) { case DNS_LOOKUP_NOT_DONE: if (cell->dns_source == DNS_RECORD_FROM_CONFIG) {
ret = 0; break;
}
fallthrough; default:
ret = -EIO; goto error; case DNS_LOOKUP_GOOD: case DNS_LOOKUP_GOOD_WITH_BAD:
ret = 0; break; case DNS_LOOKUP_GOT_NOT_FOUND:
ret = -ENOENT; goto error; case DNS_LOOKUP_BAD:
ret = -EREMOTEIO; goto error; case DNS_LOOKUP_GOT_LOCAL_FAILURE: case DNS_LOOKUP_GOT_TEMP_FAILURE: case DNS_LOOKUP_GOT_NS_FAILURE:
ret = -EDESTADDRREQ; goto error;
}
}
_leave(" = %p [cell]", cell); return cell;
cell_already_exists:
_debug("cell exists");
cell = cursor; if (reason == AFS_LOOKUP_CELL_PRELOAD) {
ret = -EEXIST;
} else {
afs_use_cell(cursor, trace);
ret = 0;
}
up_write(&net->cells_lock); if (candidate)
afs_put_cell(candidate, afs_cell_trace_put_candidate); if (ret == 0) goto wait_for_cell; goto error_noput;
error:
afs_unuse_cell(cell, afs_cell_trace_unuse_lookup_error);
error_noput:
_leave(" = %d [error]", ret); return ERR_PTR(ret);
}
/* * set the root cell information * - can be called with a module parameter string * - can be called from a write to /proc/fs/afs/rootcell
*/ int afs_cell_init(struct afs_net *net, constchar *rootcell)
{ struct afs_cell *old_root, *new_root; constchar *cp, *vllist;
size_t len;
_enter("");
if (!rootcell) { /* module is loaded with no parameters, or built statically. * - in the future we might initialize cell DB here.
*/
_leave(" = 0 [no root]"); return 0;
}
cp = strchr(rootcell, ':'); if (!cp) {
_debug("kAFS: no VL server IP addresses specified");
vllist = NULL;
len = strlen(rootcell);
} else {
vllist = cp + 1;
len = cp - rootcell;
}
now = ktime_get_real_seconds(); if (min_ttl > max_ttl)
max_ttl = min_ttl; if (expiry < now + min_ttl)
expiry = now + min_ttl; elseif (expiry > now + max_ttl)
expiry = now + max_ttl;
_debug("%s: status %d", cell->name, vllist->status); if (vllist->source == DNS_RECORD_UNAVAILABLE) { switch (vllist->status) { case DNS_LOOKUP_GOT_NOT_FOUND: /* The DNS said that the cell does not exist or there * weren't any addresses to be had.
*/
cell->dns_expiry = expiry; break;
case DNS_LOOKUP_BAD: case DNS_LOOKUP_GOT_LOCAL_FAILURE: case DNS_LOOKUP_GOT_TEMP_FAILURE: case DNS_LOOKUP_GOT_NS_FAILURE: default:
cell->dns_expiry = now + 10; break;
}
} else {
cell->dns_expiry = expiry;
}
/* Replace the VL server list if the new record has servers or the old * record doesn't.
*/
write_lock(&cell->vl_servers_lock);
p = rcu_dereference_protected(cell->vl_servers, true); if (vllist->nr_servers > 0 || p->nr_servers == 0) {
rcu_assign_pointer(cell->vl_servers, vllist);
cell->dns_source = vllist->source;
old = p;
}
write_unlock(&cell->vl_servers_lock);
afs_put_vlserverlist(cell->net, old);
/* * Drop a reference on a cell record.
*/ void afs_put_cell(struct afs_cell *cell, enum afs_cell_trace reason)
{ if (cell) { unsignedint debug_id = cell->debug_id; unsignedint a; bool zero; int r;
a = atomic_read(&cell->active);
zero = __refcount_dec_and_test(&cell->ref, &r);
trace_afs_cell(debug_id, r - 1, a, reason); if (zero) {
a = atomic_read(&cell->active);
WARN(a != 0, "Cell active count %u > 0\n", a);
WARN_ON(!queue_work(afs_wq, &cell->destroyer));
}
}
}
/* * Note a cell becoming more active.
*/ struct afs_cell *afs_use_cell(struct afs_cell *cell, enum afs_cell_trace reason)
{ int r, a;
__refcount_inc(&cell->ref, &r);
a = atomic_inc_return(&cell->active);
trace_afs_cell(cell->debug_id, r + 1, a, reason); return cell;
}
/* * Record a cell becoming less active. When the active counter reaches 1, it * is scheduled for destruction, but may get reactivated.
*/ void afs_unuse_cell(struct afs_cell *cell, enum afs_cell_trace reason)
{ unsignedint debug_id;
time64_t now, expire_delay; bool zero; int r, a;
if (!cell) return;
_enter("%s", cell->name);
now = ktime_get_real_seconds();
cell->last_inactive = now;
expire_delay = 0; if (cell->vl_servers->nr_servers)
expire_delay = afs_cell_gc_delay;
debug_id = cell->debug_id;
a = atomic_dec_return(&cell->active); if (!a) /* 'cell' may now be garbage collected. */
afs_set_cell_timer(cell, expire_delay);
zero = __refcount_dec_and_test(&cell->ref, &r);
trace_afs_cell(debug_id, r - 1, a, reason); if (zero)
WARN_ON(!queue_work(afs_wq, &cell->destroyer));
}
/* * Note that a cell has been seen.
*/ void afs_see_cell(struct afs_cell *cell, enum afs_cell_trace reason)
{ int r, a;
r = refcount_read(&cell->ref);
a = atomic_read(&cell->active);
trace_afs_cell(cell->debug_id, r, a, reason);
}
/* * Queue a cell for management, giving the workqueue a ref to hold.
*/ void afs_queue_cell(struct afs_cell *cell, enum afs_cell_trace reason)
{
queue_work(afs_wq, &cell->manager);
}
/* * Allocate a key to use as a placeholder for anonymous user security.
*/ staticint afs_alloc_anon_key(struct afs_cell *cell)
{ struct key *key; char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp;
/* Create a key to represent an anonymous user. */
memcpy(keyname, "afs@", 4);
dp = keyname + 4;
cp = cell->name; do {
*dp++ = tolower(*cp);
} while (*cp++);
key = rxrpc_get_null_key(keyname); if (IS_ERR(key)) return PTR_ERR(key);
if (expire_at <= now) returntrue; if (expire_at < *_next_manage)
*_next_manage = expire_at; returnfalse;
}
/* * Manage a cell record, initialising and destroying it, maintaining its DNS * records.
*/ staticbool afs_manage_cell(struct afs_cell *cell)
{ struct afs_net *net = cell->net;
time64_t next_manage = TIME64_MAX; int ret;
_enter("%s", cell->name);
_debug("state %u", cell->state); switch (cell->state) { case AFS_CELL_SETTING_UP: goto set_up_cell; case AFS_CELL_UNLOOKED: case AFS_CELL_ACTIVE: goto cell_is_active; case AFS_CELL_REMOVING:
WARN_ON_ONCE(1); returnfalse; case AFS_CELL_DEAD: returnfalse; default:
_debug("bad state %u", cell->state);
WARN_ON_ONCE(1); /* Unhandled state */ returnfalse;
}
set_up_cell:
ret = afs_activate_cell(net, cell); if (ret < 0) {
cell->error = ret; goto remove_cell;
}
afs_set_cell_state(cell, AFS_CELL_UNLOOKED);
cell_is_active: if (afs_has_cell_expired(cell, &next_manage)) goto remove_cell;
if (test_and_clear_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) {
ret = afs_update_cell(cell); if (ret < 0)
cell->error = ret; if (cell->state == AFS_CELL_UNLOOKED)
afs_set_cell_state(cell, AFS_CELL_ACTIVE);
}
if (next_manage < TIME64_MAX && cell->net->live) {
time64_t now = ktime_get_real_seconds();
if (next_manage - now <= 0)
afs_queue_cell(cell, afs_cell_trace_queue_again); else
afs_set_cell_timer(cell, next_manage - now);
}
_leave(" [done %u]", cell->state); returnfalse;
remove_cell:
down_write(&net->cells_lock);
if (atomic_read(&cell->active)) {
up_write(&net->cells_lock); goto cell_is_active;
}
/* Make sure that the expiring server records are going to see the fact * that the cell is caput.
*/
afs_set_cell_state(cell, AFS_CELL_REMOVING);
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.