// 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]"); return0;
}
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.0.3Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-05)
¤
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.