162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* AFS cell and server record management 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2002, 2017 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/key.h> 1062306a36Sopenharmony_ci#include <linux/ctype.h> 1162306a36Sopenharmony_ci#include <linux/dns_resolver.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/inet.h> 1462306a36Sopenharmony_ci#include <linux/namei.h> 1562306a36Sopenharmony_ci#include <keys/rxrpc-type.h> 1662306a36Sopenharmony_ci#include "internal.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic unsigned __read_mostly afs_cell_gc_delay = 10; 1962306a36Sopenharmony_cistatic unsigned __read_mostly afs_cell_min_ttl = 10 * 60; 2062306a36Sopenharmony_cistatic unsigned __read_mostly afs_cell_max_ttl = 24 * 60 * 60; 2162306a36Sopenharmony_cistatic atomic_t cell_debug_id; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void afs_queue_cell_manager(struct afs_net *); 2462306a36Sopenharmony_cistatic void afs_manage_cell_work(struct work_struct *); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void afs_dec_cells_outstanding(struct afs_net *net) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci if (atomic_dec_and_test(&net->cells_outstanding)) 2962306a36Sopenharmony_ci wake_up_var(&net->cells_outstanding); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * Set the cell timer to fire after a given delay, assuming it's not already 3462306a36Sopenharmony_ci * set for an earlier time. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic void afs_set_cell_timer(struct afs_net *net, time64_t delay) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci if (net->live) { 3962306a36Sopenharmony_ci atomic_inc(&net->cells_outstanding); 4062306a36Sopenharmony_ci if (timer_reduce(&net->cells_timer, jiffies + delay * HZ)) 4162306a36Sopenharmony_ci afs_dec_cells_outstanding(net); 4262306a36Sopenharmony_ci } else { 4362306a36Sopenharmony_ci afs_queue_cell_manager(net); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * Look up and get an activation reference on a cell record. The caller must 4962306a36Sopenharmony_ci * hold net->cells_lock at least read-locked. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistatic struct afs_cell *afs_find_cell_locked(struct afs_net *net, 5262306a36Sopenharmony_ci const char *name, unsigned int namesz, 5362306a36Sopenharmony_ci enum afs_cell_trace reason) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct afs_cell *cell = NULL; 5662306a36Sopenharmony_ci struct rb_node *p; 5762306a36Sopenharmony_ci int n; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci _enter("%*.*s", namesz, namesz, name); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (name && namesz == 0) 6262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 6362306a36Sopenharmony_ci if (namesz > AFS_MAXCELLNAME) 6462306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (!name) { 6762306a36Sopenharmony_ci cell = net->ws_cell; 6862306a36Sopenharmony_ci if (!cell) 6962306a36Sopenharmony_ci return ERR_PTR(-EDESTADDRREQ); 7062306a36Sopenharmony_ci goto found; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci p = net->cells.rb_node; 7462306a36Sopenharmony_ci while (p) { 7562306a36Sopenharmony_ci cell = rb_entry(p, struct afs_cell, net_node); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci n = strncasecmp(cell->name, name, 7862306a36Sopenharmony_ci min_t(size_t, cell->name_len, namesz)); 7962306a36Sopenharmony_ci if (n == 0) 8062306a36Sopenharmony_ci n = cell->name_len - namesz; 8162306a36Sopenharmony_ci if (n < 0) 8262306a36Sopenharmony_ci p = p->rb_left; 8362306a36Sopenharmony_ci else if (n > 0) 8462306a36Sopenharmony_ci p = p->rb_right; 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci goto found; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cifound: 9262306a36Sopenharmony_ci return afs_use_cell(cell, reason); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Look up and get an activation reference on a cell record. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistruct afs_cell *afs_find_cell(struct afs_net *net, 9962306a36Sopenharmony_ci const char *name, unsigned int namesz, 10062306a36Sopenharmony_ci enum afs_cell_trace reason) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct afs_cell *cell; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci down_read(&net->cells_lock); 10562306a36Sopenharmony_ci cell = afs_find_cell_locked(net, name, namesz, reason); 10662306a36Sopenharmony_ci up_read(&net->cells_lock); 10762306a36Sopenharmony_ci return cell; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * Set up a cell record and fill in its name, VL server address list and 11262306a36Sopenharmony_ci * allocate an anonymous key 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_cistatic struct afs_cell *afs_alloc_cell(struct afs_net *net, 11562306a36Sopenharmony_ci const char *name, unsigned int namelen, 11662306a36Sopenharmony_ci const char *addresses) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct afs_vlserver_list *vllist; 11962306a36Sopenharmony_ci struct afs_cell *cell; 12062306a36Sopenharmony_ci int i, ret; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ASSERT(name); 12362306a36Sopenharmony_ci if (namelen == 0) 12462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 12562306a36Sopenharmony_ci if (namelen > AFS_MAXCELLNAME) { 12662306a36Sopenharmony_ci _leave(" = -ENAMETOOLONG"); 12762306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Prohibit cell names that contain unprintable chars, '/' and '@' or 13162306a36Sopenharmony_ci * that begin with a dot. This also precludes "@cell". 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (name[0] == '.') 13462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 13562306a36Sopenharmony_ci for (i = 0; i < namelen; i++) { 13662306a36Sopenharmony_ci char ch = name[i]; 13762306a36Sopenharmony_ci if (!isprint(ch) || ch == '/' || ch == '@') 13862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci _enter("%*.*s,%s", namelen, namelen, name, addresses); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci cell = kzalloc(sizeof(struct afs_cell), GFP_KERNEL); 14462306a36Sopenharmony_ci if (!cell) { 14562306a36Sopenharmony_ci _leave(" = -ENOMEM"); 14662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci cell->name = kmalloc(namelen + 1, GFP_KERNEL); 15062306a36Sopenharmony_ci if (!cell->name) { 15162306a36Sopenharmony_ci kfree(cell); 15262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci cell->net = net; 15662306a36Sopenharmony_ci cell->name_len = namelen; 15762306a36Sopenharmony_ci for (i = 0; i < namelen; i++) 15862306a36Sopenharmony_ci cell->name[i] = tolower(name[i]); 15962306a36Sopenharmony_ci cell->name[i] = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci refcount_set(&cell->ref, 1); 16262306a36Sopenharmony_ci atomic_set(&cell->active, 0); 16362306a36Sopenharmony_ci INIT_WORK(&cell->manager, afs_manage_cell_work); 16462306a36Sopenharmony_ci cell->volumes = RB_ROOT; 16562306a36Sopenharmony_ci INIT_HLIST_HEAD(&cell->proc_volumes); 16662306a36Sopenharmony_ci seqlock_init(&cell->volume_lock); 16762306a36Sopenharmony_ci cell->fs_servers = RB_ROOT; 16862306a36Sopenharmony_ci seqlock_init(&cell->fs_lock); 16962306a36Sopenharmony_ci INIT_LIST_HEAD(&cell->fs_open_mmaps); 17062306a36Sopenharmony_ci init_rwsem(&cell->fs_open_mmaps_lock); 17162306a36Sopenharmony_ci rwlock_init(&cell->vl_servers_lock); 17262306a36Sopenharmony_ci cell->flags = (1 << AFS_CELL_FL_CHECK_ALIAS); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Provide a VL server list, filling it in if we were given a list of 17562306a36Sopenharmony_ci * addresses to use. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci if (addresses) { 17862306a36Sopenharmony_ci vllist = afs_parse_text_addrs(net, 17962306a36Sopenharmony_ci addresses, strlen(addresses), ':', 18062306a36Sopenharmony_ci VL_SERVICE, AFS_VL_PORT); 18162306a36Sopenharmony_ci if (IS_ERR(vllist)) { 18262306a36Sopenharmony_ci ret = PTR_ERR(vllist); 18362306a36Sopenharmony_ci goto parse_failed; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci vllist->source = DNS_RECORD_FROM_CONFIG; 18762306a36Sopenharmony_ci vllist->status = DNS_LOOKUP_NOT_DONE; 18862306a36Sopenharmony_ci cell->dns_expiry = TIME64_MAX; 18962306a36Sopenharmony_ci } else { 19062306a36Sopenharmony_ci ret = -ENOMEM; 19162306a36Sopenharmony_ci vllist = afs_alloc_vlserver_list(0); 19262306a36Sopenharmony_ci if (!vllist) 19362306a36Sopenharmony_ci goto error; 19462306a36Sopenharmony_ci vllist->source = DNS_RECORD_UNAVAILABLE; 19562306a36Sopenharmony_ci vllist->status = DNS_LOOKUP_NOT_DONE; 19662306a36Sopenharmony_ci cell->dns_expiry = ktime_get_real_seconds(); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci rcu_assign_pointer(cell->vl_servers, vllist); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci cell->dns_source = vllist->source; 20262306a36Sopenharmony_ci cell->dns_status = vllist->status; 20362306a36Sopenharmony_ci smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ 20462306a36Sopenharmony_ci atomic_inc(&net->cells_outstanding); 20562306a36Sopenharmony_ci cell->debug_id = atomic_inc_return(&cell_debug_id); 20662306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, 1, 0, afs_cell_trace_alloc); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci _leave(" = %p", cell); 20962306a36Sopenharmony_ci return cell; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciparse_failed: 21262306a36Sopenharmony_ci if (ret == -EINVAL) 21362306a36Sopenharmony_ci printk(KERN_ERR "kAFS: bad VL server IP address\n"); 21462306a36Sopenharmony_cierror: 21562306a36Sopenharmony_ci kfree(cell->name); 21662306a36Sopenharmony_ci kfree(cell); 21762306a36Sopenharmony_ci _leave(" = %d", ret); 21862306a36Sopenharmony_ci return ERR_PTR(ret); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* 22262306a36Sopenharmony_ci * afs_lookup_cell - Look up or create a cell record. 22362306a36Sopenharmony_ci * @net: The network namespace 22462306a36Sopenharmony_ci * @name: The name of the cell. 22562306a36Sopenharmony_ci * @namesz: The strlen of the cell name. 22662306a36Sopenharmony_ci * @vllist: A colon/comma separated list of numeric IP addresses or NULL. 22762306a36Sopenharmony_ci * @excl: T if an error should be given if the cell name already exists. 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * Look up a cell record by name and query the DNS for VL server addresses if 23062306a36Sopenharmony_ci * needed. Note that that actual DNS query is punted off to the manager thread 23162306a36Sopenharmony_ci * so that this function can return immediately if interrupted whilst allowing 23262306a36Sopenharmony_ci * cell records to be shared even if not yet fully constructed. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistruct afs_cell *afs_lookup_cell(struct afs_net *net, 23562306a36Sopenharmony_ci const char *name, unsigned int namesz, 23662306a36Sopenharmony_ci const char *vllist, bool excl) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct afs_cell *cell, *candidate, *cursor; 23962306a36Sopenharmony_ci struct rb_node *parent, **pp; 24062306a36Sopenharmony_ci enum afs_cell_state state; 24162306a36Sopenharmony_ci int ret, n; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci _enter("%s,%s", name, vllist); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (!excl) { 24662306a36Sopenharmony_ci cell = afs_find_cell(net, name, namesz, afs_cell_trace_use_lookup); 24762306a36Sopenharmony_ci if (!IS_ERR(cell)) 24862306a36Sopenharmony_ci goto wait_for_cell; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Assume we're probably going to create a cell and preallocate and 25262306a36Sopenharmony_ci * mostly set up a candidate record. We can then use this to stash the 25362306a36Sopenharmony_ci * name, the net namespace and VL server addresses. 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * We also want to do this before we hold any locks as it may involve 25662306a36Sopenharmony_ci * upcalling to userspace to make DNS queries. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci candidate = afs_alloc_cell(net, name, namesz, vllist); 25962306a36Sopenharmony_ci if (IS_ERR(candidate)) { 26062306a36Sopenharmony_ci _leave(" = %ld", PTR_ERR(candidate)); 26162306a36Sopenharmony_ci return candidate; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Find the insertion point and check to see if someone else added a 26562306a36Sopenharmony_ci * cell whilst we were allocating. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci down_write(&net->cells_lock); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci pp = &net->cells.rb_node; 27062306a36Sopenharmony_ci parent = NULL; 27162306a36Sopenharmony_ci while (*pp) { 27262306a36Sopenharmony_ci parent = *pp; 27362306a36Sopenharmony_ci cursor = rb_entry(parent, struct afs_cell, net_node); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci n = strncasecmp(cursor->name, name, 27662306a36Sopenharmony_ci min_t(size_t, cursor->name_len, namesz)); 27762306a36Sopenharmony_ci if (n == 0) 27862306a36Sopenharmony_ci n = cursor->name_len - namesz; 27962306a36Sopenharmony_ci if (n < 0) 28062306a36Sopenharmony_ci pp = &(*pp)->rb_left; 28162306a36Sopenharmony_ci else if (n > 0) 28262306a36Sopenharmony_ci pp = &(*pp)->rb_right; 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci goto cell_already_exists; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci cell = candidate; 28862306a36Sopenharmony_ci candidate = NULL; 28962306a36Sopenharmony_ci atomic_set(&cell->active, 2); 29062306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 2, afs_cell_trace_insert); 29162306a36Sopenharmony_ci rb_link_node_rcu(&cell->net_node, parent, pp); 29262306a36Sopenharmony_ci rb_insert_color(&cell->net_node, &net->cells); 29362306a36Sopenharmony_ci up_write(&net->cells_lock); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci afs_queue_cell(cell, afs_cell_trace_get_queue_new); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciwait_for_cell: 29862306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), atomic_read(&cell->active), 29962306a36Sopenharmony_ci afs_cell_trace_wait); 30062306a36Sopenharmony_ci _debug("wait_for_cell"); 30162306a36Sopenharmony_ci wait_var_event(&cell->state, 30262306a36Sopenharmony_ci ({ 30362306a36Sopenharmony_ci state = smp_load_acquire(&cell->state); /* vs error */ 30462306a36Sopenharmony_ci state == AFS_CELL_ACTIVE || state == AFS_CELL_REMOVED; 30562306a36Sopenharmony_ci })); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Check the state obtained from the wait check. */ 30862306a36Sopenharmony_ci if (state == AFS_CELL_REMOVED) { 30962306a36Sopenharmony_ci ret = cell->error; 31062306a36Sopenharmony_ci goto error; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci _leave(" = %p [cell]", cell); 31462306a36Sopenharmony_ci return cell; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cicell_already_exists: 31762306a36Sopenharmony_ci _debug("cell exists"); 31862306a36Sopenharmony_ci cell = cursor; 31962306a36Sopenharmony_ci if (excl) { 32062306a36Sopenharmony_ci ret = -EEXIST; 32162306a36Sopenharmony_ci } else { 32262306a36Sopenharmony_ci afs_use_cell(cursor, afs_cell_trace_use_lookup); 32362306a36Sopenharmony_ci ret = 0; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci up_write(&net->cells_lock); 32662306a36Sopenharmony_ci if (candidate) 32762306a36Sopenharmony_ci afs_put_cell(candidate, afs_cell_trace_put_candidate); 32862306a36Sopenharmony_ci if (ret == 0) 32962306a36Sopenharmony_ci goto wait_for_cell; 33062306a36Sopenharmony_ci goto error_noput; 33162306a36Sopenharmony_cierror: 33262306a36Sopenharmony_ci afs_unuse_cell(net, cell, afs_cell_trace_unuse_lookup); 33362306a36Sopenharmony_cierror_noput: 33462306a36Sopenharmony_ci _leave(" = %d [error]", ret); 33562306a36Sopenharmony_ci return ERR_PTR(ret); 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci/* 33962306a36Sopenharmony_ci * set the root cell information 34062306a36Sopenharmony_ci * - can be called with a module parameter string 34162306a36Sopenharmony_ci * - can be called from a write to /proc/fs/afs/rootcell 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ciint afs_cell_init(struct afs_net *net, const char *rootcell) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct afs_cell *old_root, *new_root; 34662306a36Sopenharmony_ci const char *cp, *vllist; 34762306a36Sopenharmony_ci size_t len; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci _enter(""); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!rootcell) { 35262306a36Sopenharmony_ci /* module is loaded with no parameters, or built statically. 35362306a36Sopenharmony_ci * - in the future we might initialize cell DB here. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci _leave(" = 0 [no root]"); 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci cp = strchr(rootcell, ':'); 36062306a36Sopenharmony_ci if (!cp) { 36162306a36Sopenharmony_ci _debug("kAFS: no VL server IP addresses specified"); 36262306a36Sopenharmony_ci vllist = NULL; 36362306a36Sopenharmony_ci len = strlen(rootcell); 36462306a36Sopenharmony_ci } else { 36562306a36Sopenharmony_ci vllist = cp + 1; 36662306a36Sopenharmony_ci len = cp - rootcell; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* allocate a cell record for the root cell */ 37062306a36Sopenharmony_ci new_root = afs_lookup_cell(net, rootcell, len, vllist, false); 37162306a36Sopenharmony_ci if (IS_ERR(new_root)) { 37262306a36Sopenharmony_ci _leave(" = %ld", PTR_ERR(new_root)); 37362306a36Sopenharmony_ci return PTR_ERR(new_root); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (!test_and_set_bit(AFS_CELL_FL_NO_GC, &new_root->flags)) 37762306a36Sopenharmony_ci afs_use_cell(new_root, afs_cell_trace_use_pin); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* install the new cell */ 38062306a36Sopenharmony_ci down_write(&net->cells_lock); 38162306a36Sopenharmony_ci afs_see_cell(new_root, afs_cell_trace_see_ws); 38262306a36Sopenharmony_ci old_root = net->ws_cell; 38362306a36Sopenharmony_ci net->ws_cell = new_root; 38462306a36Sopenharmony_ci up_write(&net->cells_lock); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci afs_unuse_cell(net, old_root, afs_cell_trace_unuse_ws); 38762306a36Sopenharmony_ci _leave(" = 0"); 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* 39262306a36Sopenharmony_ci * Update a cell's VL server address list from the DNS. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic int afs_update_cell(struct afs_cell *cell) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct afs_vlserver_list *vllist, *old = NULL, *p; 39762306a36Sopenharmony_ci unsigned int min_ttl = READ_ONCE(afs_cell_min_ttl); 39862306a36Sopenharmony_ci unsigned int max_ttl = READ_ONCE(afs_cell_max_ttl); 39962306a36Sopenharmony_ci time64_t now, expiry = 0; 40062306a36Sopenharmony_ci int ret = 0; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci _enter("%s", cell->name); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci vllist = afs_dns_query(cell, &expiry); 40562306a36Sopenharmony_ci if (IS_ERR(vllist)) { 40662306a36Sopenharmony_ci ret = PTR_ERR(vllist); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci _debug("%s: fail %d", cell->name, ret); 40962306a36Sopenharmony_ci if (ret == -ENOMEM) 41062306a36Sopenharmony_ci goto out_wake; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci vllist = afs_alloc_vlserver_list(0); 41362306a36Sopenharmony_ci if (!vllist) { 41462306a36Sopenharmony_ci if (ret >= 0) 41562306a36Sopenharmony_ci ret = -ENOMEM; 41662306a36Sopenharmony_ci goto out_wake; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci switch (ret) { 42062306a36Sopenharmony_ci case -ENODATA: 42162306a36Sopenharmony_ci case -EDESTADDRREQ: 42262306a36Sopenharmony_ci vllist->status = DNS_LOOKUP_GOT_NOT_FOUND; 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci case -EAGAIN: 42562306a36Sopenharmony_ci case -ECONNREFUSED: 42662306a36Sopenharmony_ci vllist->status = DNS_LOOKUP_GOT_TEMP_FAILURE; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci vllist->status = DNS_LOOKUP_GOT_LOCAL_FAILURE; 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci _debug("%s: got list %d %d", cell->name, vllist->source, vllist->status); 43562306a36Sopenharmony_ci cell->dns_status = vllist->status; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci now = ktime_get_real_seconds(); 43862306a36Sopenharmony_ci if (min_ttl > max_ttl) 43962306a36Sopenharmony_ci max_ttl = min_ttl; 44062306a36Sopenharmony_ci if (expiry < now + min_ttl) 44162306a36Sopenharmony_ci expiry = now + min_ttl; 44262306a36Sopenharmony_ci else if (expiry > now + max_ttl) 44362306a36Sopenharmony_ci expiry = now + max_ttl; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci _debug("%s: status %d", cell->name, vllist->status); 44662306a36Sopenharmony_ci if (vllist->source == DNS_RECORD_UNAVAILABLE) { 44762306a36Sopenharmony_ci switch (vllist->status) { 44862306a36Sopenharmony_ci case DNS_LOOKUP_GOT_NOT_FOUND: 44962306a36Sopenharmony_ci /* The DNS said that the cell does not exist or there 45062306a36Sopenharmony_ci * weren't any addresses to be had. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci cell->dns_expiry = expiry; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci case DNS_LOOKUP_BAD: 45662306a36Sopenharmony_ci case DNS_LOOKUP_GOT_LOCAL_FAILURE: 45762306a36Sopenharmony_ci case DNS_LOOKUP_GOT_TEMP_FAILURE: 45862306a36Sopenharmony_ci case DNS_LOOKUP_GOT_NS_FAILURE: 45962306a36Sopenharmony_ci default: 46062306a36Sopenharmony_ci cell->dns_expiry = now + 10; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci } else { 46462306a36Sopenharmony_ci cell->dns_expiry = expiry; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Replace the VL server list if the new record has servers or the old 46862306a36Sopenharmony_ci * record doesn't. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci write_lock(&cell->vl_servers_lock); 47162306a36Sopenharmony_ci p = rcu_dereference_protected(cell->vl_servers, true); 47262306a36Sopenharmony_ci if (vllist->nr_servers > 0 || p->nr_servers == 0) { 47362306a36Sopenharmony_ci rcu_assign_pointer(cell->vl_servers, vllist); 47462306a36Sopenharmony_ci cell->dns_source = vllist->source; 47562306a36Sopenharmony_ci old = p; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci write_unlock(&cell->vl_servers_lock); 47862306a36Sopenharmony_ci afs_put_vlserverlist(cell->net, old); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciout_wake: 48162306a36Sopenharmony_ci smp_store_release(&cell->dns_lookup_count, 48262306a36Sopenharmony_ci cell->dns_lookup_count + 1); /* vs source/status */ 48362306a36Sopenharmony_ci wake_up_var(&cell->dns_lookup_count); 48462306a36Sopenharmony_ci _leave(" = %d", ret); 48562306a36Sopenharmony_ci return ret; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* 48962306a36Sopenharmony_ci * Destroy a cell record 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic void afs_cell_destroy(struct rcu_head *rcu) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct afs_cell *cell = container_of(rcu, struct afs_cell, rcu); 49462306a36Sopenharmony_ci struct afs_net *net = cell->net; 49562306a36Sopenharmony_ci int r; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci _enter("%p{%s}", cell, cell->name); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci r = refcount_read(&cell->ref); 50062306a36Sopenharmony_ci ASSERTCMP(r, ==, 0); 50162306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, r, atomic_read(&cell->active), afs_cell_trace_free); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers)); 50462306a36Sopenharmony_ci afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias); 50562306a36Sopenharmony_ci key_put(cell->anonymous_key); 50662306a36Sopenharmony_ci kfree(cell->name); 50762306a36Sopenharmony_ci kfree(cell); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci afs_dec_cells_outstanding(net); 51062306a36Sopenharmony_ci _leave(" [destroyed]"); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci/* 51462306a36Sopenharmony_ci * Queue the cell manager. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_cistatic void afs_queue_cell_manager(struct afs_net *net) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int outstanding = atomic_inc_return(&net->cells_outstanding); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci _enter("%d", outstanding); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (!queue_work(afs_wq, &net->cells_manager)) 52362306a36Sopenharmony_ci afs_dec_cells_outstanding(net); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* 52762306a36Sopenharmony_ci * Cell management timer. We have an increment on cells_outstanding that we 52862306a36Sopenharmony_ci * need to pass along to the work item. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_civoid afs_cells_timer(struct timer_list *timer) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct afs_net *net = container_of(timer, struct afs_net, cells_timer); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci _enter(""); 53562306a36Sopenharmony_ci if (!queue_work(afs_wq, &net->cells_manager)) 53662306a36Sopenharmony_ci afs_dec_cells_outstanding(net); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* 54062306a36Sopenharmony_ci * Get a reference on a cell record. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_cistruct afs_cell *afs_get_cell(struct afs_cell *cell, enum afs_cell_trace reason) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci int r; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci __refcount_inc(&cell->ref, &r); 54762306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, r + 1, atomic_read(&cell->active), reason); 54862306a36Sopenharmony_ci return cell; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/* 55262306a36Sopenharmony_ci * Drop a reference on a cell record. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_civoid afs_put_cell(struct afs_cell *cell, enum afs_cell_trace reason) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci if (cell) { 55762306a36Sopenharmony_ci unsigned int debug_id = cell->debug_id; 55862306a36Sopenharmony_ci unsigned int a; 55962306a36Sopenharmony_ci bool zero; 56062306a36Sopenharmony_ci int r; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci a = atomic_read(&cell->active); 56362306a36Sopenharmony_ci zero = __refcount_dec_and_test(&cell->ref, &r); 56462306a36Sopenharmony_ci trace_afs_cell(debug_id, r - 1, a, reason); 56562306a36Sopenharmony_ci if (zero) { 56662306a36Sopenharmony_ci a = atomic_read(&cell->active); 56762306a36Sopenharmony_ci WARN(a != 0, "Cell active count %u > 0\n", a); 56862306a36Sopenharmony_ci call_rcu(&cell->rcu, afs_cell_destroy); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* 57462306a36Sopenharmony_ci * Note a cell becoming more active. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_cistruct afs_cell *afs_use_cell(struct afs_cell *cell, enum afs_cell_trace reason) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci int r, a; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci r = refcount_read(&cell->ref); 58162306a36Sopenharmony_ci WARN_ON(r == 0); 58262306a36Sopenharmony_ci a = atomic_inc_return(&cell->active); 58362306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, r, a, reason); 58462306a36Sopenharmony_ci return cell; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/* 58862306a36Sopenharmony_ci * Record a cell becoming less active. When the active counter reaches 1, it 58962306a36Sopenharmony_ci * is scheduled for destruction, but may get reactivated. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_civoid afs_unuse_cell(struct afs_net *net, struct afs_cell *cell, enum afs_cell_trace reason) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci unsigned int debug_id; 59462306a36Sopenharmony_ci time64_t now, expire_delay; 59562306a36Sopenharmony_ci int r, a; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!cell) 59862306a36Sopenharmony_ci return; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci _enter("%s", cell->name); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci now = ktime_get_real_seconds(); 60362306a36Sopenharmony_ci cell->last_inactive = now; 60462306a36Sopenharmony_ci expire_delay = 0; 60562306a36Sopenharmony_ci if (cell->vl_servers->nr_servers) 60662306a36Sopenharmony_ci expire_delay = afs_cell_gc_delay; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci debug_id = cell->debug_id; 60962306a36Sopenharmony_ci r = refcount_read(&cell->ref); 61062306a36Sopenharmony_ci a = atomic_dec_return(&cell->active); 61162306a36Sopenharmony_ci trace_afs_cell(debug_id, r, a, reason); 61262306a36Sopenharmony_ci WARN_ON(a == 0); 61362306a36Sopenharmony_ci if (a == 1) 61462306a36Sopenharmony_ci /* 'cell' may now be garbage collected. */ 61562306a36Sopenharmony_ci afs_set_cell_timer(net, expire_delay); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* 61962306a36Sopenharmony_ci * Note that a cell has been seen. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_civoid afs_see_cell(struct afs_cell *cell, enum afs_cell_trace reason) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci int r, a; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci r = refcount_read(&cell->ref); 62662306a36Sopenharmony_ci a = atomic_read(&cell->active); 62762306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, r, a, reason); 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci/* 63162306a36Sopenharmony_ci * Queue a cell for management, giving the workqueue a ref to hold. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_civoid afs_queue_cell(struct afs_cell *cell, enum afs_cell_trace reason) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci afs_get_cell(cell, reason); 63662306a36Sopenharmony_ci if (!queue_work(afs_wq, &cell->manager)) 63762306a36Sopenharmony_ci afs_put_cell(cell, afs_cell_trace_put_queue_fail); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci/* 64162306a36Sopenharmony_ci * Allocate a key to use as a placeholder for anonymous user security. 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_cistatic int afs_alloc_anon_key(struct afs_cell *cell) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct key *key; 64662306a36Sopenharmony_ci char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Create a key to represent an anonymous user. */ 64962306a36Sopenharmony_ci memcpy(keyname, "afs@", 4); 65062306a36Sopenharmony_ci dp = keyname + 4; 65162306a36Sopenharmony_ci cp = cell->name; 65262306a36Sopenharmony_ci do { 65362306a36Sopenharmony_ci *dp++ = tolower(*cp); 65462306a36Sopenharmony_ci } while (*cp++); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci key = rxrpc_get_null_key(keyname); 65762306a36Sopenharmony_ci if (IS_ERR(key)) 65862306a36Sopenharmony_ci return PTR_ERR(key); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci cell->anonymous_key = key; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci _debug("anon key %p{%x}", 66362306a36Sopenharmony_ci cell->anonymous_key, key_serial(cell->anonymous_key)); 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * Activate a cell. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_cistatic int afs_activate_cell(struct afs_net *net, struct afs_cell *cell) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct hlist_node **p; 67362306a36Sopenharmony_ci struct afs_cell *pcell; 67462306a36Sopenharmony_ci int ret; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (!cell->anonymous_key) { 67762306a36Sopenharmony_ci ret = afs_alloc_anon_key(cell); 67862306a36Sopenharmony_ci if (ret < 0) 67962306a36Sopenharmony_ci return ret; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = afs_proc_cell_setup(cell); 68362306a36Sopenharmony_ci if (ret < 0) 68462306a36Sopenharmony_ci return ret; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci mutex_lock(&net->proc_cells_lock); 68762306a36Sopenharmony_ci for (p = &net->proc_cells.first; *p; p = &(*p)->next) { 68862306a36Sopenharmony_ci pcell = hlist_entry(*p, struct afs_cell, proc_link); 68962306a36Sopenharmony_ci if (strcmp(cell->name, pcell->name) < 0) 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci cell->proc_link.pprev = p; 69462306a36Sopenharmony_ci cell->proc_link.next = *p; 69562306a36Sopenharmony_ci rcu_assign_pointer(*p, &cell->proc_link.next); 69662306a36Sopenharmony_ci if (cell->proc_link.next) 69762306a36Sopenharmony_ci cell->proc_link.next->pprev = &cell->proc_link.next; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci afs_dynroot_mkdir(net, cell); 70062306a36Sopenharmony_ci mutex_unlock(&net->proc_cells_lock); 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/* 70562306a36Sopenharmony_ci * Deactivate a cell. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_cistatic void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci _enter("%s", cell->name); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci afs_proc_cell_remove(cell); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci mutex_lock(&net->proc_cells_lock); 71462306a36Sopenharmony_ci hlist_del_rcu(&cell->proc_link); 71562306a36Sopenharmony_ci afs_dynroot_rmdir(net, cell); 71662306a36Sopenharmony_ci mutex_unlock(&net->proc_cells_lock); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci _leave(""); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/* 72262306a36Sopenharmony_ci * Manage a cell record, initialising and destroying it, maintaining its DNS 72362306a36Sopenharmony_ci * records. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_cistatic void afs_manage_cell(struct afs_cell *cell) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct afs_net *net = cell->net; 72862306a36Sopenharmony_ci int ret, active; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci _enter("%s", cell->name); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ciagain: 73362306a36Sopenharmony_ci _debug("state %u", cell->state); 73462306a36Sopenharmony_ci switch (cell->state) { 73562306a36Sopenharmony_ci case AFS_CELL_INACTIVE: 73662306a36Sopenharmony_ci case AFS_CELL_FAILED: 73762306a36Sopenharmony_ci down_write(&net->cells_lock); 73862306a36Sopenharmony_ci active = 1; 73962306a36Sopenharmony_ci if (atomic_try_cmpxchg_relaxed(&cell->active, &active, 0)) { 74062306a36Sopenharmony_ci rb_erase(&cell->net_node, &net->cells); 74162306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 0, 74262306a36Sopenharmony_ci afs_cell_trace_unuse_delete); 74362306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_REMOVED); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci up_write(&net->cells_lock); 74662306a36Sopenharmony_ci if (cell->state == AFS_CELL_REMOVED) { 74762306a36Sopenharmony_ci wake_up_var(&cell->state); 74862306a36Sopenharmony_ci goto final_destruction; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci if (cell->state == AFS_CELL_FAILED) 75162306a36Sopenharmony_ci goto done; 75262306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_UNSET); 75362306a36Sopenharmony_ci wake_up_var(&cell->state); 75462306a36Sopenharmony_ci goto again; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci case AFS_CELL_UNSET: 75762306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_ACTIVATING); 75862306a36Sopenharmony_ci wake_up_var(&cell->state); 75962306a36Sopenharmony_ci goto again; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci case AFS_CELL_ACTIVATING: 76262306a36Sopenharmony_ci ret = afs_activate_cell(net, cell); 76362306a36Sopenharmony_ci if (ret < 0) 76462306a36Sopenharmony_ci goto activation_failed; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_ACTIVE); 76762306a36Sopenharmony_ci wake_up_var(&cell->state); 76862306a36Sopenharmony_ci goto again; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci case AFS_CELL_ACTIVE: 77162306a36Sopenharmony_ci if (atomic_read(&cell->active) > 1) { 77262306a36Sopenharmony_ci if (test_and_clear_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) { 77362306a36Sopenharmony_ci ret = afs_update_cell(cell); 77462306a36Sopenharmony_ci if (ret < 0) 77562306a36Sopenharmony_ci cell->error = ret; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci goto done; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_DEACTIVATING); 78062306a36Sopenharmony_ci wake_up_var(&cell->state); 78162306a36Sopenharmony_ci goto again; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci case AFS_CELL_DEACTIVATING: 78462306a36Sopenharmony_ci if (atomic_read(&cell->active) > 1) 78562306a36Sopenharmony_ci goto reverse_deactivation; 78662306a36Sopenharmony_ci afs_deactivate_cell(net, cell); 78762306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_INACTIVE); 78862306a36Sopenharmony_ci wake_up_var(&cell->state); 78962306a36Sopenharmony_ci goto again; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci case AFS_CELL_REMOVED: 79262306a36Sopenharmony_ci goto done; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci default: 79562306a36Sopenharmony_ci break; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci _debug("bad state %u", cell->state); 79862306a36Sopenharmony_ci BUG(); /* Unhandled state */ 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ciactivation_failed: 80162306a36Sopenharmony_ci cell->error = ret; 80262306a36Sopenharmony_ci afs_deactivate_cell(net, cell); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_FAILED); /* vs error */ 80562306a36Sopenharmony_ci wake_up_var(&cell->state); 80662306a36Sopenharmony_ci goto again; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cireverse_deactivation: 80962306a36Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_ACTIVE); 81062306a36Sopenharmony_ci wake_up_var(&cell->state); 81162306a36Sopenharmony_ci _leave(" [deact->act]"); 81262306a36Sopenharmony_ci return; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cidone: 81562306a36Sopenharmony_ci _leave(" [done %u]", cell->state); 81662306a36Sopenharmony_ci return; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cifinal_destruction: 81962306a36Sopenharmony_ci /* The root volume is pinning the cell */ 82062306a36Sopenharmony_ci afs_put_volume(cell->net, cell->root_volume, afs_volume_trace_put_cell_root); 82162306a36Sopenharmony_ci cell->root_volume = NULL; 82262306a36Sopenharmony_ci afs_put_cell(cell, afs_cell_trace_put_destroy); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void afs_manage_cell_work(struct work_struct *work) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct afs_cell *cell = container_of(work, struct afs_cell, manager); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci afs_manage_cell(cell); 83062306a36Sopenharmony_ci afs_put_cell(cell, afs_cell_trace_put_queue_work); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/* 83462306a36Sopenharmony_ci * Manage the records of cells known to a network namespace. This includes 83562306a36Sopenharmony_ci * updating the DNS records and garbage collecting unused cells that were 83662306a36Sopenharmony_ci * automatically added. 83762306a36Sopenharmony_ci * 83862306a36Sopenharmony_ci * Note that constructed cell records may only be removed from net->cells by 83962306a36Sopenharmony_ci * this work item, so it is safe for this work item to stash a cursor pointing 84062306a36Sopenharmony_ci * into the tree and then return to caller (provided it skips cells that are 84162306a36Sopenharmony_ci * still under construction). 84262306a36Sopenharmony_ci * 84362306a36Sopenharmony_ci * Note also that we were given an increment on net->cells_outstanding by 84462306a36Sopenharmony_ci * whoever queued us that we need to deal with before returning. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_civoid afs_manage_cells(struct work_struct *work) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct afs_net *net = container_of(work, struct afs_net, cells_manager); 84962306a36Sopenharmony_ci struct rb_node *cursor; 85062306a36Sopenharmony_ci time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX; 85162306a36Sopenharmony_ci bool purging = !net->live; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci _enter(""); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* Trawl the cell database looking for cells that have expired from 85662306a36Sopenharmony_ci * lack of use and cells whose DNS results have expired and dispatch 85762306a36Sopenharmony_ci * their managers. 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci down_read(&net->cells_lock); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci for (cursor = rb_first(&net->cells); cursor; cursor = rb_next(cursor)) { 86262306a36Sopenharmony_ci struct afs_cell *cell = 86362306a36Sopenharmony_ci rb_entry(cursor, struct afs_cell, net_node); 86462306a36Sopenharmony_ci unsigned active; 86562306a36Sopenharmony_ci bool sched_cell = false; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci active = atomic_read(&cell->active); 86862306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 86962306a36Sopenharmony_ci active, afs_cell_trace_manage); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci ASSERTCMP(active, >=, 1); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (purging) { 87462306a36Sopenharmony_ci if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags)) { 87562306a36Sopenharmony_ci active = atomic_dec_return(&cell->active); 87662306a36Sopenharmony_ci trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 87762306a36Sopenharmony_ci active, afs_cell_trace_unuse_pin); 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (active == 1) { 88262306a36Sopenharmony_ci struct afs_vlserver_list *vllist; 88362306a36Sopenharmony_ci time64_t expire_at = cell->last_inactive; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci read_lock(&cell->vl_servers_lock); 88662306a36Sopenharmony_ci vllist = rcu_dereference_protected( 88762306a36Sopenharmony_ci cell->vl_servers, 88862306a36Sopenharmony_ci lockdep_is_held(&cell->vl_servers_lock)); 88962306a36Sopenharmony_ci if (vllist->nr_servers > 0) 89062306a36Sopenharmony_ci expire_at += afs_cell_gc_delay; 89162306a36Sopenharmony_ci read_unlock(&cell->vl_servers_lock); 89262306a36Sopenharmony_ci if (purging || expire_at <= now) 89362306a36Sopenharmony_ci sched_cell = true; 89462306a36Sopenharmony_ci else if (expire_at < next_manage) 89562306a36Sopenharmony_ci next_manage = expire_at; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (!purging) { 89962306a36Sopenharmony_ci if (test_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) 90062306a36Sopenharmony_ci sched_cell = true; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (sched_cell) 90462306a36Sopenharmony_ci afs_queue_cell(cell, afs_cell_trace_get_queue_manage); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci up_read(&net->cells_lock); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* Update the timer on the way out. We have to pass an increment on 91062306a36Sopenharmony_ci * cells_outstanding in the namespace that we are in to the timer or 91162306a36Sopenharmony_ci * the work scheduler. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci if (!purging && next_manage < TIME64_MAX) { 91462306a36Sopenharmony_ci now = ktime_get_real_seconds(); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (next_manage - now <= 0) { 91762306a36Sopenharmony_ci if (queue_work(afs_wq, &net->cells_manager)) 91862306a36Sopenharmony_ci atomic_inc(&net->cells_outstanding); 91962306a36Sopenharmony_ci } else { 92062306a36Sopenharmony_ci afs_set_cell_timer(net, next_manage - now); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci afs_dec_cells_outstanding(net); 92562306a36Sopenharmony_ci _leave(" [%d]", atomic_read(&net->cells_outstanding)); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/* 92962306a36Sopenharmony_ci * Purge in-memory cell database. 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_civoid afs_cell_purge(struct afs_net *net) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct afs_cell *ws; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci _enter(""); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci down_write(&net->cells_lock); 93862306a36Sopenharmony_ci ws = net->ws_cell; 93962306a36Sopenharmony_ci net->ws_cell = NULL; 94062306a36Sopenharmony_ci up_write(&net->cells_lock); 94162306a36Sopenharmony_ci afs_unuse_cell(net, ws, afs_cell_trace_unuse_ws); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci _debug("del timer"); 94462306a36Sopenharmony_ci if (del_timer_sync(&net->cells_timer)) 94562306a36Sopenharmony_ci atomic_dec(&net->cells_outstanding); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci _debug("kick mgr"); 94862306a36Sopenharmony_ci afs_queue_cell_manager(net); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci _debug("wait"); 95162306a36Sopenharmony_ci wait_var_event(&net->cells_outstanding, 95262306a36Sopenharmony_ci !atomic_read(&net->cells_outstanding)); 95362306a36Sopenharmony_ci _leave(""); 95462306a36Sopenharmony_ci} 955