18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* AFS cell and server record management 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2002, 2017 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/key.h> 108c2ecf20Sopenharmony_ci#include <linux/ctype.h> 118c2ecf20Sopenharmony_ci#include <linux/dns_resolver.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/inet.h> 148c2ecf20Sopenharmony_ci#include <linux/namei.h> 158c2ecf20Sopenharmony_ci#include <keys/rxrpc-type.h> 168c2ecf20Sopenharmony_ci#include "internal.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic unsigned __read_mostly afs_cell_gc_delay = 10; 198c2ecf20Sopenharmony_cistatic unsigned __read_mostly afs_cell_min_ttl = 10 * 60; 208c2ecf20Sopenharmony_cistatic unsigned __read_mostly afs_cell_max_ttl = 24 * 60 * 60; 218c2ecf20Sopenharmony_cistatic atomic_t cell_debug_id; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void afs_queue_cell_manager(struct afs_net *); 248c2ecf20Sopenharmony_cistatic void afs_manage_cell_work(struct work_struct *); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void afs_dec_cells_outstanding(struct afs_net *net) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&net->cells_outstanding)) 298c2ecf20Sopenharmony_ci wake_up_var(&net->cells_outstanding); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Set the cell timer to fire after a given delay, assuming it's not already 348c2ecf20Sopenharmony_ci * set for an earlier time. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic void afs_set_cell_timer(struct afs_net *net, time64_t delay) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci if (net->live) { 398c2ecf20Sopenharmony_ci atomic_inc(&net->cells_outstanding); 408c2ecf20Sopenharmony_ci if (timer_reduce(&net->cells_timer, jiffies + delay * HZ)) 418c2ecf20Sopenharmony_ci afs_dec_cells_outstanding(net); 428c2ecf20Sopenharmony_ci } else { 438c2ecf20Sopenharmony_ci afs_queue_cell_manager(net); 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Look up and get an activation reference on a cell record. The caller must 498c2ecf20Sopenharmony_ci * hold net->cells_lock at least read-locked. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistatic struct afs_cell *afs_find_cell_locked(struct afs_net *net, 528c2ecf20Sopenharmony_ci const char *name, unsigned int namesz, 538c2ecf20Sopenharmony_ci enum afs_cell_trace reason) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct afs_cell *cell = NULL; 568c2ecf20Sopenharmony_ci struct rb_node *p; 578c2ecf20Sopenharmony_ci int n; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci _enter("%*.*s", namesz, namesz, name); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (name && namesz == 0) 628c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 638c2ecf20Sopenharmony_ci if (namesz > AFS_MAXCELLNAME) 648c2ecf20Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (!name) { 678c2ecf20Sopenharmony_ci cell = net->ws_cell; 688c2ecf20Sopenharmony_ci if (!cell) 698c2ecf20Sopenharmony_ci return ERR_PTR(-EDESTADDRREQ); 708c2ecf20Sopenharmony_ci goto found; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci p = net->cells.rb_node; 748c2ecf20Sopenharmony_ci while (p) { 758c2ecf20Sopenharmony_ci cell = rb_entry(p, struct afs_cell, net_node); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci n = strncasecmp(cell->name, name, 788c2ecf20Sopenharmony_ci min_t(size_t, cell->name_len, namesz)); 798c2ecf20Sopenharmony_ci if (n == 0) 808c2ecf20Sopenharmony_ci n = cell->name_len - namesz; 818c2ecf20Sopenharmony_ci if (n < 0) 828c2ecf20Sopenharmony_ci p = p->rb_left; 838c2ecf20Sopenharmony_ci else if (n > 0) 848c2ecf20Sopenharmony_ci p = p->rb_right; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci goto found; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cifound: 928c2ecf20Sopenharmony_ci return afs_use_cell(cell, reason); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * Look up and get an activation reference on a cell record. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_cistruct afs_cell *afs_find_cell(struct afs_net *net, 998c2ecf20Sopenharmony_ci const char *name, unsigned int namesz, 1008c2ecf20Sopenharmony_ci enum afs_cell_trace reason) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct afs_cell *cell; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci down_read(&net->cells_lock); 1058c2ecf20Sopenharmony_ci cell = afs_find_cell_locked(net, name, namesz, reason); 1068c2ecf20Sopenharmony_ci up_read(&net->cells_lock); 1078c2ecf20Sopenharmony_ci return cell; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * Set up a cell record and fill in its name, VL server address list and 1128c2ecf20Sopenharmony_ci * allocate an anonymous key 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic struct afs_cell *afs_alloc_cell(struct afs_net *net, 1158c2ecf20Sopenharmony_ci const char *name, unsigned int namelen, 1168c2ecf20Sopenharmony_ci const char *addresses) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct afs_vlserver_list *vllist; 1198c2ecf20Sopenharmony_ci struct afs_cell *cell; 1208c2ecf20Sopenharmony_ci int i, ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ASSERT(name); 1238c2ecf20Sopenharmony_ci if (namelen == 0) 1248c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1258c2ecf20Sopenharmony_ci if (namelen > AFS_MAXCELLNAME) { 1268c2ecf20Sopenharmony_ci _leave(" = -ENAMETOOLONG"); 1278c2ecf20Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Prohibit cell names that contain unprintable chars, '/' and '@' or 1318c2ecf20Sopenharmony_ci * that begin with a dot. This also precludes "@cell". 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci if (name[0] == '.') 1348c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1358c2ecf20Sopenharmony_ci for (i = 0; i < namelen; i++) { 1368c2ecf20Sopenharmony_ci char ch = name[i]; 1378c2ecf20Sopenharmony_ci if (!isprint(ch) || ch == '/' || ch == '@') 1388c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci _enter("%*.*s,%s", namelen, namelen, name, addresses); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci cell = kzalloc(sizeof(struct afs_cell), GFP_KERNEL); 1448c2ecf20Sopenharmony_ci if (!cell) { 1458c2ecf20Sopenharmony_ci _leave(" = -ENOMEM"); 1468c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci cell->name = kmalloc(namelen + 1, GFP_KERNEL); 1508c2ecf20Sopenharmony_ci if (!cell->name) { 1518c2ecf20Sopenharmony_ci kfree(cell); 1528c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci cell->net = net; 1568c2ecf20Sopenharmony_ci cell->name_len = namelen; 1578c2ecf20Sopenharmony_ci for (i = 0; i < namelen; i++) 1588c2ecf20Sopenharmony_ci cell->name[i] = tolower(name[i]); 1598c2ecf20Sopenharmony_ci cell->name[i] = 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci atomic_set(&cell->ref, 1); 1628c2ecf20Sopenharmony_ci atomic_set(&cell->active, 0); 1638c2ecf20Sopenharmony_ci INIT_WORK(&cell->manager, afs_manage_cell_work); 1648c2ecf20Sopenharmony_ci cell->volumes = RB_ROOT; 1658c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&cell->proc_volumes); 1668c2ecf20Sopenharmony_ci seqlock_init(&cell->volume_lock); 1678c2ecf20Sopenharmony_ci cell->fs_servers = RB_ROOT; 1688c2ecf20Sopenharmony_ci seqlock_init(&cell->fs_lock); 1698c2ecf20Sopenharmony_ci rwlock_init(&cell->vl_servers_lock); 1708c2ecf20Sopenharmony_ci cell->flags = (1 << AFS_CELL_FL_CHECK_ALIAS); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Provide a VL server list, filling it in if we were given a list of 1738c2ecf20Sopenharmony_ci * addresses to use. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci if (addresses) { 1768c2ecf20Sopenharmony_ci vllist = afs_parse_text_addrs(net, 1778c2ecf20Sopenharmony_ci addresses, strlen(addresses), ':', 1788c2ecf20Sopenharmony_ci VL_SERVICE, AFS_VL_PORT); 1798c2ecf20Sopenharmony_ci if (IS_ERR(vllist)) { 1808c2ecf20Sopenharmony_ci ret = PTR_ERR(vllist); 1818c2ecf20Sopenharmony_ci goto parse_failed; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci vllist->source = DNS_RECORD_FROM_CONFIG; 1858c2ecf20Sopenharmony_ci vllist->status = DNS_LOOKUP_NOT_DONE; 1868c2ecf20Sopenharmony_ci cell->dns_expiry = TIME64_MAX; 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci ret = -ENOMEM; 1898c2ecf20Sopenharmony_ci vllist = afs_alloc_vlserver_list(0); 1908c2ecf20Sopenharmony_ci if (!vllist) 1918c2ecf20Sopenharmony_ci goto error; 1928c2ecf20Sopenharmony_ci vllist->source = DNS_RECORD_UNAVAILABLE; 1938c2ecf20Sopenharmony_ci vllist->status = DNS_LOOKUP_NOT_DONE; 1948c2ecf20Sopenharmony_ci cell->dns_expiry = ktime_get_real_seconds(); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rcu_assign_pointer(cell->vl_servers, vllist); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci cell->dns_source = vllist->source; 2008c2ecf20Sopenharmony_ci cell->dns_status = vllist->status; 2018c2ecf20Sopenharmony_ci smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ 2028c2ecf20Sopenharmony_ci atomic_inc(&net->cells_outstanding); 2038c2ecf20Sopenharmony_ci cell->debug_id = atomic_inc_return(&cell_debug_id); 2048c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, 1, 0, afs_cell_trace_alloc); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci _leave(" = %p", cell); 2078c2ecf20Sopenharmony_ci return cell; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciparse_failed: 2108c2ecf20Sopenharmony_ci if (ret == -EINVAL) 2118c2ecf20Sopenharmony_ci printk(KERN_ERR "kAFS: bad VL server IP address\n"); 2128c2ecf20Sopenharmony_cierror: 2138c2ecf20Sopenharmony_ci kfree(cell->name); 2148c2ecf20Sopenharmony_ci kfree(cell); 2158c2ecf20Sopenharmony_ci _leave(" = %d", ret); 2168c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* 2208c2ecf20Sopenharmony_ci * afs_lookup_cell - Look up or create a cell record. 2218c2ecf20Sopenharmony_ci * @net: The network namespace 2228c2ecf20Sopenharmony_ci * @name: The name of the cell. 2238c2ecf20Sopenharmony_ci * @namesz: The strlen of the cell name. 2248c2ecf20Sopenharmony_ci * @vllist: A colon/comma separated list of numeric IP addresses or NULL. 2258c2ecf20Sopenharmony_ci * @excl: T if an error should be given if the cell name already exists. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Look up a cell record by name and query the DNS for VL server addresses if 2288c2ecf20Sopenharmony_ci * needed. Note that that actual DNS query is punted off to the manager thread 2298c2ecf20Sopenharmony_ci * so that this function can return immediately if interrupted whilst allowing 2308c2ecf20Sopenharmony_ci * cell records to be shared even if not yet fully constructed. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_cistruct afs_cell *afs_lookup_cell(struct afs_net *net, 2338c2ecf20Sopenharmony_ci const char *name, unsigned int namesz, 2348c2ecf20Sopenharmony_ci const char *vllist, bool excl) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct afs_cell *cell, *candidate, *cursor; 2378c2ecf20Sopenharmony_ci struct rb_node *parent, **pp; 2388c2ecf20Sopenharmony_ci enum afs_cell_state state; 2398c2ecf20Sopenharmony_ci int ret, n; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci _enter("%s,%s", name, vllist); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!excl) { 2448c2ecf20Sopenharmony_ci cell = afs_find_cell(net, name, namesz, afs_cell_trace_use_lookup); 2458c2ecf20Sopenharmony_ci if (!IS_ERR(cell)) 2468c2ecf20Sopenharmony_ci goto wait_for_cell; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Assume we're probably going to create a cell and preallocate and 2508c2ecf20Sopenharmony_ci * mostly set up a candidate record. We can then use this to stash the 2518c2ecf20Sopenharmony_ci * name, the net namespace and VL server addresses. 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * We also want to do this before we hold any locks as it may involve 2548c2ecf20Sopenharmony_ci * upcalling to userspace to make DNS queries. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci candidate = afs_alloc_cell(net, name, namesz, vllist); 2578c2ecf20Sopenharmony_ci if (IS_ERR(candidate)) { 2588c2ecf20Sopenharmony_ci _leave(" = %ld", PTR_ERR(candidate)); 2598c2ecf20Sopenharmony_ci return candidate; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Find the insertion point and check to see if someone else added a 2638c2ecf20Sopenharmony_ci * cell whilst we were allocating. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci down_write(&net->cells_lock); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci pp = &net->cells.rb_node; 2688c2ecf20Sopenharmony_ci parent = NULL; 2698c2ecf20Sopenharmony_ci while (*pp) { 2708c2ecf20Sopenharmony_ci parent = *pp; 2718c2ecf20Sopenharmony_ci cursor = rb_entry(parent, struct afs_cell, net_node); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci n = strncasecmp(cursor->name, name, 2748c2ecf20Sopenharmony_ci min_t(size_t, cursor->name_len, namesz)); 2758c2ecf20Sopenharmony_ci if (n == 0) 2768c2ecf20Sopenharmony_ci n = cursor->name_len - namesz; 2778c2ecf20Sopenharmony_ci if (n < 0) 2788c2ecf20Sopenharmony_ci pp = &(*pp)->rb_left; 2798c2ecf20Sopenharmony_ci else if (n > 0) 2808c2ecf20Sopenharmony_ci pp = &(*pp)->rb_right; 2818c2ecf20Sopenharmony_ci else 2828c2ecf20Sopenharmony_ci goto cell_already_exists; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci cell = candidate; 2868c2ecf20Sopenharmony_ci candidate = NULL; 2878c2ecf20Sopenharmony_ci atomic_set(&cell->active, 2); 2888c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 2, afs_cell_trace_insert); 2898c2ecf20Sopenharmony_ci rb_link_node_rcu(&cell->net_node, parent, pp); 2908c2ecf20Sopenharmony_ci rb_insert_color(&cell->net_node, &net->cells); 2918c2ecf20Sopenharmony_ci up_write(&net->cells_lock); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci afs_queue_cell(cell, afs_cell_trace_get_queue_new); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ciwait_for_cell: 2968c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), atomic_read(&cell->active), 2978c2ecf20Sopenharmony_ci afs_cell_trace_wait); 2988c2ecf20Sopenharmony_ci _debug("wait_for_cell"); 2998c2ecf20Sopenharmony_ci wait_var_event(&cell->state, 3008c2ecf20Sopenharmony_ci ({ 3018c2ecf20Sopenharmony_ci state = smp_load_acquire(&cell->state); /* vs error */ 3028c2ecf20Sopenharmony_ci state == AFS_CELL_ACTIVE || state == AFS_CELL_REMOVED; 3038c2ecf20Sopenharmony_ci })); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Check the state obtained from the wait check. */ 3068c2ecf20Sopenharmony_ci if (state == AFS_CELL_REMOVED) { 3078c2ecf20Sopenharmony_ci ret = cell->error; 3088c2ecf20Sopenharmony_ci goto error; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci _leave(" = %p [cell]", cell); 3128c2ecf20Sopenharmony_ci return cell; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cicell_already_exists: 3158c2ecf20Sopenharmony_ci _debug("cell exists"); 3168c2ecf20Sopenharmony_ci cell = cursor; 3178c2ecf20Sopenharmony_ci if (excl) { 3188c2ecf20Sopenharmony_ci ret = -EEXIST; 3198c2ecf20Sopenharmony_ci } else { 3208c2ecf20Sopenharmony_ci afs_use_cell(cursor, afs_cell_trace_use_lookup); 3218c2ecf20Sopenharmony_ci ret = 0; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci up_write(&net->cells_lock); 3248c2ecf20Sopenharmony_ci if (candidate) 3258c2ecf20Sopenharmony_ci afs_put_cell(candidate, afs_cell_trace_put_candidate); 3268c2ecf20Sopenharmony_ci if (ret == 0) 3278c2ecf20Sopenharmony_ci goto wait_for_cell; 3288c2ecf20Sopenharmony_ci goto error_noput; 3298c2ecf20Sopenharmony_cierror: 3308c2ecf20Sopenharmony_ci afs_unuse_cell(net, cell, afs_cell_trace_unuse_lookup); 3318c2ecf20Sopenharmony_cierror_noput: 3328c2ecf20Sopenharmony_ci _leave(" = %d [error]", ret); 3338c2ecf20Sopenharmony_ci return ERR_PTR(ret); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* 3378c2ecf20Sopenharmony_ci * set the root cell information 3388c2ecf20Sopenharmony_ci * - can be called with a module parameter string 3398c2ecf20Sopenharmony_ci * - can be called from a write to /proc/fs/afs/rootcell 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ciint afs_cell_init(struct afs_net *net, const char *rootcell) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct afs_cell *old_root, *new_root; 3448c2ecf20Sopenharmony_ci const char *cp, *vllist; 3458c2ecf20Sopenharmony_ci size_t len; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci _enter(""); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!rootcell) { 3508c2ecf20Sopenharmony_ci /* module is loaded with no parameters, or built statically. 3518c2ecf20Sopenharmony_ci * - in the future we might initialize cell DB here. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci _leave(" = 0 [no root]"); 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci cp = strchr(rootcell, ':'); 3588c2ecf20Sopenharmony_ci if (!cp) { 3598c2ecf20Sopenharmony_ci _debug("kAFS: no VL server IP addresses specified"); 3608c2ecf20Sopenharmony_ci vllist = NULL; 3618c2ecf20Sopenharmony_ci len = strlen(rootcell); 3628c2ecf20Sopenharmony_ci } else { 3638c2ecf20Sopenharmony_ci vllist = cp + 1; 3648c2ecf20Sopenharmony_ci len = cp - rootcell; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* allocate a cell record for the root cell */ 3688c2ecf20Sopenharmony_ci new_root = afs_lookup_cell(net, rootcell, len, vllist, false); 3698c2ecf20Sopenharmony_ci if (IS_ERR(new_root)) { 3708c2ecf20Sopenharmony_ci _leave(" = %ld", PTR_ERR(new_root)); 3718c2ecf20Sopenharmony_ci return PTR_ERR(new_root); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (!test_and_set_bit(AFS_CELL_FL_NO_GC, &new_root->flags)) 3758c2ecf20Sopenharmony_ci afs_use_cell(new_root, afs_cell_trace_use_pin); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* install the new cell */ 3788c2ecf20Sopenharmony_ci down_write(&net->cells_lock); 3798c2ecf20Sopenharmony_ci afs_see_cell(new_root, afs_cell_trace_see_ws); 3808c2ecf20Sopenharmony_ci old_root = net->ws_cell; 3818c2ecf20Sopenharmony_ci net->ws_cell = new_root; 3828c2ecf20Sopenharmony_ci up_write(&net->cells_lock); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci afs_unuse_cell(net, old_root, afs_cell_trace_unuse_ws); 3858c2ecf20Sopenharmony_ci _leave(" = 0"); 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* 3908c2ecf20Sopenharmony_ci * Update a cell's VL server address list from the DNS. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_cistatic int afs_update_cell(struct afs_cell *cell) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct afs_vlserver_list *vllist, *old = NULL, *p; 3958c2ecf20Sopenharmony_ci unsigned int min_ttl = READ_ONCE(afs_cell_min_ttl); 3968c2ecf20Sopenharmony_ci unsigned int max_ttl = READ_ONCE(afs_cell_max_ttl); 3978c2ecf20Sopenharmony_ci time64_t now, expiry = 0; 3988c2ecf20Sopenharmony_ci int ret = 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci _enter("%s", cell->name); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci vllist = afs_dns_query(cell, &expiry); 4038c2ecf20Sopenharmony_ci if (IS_ERR(vllist)) { 4048c2ecf20Sopenharmony_ci ret = PTR_ERR(vllist); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci _debug("%s: fail %d", cell->name, ret); 4078c2ecf20Sopenharmony_ci if (ret == -ENOMEM) 4088c2ecf20Sopenharmony_ci goto out_wake; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci vllist = afs_alloc_vlserver_list(0); 4118c2ecf20Sopenharmony_ci if (!vllist) { 4128c2ecf20Sopenharmony_ci if (ret >= 0) 4138c2ecf20Sopenharmony_ci ret = -ENOMEM; 4148c2ecf20Sopenharmony_ci goto out_wake; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci switch (ret) { 4188c2ecf20Sopenharmony_ci case -ENODATA: 4198c2ecf20Sopenharmony_ci case -EDESTADDRREQ: 4208c2ecf20Sopenharmony_ci vllist->status = DNS_LOOKUP_GOT_NOT_FOUND; 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case -EAGAIN: 4238c2ecf20Sopenharmony_ci case -ECONNREFUSED: 4248c2ecf20Sopenharmony_ci vllist->status = DNS_LOOKUP_GOT_TEMP_FAILURE; 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci default: 4278c2ecf20Sopenharmony_ci vllist->status = DNS_LOOKUP_GOT_LOCAL_FAILURE; 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci _debug("%s: got list %d %d", cell->name, vllist->source, vllist->status); 4338c2ecf20Sopenharmony_ci cell->dns_status = vllist->status; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci now = ktime_get_real_seconds(); 4368c2ecf20Sopenharmony_ci if (min_ttl > max_ttl) 4378c2ecf20Sopenharmony_ci max_ttl = min_ttl; 4388c2ecf20Sopenharmony_ci if (expiry < now + min_ttl) 4398c2ecf20Sopenharmony_ci expiry = now + min_ttl; 4408c2ecf20Sopenharmony_ci else if (expiry > now + max_ttl) 4418c2ecf20Sopenharmony_ci expiry = now + max_ttl; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci _debug("%s: status %d", cell->name, vllist->status); 4448c2ecf20Sopenharmony_ci if (vllist->source == DNS_RECORD_UNAVAILABLE) { 4458c2ecf20Sopenharmony_ci switch (vllist->status) { 4468c2ecf20Sopenharmony_ci case DNS_LOOKUP_GOT_NOT_FOUND: 4478c2ecf20Sopenharmony_ci /* The DNS said that the cell does not exist or there 4488c2ecf20Sopenharmony_ci * weren't any addresses to be had. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci cell->dns_expiry = expiry; 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci case DNS_LOOKUP_BAD: 4548c2ecf20Sopenharmony_ci case DNS_LOOKUP_GOT_LOCAL_FAILURE: 4558c2ecf20Sopenharmony_ci case DNS_LOOKUP_GOT_TEMP_FAILURE: 4568c2ecf20Sopenharmony_ci case DNS_LOOKUP_GOT_NS_FAILURE: 4578c2ecf20Sopenharmony_ci default: 4588c2ecf20Sopenharmony_ci cell->dns_expiry = now + 10; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci } else { 4628c2ecf20Sopenharmony_ci cell->dns_expiry = expiry; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Replace the VL server list if the new record has servers or the old 4668c2ecf20Sopenharmony_ci * record doesn't. 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_ci write_lock(&cell->vl_servers_lock); 4698c2ecf20Sopenharmony_ci p = rcu_dereference_protected(cell->vl_servers, true); 4708c2ecf20Sopenharmony_ci if (vllist->nr_servers > 0 || p->nr_servers == 0) { 4718c2ecf20Sopenharmony_ci rcu_assign_pointer(cell->vl_servers, vllist); 4728c2ecf20Sopenharmony_ci cell->dns_source = vllist->source; 4738c2ecf20Sopenharmony_ci old = p; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci write_unlock(&cell->vl_servers_lock); 4768c2ecf20Sopenharmony_ci afs_put_vlserverlist(cell->net, old); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ciout_wake: 4798c2ecf20Sopenharmony_ci smp_store_release(&cell->dns_lookup_count, 4808c2ecf20Sopenharmony_ci cell->dns_lookup_count + 1); /* vs source/status */ 4818c2ecf20Sopenharmony_ci wake_up_var(&cell->dns_lookup_count); 4828c2ecf20Sopenharmony_ci _leave(" = %d", ret); 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* 4878c2ecf20Sopenharmony_ci * Destroy a cell record 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_cistatic void afs_cell_destroy(struct rcu_head *rcu) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct afs_cell *cell = container_of(rcu, struct afs_cell, rcu); 4928c2ecf20Sopenharmony_ci struct afs_net *net = cell->net; 4938c2ecf20Sopenharmony_ci int u; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci _enter("%p{%s}", cell, cell->name); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci u = atomic_read(&cell->ref); 4988c2ecf20Sopenharmony_ci ASSERTCMP(u, ==, 0); 4998c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, u, atomic_read(&cell->active), afs_cell_trace_free); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers)); 5028c2ecf20Sopenharmony_ci afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias); 5038c2ecf20Sopenharmony_ci key_put(cell->anonymous_key); 5048c2ecf20Sopenharmony_ci kfree(cell->name); 5058c2ecf20Sopenharmony_ci kfree(cell); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci afs_dec_cells_outstanding(net); 5088c2ecf20Sopenharmony_ci _leave(" [destroyed]"); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/* 5128c2ecf20Sopenharmony_ci * Queue the cell manager. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_cistatic void afs_queue_cell_manager(struct afs_net *net) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int outstanding = atomic_inc_return(&net->cells_outstanding); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci _enter("%d", outstanding); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (!queue_work(afs_wq, &net->cells_manager)) 5218c2ecf20Sopenharmony_ci afs_dec_cells_outstanding(net); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/* 5258c2ecf20Sopenharmony_ci * Cell management timer. We have an increment on cells_outstanding that we 5268c2ecf20Sopenharmony_ci * need to pass along to the work item. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_civoid afs_cells_timer(struct timer_list *timer) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct afs_net *net = container_of(timer, struct afs_net, cells_timer); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci _enter(""); 5338c2ecf20Sopenharmony_ci if (!queue_work(afs_wq, &net->cells_manager)) 5348c2ecf20Sopenharmony_ci afs_dec_cells_outstanding(net); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci/* 5388c2ecf20Sopenharmony_ci * Get a reference on a cell record. 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_cistruct afs_cell *afs_get_cell(struct afs_cell *cell, enum afs_cell_trace reason) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci int u; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (atomic_read(&cell->ref) <= 0) 5458c2ecf20Sopenharmony_ci BUG(); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci u = atomic_inc_return(&cell->ref); 5488c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, u, atomic_read(&cell->active), reason); 5498c2ecf20Sopenharmony_ci return cell; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci/* 5538c2ecf20Sopenharmony_ci * Drop a reference on a cell record. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_civoid afs_put_cell(struct afs_cell *cell, enum afs_cell_trace reason) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci if (cell) { 5588c2ecf20Sopenharmony_ci unsigned int debug_id = cell->debug_id; 5598c2ecf20Sopenharmony_ci unsigned int u, a; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci a = atomic_read(&cell->active); 5628c2ecf20Sopenharmony_ci u = atomic_dec_return(&cell->ref); 5638c2ecf20Sopenharmony_ci trace_afs_cell(debug_id, u, a, reason); 5648c2ecf20Sopenharmony_ci if (u == 0) { 5658c2ecf20Sopenharmony_ci a = atomic_read(&cell->active); 5668c2ecf20Sopenharmony_ci WARN(a != 0, "Cell active count %u > 0\n", a); 5678c2ecf20Sopenharmony_ci call_rcu(&cell->rcu, afs_cell_destroy); 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/* 5738c2ecf20Sopenharmony_ci * Note a cell becoming more active. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_cistruct afs_cell *afs_use_cell(struct afs_cell *cell, enum afs_cell_trace reason) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci int u, a; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (atomic_read(&cell->ref) <= 0) 5808c2ecf20Sopenharmony_ci BUG(); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci u = atomic_read(&cell->ref); 5838c2ecf20Sopenharmony_ci a = atomic_inc_return(&cell->active); 5848c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, u, a, reason); 5858c2ecf20Sopenharmony_ci return cell; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/* 5898c2ecf20Sopenharmony_ci * Record a cell becoming less active. When the active counter reaches 1, it 5908c2ecf20Sopenharmony_ci * is scheduled for destruction, but may get reactivated. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_civoid afs_unuse_cell(struct afs_net *net, struct afs_cell *cell, enum afs_cell_trace reason) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci unsigned int debug_id; 5958c2ecf20Sopenharmony_ci time64_t now, expire_delay; 5968c2ecf20Sopenharmony_ci int u, a; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (!cell) 5998c2ecf20Sopenharmony_ci return; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci _enter("%s", cell->name); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci now = ktime_get_real_seconds(); 6048c2ecf20Sopenharmony_ci cell->last_inactive = now; 6058c2ecf20Sopenharmony_ci expire_delay = 0; 6068c2ecf20Sopenharmony_ci if (cell->vl_servers->nr_servers) 6078c2ecf20Sopenharmony_ci expire_delay = afs_cell_gc_delay; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci debug_id = cell->debug_id; 6108c2ecf20Sopenharmony_ci u = atomic_read(&cell->ref); 6118c2ecf20Sopenharmony_ci a = atomic_dec_return(&cell->active); 6128c2ecf20Sopenharmony_ci trace_afs_cell(debug_id, u, a, reason); 6138c2ecf20Sopenharmony_ci WARN_ON(a == 0); 6148c2ecf20Sopenharmony_ci if (a == 1) 6158c2ecf20Sopenharmony_ci /* 'cell' may now be garbage collected. */ 6168c2ecf20Sopenharmony_ci afs_set_cell_timer(net, expire_delay); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci/* 6208c2ecf20Sopenharmony_ci * Note that a cell has been seen. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_civoid afs_see_cell(struct afs_cell *cell, enum afs_cell_trace reason) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci int u, a; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci u = atomic_read(&cell->ref); 6278c2ecf20Sopenharmony_ci a = atomic_read(&cell->active); 6288c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, u, a, reason); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/* 6328c2ecf20Sopenharmony_ci * Queue a cell for management, giving the workqueue a ref to hold. 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_civoid afs_queue_cell(struct afs_cell *cell, enum afs_cell_trace reason) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci afs_get_cell(cell, reason); 6378c2ecf20Sopenharmony_ci if (!queue_work(afs_wq, &cell->manager)) 6388c2ecf20Sopenharmony_ci afs_put_cell(cell, afs_cell_trace_put_queue_fail); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci/* 6428c2ecf20Sopenharmony_ci * Allocate a key to use as a placeholder for anonymous user security. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_cistatic int afs_alloc_anon_key(struct afs_cell *cell) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct key *key; 6478c2ecf20Sopenharmony_ci char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* Create a key to represent an anonymous user. */ 6508c2ecf20Sopenharmony_ci memcpy(keyname, "afs@", 4); 6518c2ecf20Sopenharmony_ci dp = keyname + 4; 6528c2ecf20Sopenharmony_ci cp = cell->name; 6538c2ecf20Sopenharmony_ci do { 6548c2ecf20Sopenharmony_ci *dp++ = tolower(*cp); 6558c2ecf20Sopenharmony_ci } while (*cp++); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci key = rxrpc_get_null_key(keyname); 6588c2ecf20Sopenharmony_ci if (IS_ERR(key)) 6598c2ecf20Sopenharmony_ci return PTR_ERR(key); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci cell->anonymous_key = key; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci _debug("anon key %p{%x}", 6648c2ecf20Sopenharmony_ci cell->anonymous_key, key_serial(cell->anonymous_key)); 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* 6698c2ecf20Sopenharmony_ci * Activate a cell. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic int afs_activate_cell(struct afs_net *net, struct afs_cell *cell) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct hlist_node **p; 6748c2ecf20Sopenharmony_ci struct afs_cell *pcell; 6758c2ecf20Sopenharmony_ci int ret; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (!cell->anonymous_key) { 6788c2ecf20Sopenharmony_ci ret = afs_alloc_anon_key(cell); 6798c2ecf20Sopenharmony_ci if (ret < 0) 6808c2ecf20Sopenharmony_ci return ret; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE 6848c2ecf20Sopenharmony_ci cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index, 6858c2ecf20Sopenharmony_ci &afs_cell_cache_index_def, 6868c2ecf20Sopenharmony_ci cell->name, strlen(cell->name), 6878c2ecf20Sopenharmony_ci NULL, 0, 6888c2ecf20Sopenharmony_ci cell, 0, true); 6898c2ecf20Sopenharmony_ci#endif 6908c2ecf20Sopenharmony_ci ret = afs_proc_cell_setup(cell); 6918c2ecf20Sopenharmony_ci if (ret < 0) 6928c2ecf20Sopenharmony_ci return ret; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci mutex_lock(&net->proc_cells_lock); 6958c2ecf20Sopenharmony_ci for (p = &net->proc_cells.first; *p; p = &(*p)->next) { 6968c2ecf20Sopenharmony_ci pcell = hlist_entry(*p, struct afs_cell, proc_link); 6978c2ecf20Sopenharmony_ci if (strcmp(cell->name, pcell->name) < 0) 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci cell->proc_link.pprev = p; 7028c2ecf20Sopenharmony_ci cell->proc_link.next = *p; 7038c2ecf20Sopenharmony_ci rcu_assign_pointer(*p, &cell->proc_link.next); 7048c2ecf20Sopenharmony_ci if (cell->proc_link.next) 7058c2ecf20Sopenharmony_ci cell->proc_link.next->pprev = &cell->proc_link.next; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci afs_dynroot_mkdir(net, cell); 7088c2ecf20Sopenharmony_ci mutex_unlock(&net->proc_cells_lock); 7098c2ecf20Sopenharmony_ci return 0; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/* 7138c2ecf20Sopenharmony_ci * Deactivate a cell. 7148c2ecf20Sopenharmony_ci */ 7158c2ecf20Sopenharmony_cistatic void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci _enter("%s", cell->name); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci afs_proc_cell_remove(cell); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci mutex_lock(&net->proc_cells_lock); 7228c2ecf20Sopenharmony_ci hlist_del_rcu(&cell->proc_link); 7238c2ecf20Sopenharmony_ci afs_dynroot_rmdir(net, cell); 7248c2ecf20Sopenharmony_ci mutex_unlock(&net->proc_cells_lock); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE 7278c2ecf20Sopenharmony_ci fscache_relinquish_cookie(cell->cache, NULL, false); 7288c2ecf20Sopenharmony_ci cell->cache = NULL; 7298c2ecf20Sopenharmony_ci#endif 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci _leave(""); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci/* 7358c2ecf20Sopenharmony_ci * Manage a cell record, initialising and destroying it, maintaining its DNS 7368c2ecf20Sopenharmony_ci * records. 7378c2ecf20Sopenharmony_ci */ 7388c2ecf20Sopenharmony_cistatic void afs_manage_cell(struct afs_cell *cell) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct afs_net *net = cell->net; 7418c2ecf20Sopenharmony_ci int ret, active; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci _enter("%s", cell->name); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ciagain: 7468c2ecf20Sopenharmony_ci _debug("state %u", cell->state); 7478c2ecf20Sopenharmony_ci switch (cell->state) { 7488c2ecf20Sopenharmony_ci case AFS_CELL_INACTIVE: 7498c2ecf20Sopenharmony_ci case AFS_CELL_FAILED: 7508c2ecf20Sopenharmony_ci down_write(&net->cells_lock); 7518c2ecf20Sopenharmony_ci active = 1; 7528c2ecf20Sopenharmony_ci if (atomic_try_cmpxchg_relaxed(&cell->active, &active, 0)) { 7538c2ecf20Sopenharmony_ci rb_erase(&cell->net_node, &net->cells); 7548c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 0, 7558c2ecf20Sopenharmony_ci afs_cell_trace_unuse_delete); 7568c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_REMOVED); 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci up_write(&net->cells_lock); 7598c2ecf20Sopenharmony_ci if (cell->state == AFS_CELL_REMOVED) { 7608c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 7618c2ecf20Sopenharmony_ci goto final_destruction; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci if (cell->state == AFS_CELL_FAILED) 7648c2ecf20Sopenharmony_ci goto done; 7658c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_UNSET); 7668c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 7678c2ecf20Sopenharmony_ci goto again; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci case AFS_CELL_UNSET: 7708c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_ACTIVATING); 7718c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 7728c2ecf20Sopenharmony_ci goto again; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci case AFS_CELL_ACTIVATING: 7758c2ecf20Sopenharmony_ci ret = afs_activate_cell(net, cell); 7768c2ecf20Sopenharmony_ci if (ret < 0) 7778c2ecf20Sopenharmony_ci goto activation_failed; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_ACTIVE); 7808c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 7818c2ecf20Sopenharmony_ci goto again; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci case AFS_CELL_ACTIVE: 7848c2ecf20Sopenharmony_ci if (atomic_read(&cell->active) > 1) { 7858c2ecf20Sopenharmony_ci if (test_and_clear_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) { 7868c2ecf20Sopenharmony_ci ret = afs_update_cell(cell); 7878c2ecf20Sopenharmony_ci if (ret < 0) 7888c2ecf20Sopenharmony_ci cell->error = ret; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci goto done; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_DEACTIVATING); 7938c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 7948c2ecf20Sopenharmony_ci goto again; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci case AFS_CELL_DEACTIVATING: 7978c2ecf20Sopenharmony_ci if (atomic_read(&cell->active) > 1) 7988c2ecf20Sopenharmony_ci goto reverse_deactivation; 7998c2ecf20Sopenharmony_ci afs_deactivate_cell(net, cell); 8008c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_INACTIVE); 8018c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 8028c2ecf20Sopenharmony_ci goto again; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci case AFS_CELL_REMOVED: 8058c2ecf20Sopenharmony_ci goto done; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci default: 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci _debug("bad state %u", cell->state); 8118c2ecf20Sopenharmony_ci BUG(); /* Unhandled state */ 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ciactivation_failed: 8148c2ecf20Sopenharmony_ci cell->error = ret; 8158c2ecf20Sopenharmony_ci afs_deactivate_cell(net, cell); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_FAILED); /* vs error */ 8188c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 8198c2ecf20Sopenharmony_ci goto again; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cireverse_deactivation: 8228c2ecf20Sopenharmony_ci smp_store_release(&cell->state, AFS_CELL_ACTIVE); 8238c2ecf20Sopenharmony_ci wake_up_var(&cell->state); 8248c2ecf20Sopenharmony_ci _leave(" [deact->act]"); 8258c2ecf20Sopenharmony_ci return; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cidone: 8288c2ecf20Sopenharmony_ci _leave(" [done %u]", cell->state); 8298c2ecf20Sopenharmony_ci return; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cifinal_destruction: 8328c2ecf20Sopenharmony_ci /* The root volume is pinning the cell */ 8338c2ecf20Sopenharmony_ci afs_put_volume(cell->net, cell->root_volume, afs_volume_trace_put_cell_root); 8348c2ecf20Sopenharmony_ci cell->root_volume = NULL; 8358c2ecf20Sopenharmony_ci afs_put_cell(cell, afs_cell_trace_put_destroy); 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic void afs_manage_cell_work(struct work_struct *work) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct afs_cell *cell = container_of(work, struct afs_cell, manager); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci afs_manage_cell(cell); 8438c2ecf20Sopenharmony_ci afs_put_cell(cell, afs_cell_trace_put_queue_work); 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/* 8478c2ecf20Sopenharmony_ci * Manage the records of cells known to a network namespace. This includes 8488c2ecf20Sopenharmony_ci * updating the DNS records and garbage collecting unused cells that were 8498c2ecf20Sopenharmony_ci * automatically added. 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * Note that constructed cell records may only be removed from net->cells by 8528c2ecf20Sopenharmony_ci * this work item, so it is safe for this work item to stash a cursor pointing 8538c2ecf20Sopenharmony_ci * into the tree and then return to caller (provided it skips cells that are 8548c2ecf20Sopenharmony_ci * still under construction). 8558c2ecf20Sopenharmony_ci * 8568c2ecf20Sopenharmony_ci * Note also that we were given an increment on net->cells_outstanding by 8578c2ecf20Sopenharmony_ci * whoever queued us that we need to deal with before returning. 8588c2ecf20Sopenharmony_ci */ 8598c2ecf20Sopenharmony_civoid afs_manage_cells(struct work_struct *work) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci struct afs_net *net = container_of(work, struct afs_net, cells_manager); 8628c2ecf20Sopenharmony_ci struct rb_node *cursor; 8638c2ecf20Sopenharmony_ci time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX; 8648c2ecf20Sopenharmony_ci bool purging = !net->live; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci _enter(""); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* Trawl the cell database looking for cells that have expired from 8698c2ecf20Sopenharmony_ci * lack of use and cells whose DNS results have expired and dispatch 8708c2ecf20Sopenharmony_ci * their managers. 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_ci down_read(&net->cells_lock); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci for (cursor = rb_first(&net->cells); cursor; cursor = rb_next(cursor)) { 8758c2ecf20Sopenharmony_ci struct afs_cell *cell = 8768c2ecf20Sopenharmony_ci rb_entry(cursor, struct afs_cell, net_node); 8778c2ecf20Sopenharmony_ci unsigned active; 8788c2ecf20Sopenharmony_ci bool sched_cell = false; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci active = atomic_read(&cell->active); 8818c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 8828c2ecf20Sopenharmony_ci active, afs_cell_trace_manage); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci ASSERTCMP(active, >=, 1); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (purging) { 8878c2ecf20Sopenharmony_ci if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags)) { 8888c2ecf20Sopenharmony_ci active = atomic_dec_return(&cell->active); 8898c2ecf20Sopenharmony_ci trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 8908c2ecf20Sopenharmony_ci active, afs_cell_trace_unuse_pin); 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (active == 1) { 8958c2ecf20Sopenharmony_ci struct afs_vlserver_list *vllist; 8968c2ecf20Sopenharmony_ci time64_t expire_at = cell->last_inactive; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci read_lock(&cell->vl_servers_lock); 8998c2ecf20Sopenharmony_ci vllist = rcu_dereference_protected( 9008c2ecf20Sopenharmony_ci cell->vl_servers, 9018c2ecf20Sopenharmony_ci lockdep_is_held(&cell->vl_servers_lock)); 9028c2ecf20Sopenharmony_ci if (vllist->nr_servers > 0) 9038c2ecf20Sopenharmony_ci expire_at += afs_cell_gc_delay; 9048c2ecf20Sopenharmony_ci read_unlock(&cell->vl_servers_lock); 9058c2ecf20Sopenharmony_ci if (purging || expire_at <= now) 9068c2ecf20Sopenharmony_ci sched_cell = true; 9078c2ecf20Sopenharmony_ci else if (expire_at < next_manage) 9088c2ecf20Sopenharmony_ci next_manage = expire_at; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (!purging) { 9128c2ecf20Sopenharmony_ci if (test_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) 9138c2ecf20Sopenharmony_ci sched_cell = true; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (sched_cell) 9178c2ecf20Sopenharmony_ci afs_queue_cell(cell, afs_cell_trace_get_queue_manage); 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci up_read(&net->cells_lock); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* Update the timer on the way out. We have to pass an increment on 9238c2ecf20Sopenharmony_ci * cells_outstanding in the namespace that we are in to the timer or 9248c2ecf20Sopenharmony_ci * the work scheduler. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ci if (!purging && next_manage < TIME64_MAX) { 9278c2ecf20Sopenharmony_ci now = ktime_get_real_seconds(); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (next_manage - now <= 0) { 9308c2ecf20Sopenharmony_ci if (queue_work(afs_wq, &net->cells_manager)) 9318c2ecf20Sopenharmony_ci atomic_inc(&net->cells_outstanding); 9328c2ecf20Sopenharmony_ci } else { 9338c2ecf20Sopenharmony_ci afs_set_cell_timer(net, next_manage - now); 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci afs_dec_cells_outstanding(net); 9388c2ecf20Sopenharmony_ci _leave(" [%d]", atomic_read(&net->cells_outstanding)); 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci/* 9428c2ecf20Sopenharmony_ci * Purge in-memory cell database. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_civoid afs_cell_purge(struct afs_net *net) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct afs_cell *ws; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci _enter(""); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci down_write(&net->cells_lock); 9518c2ecf20Sopenharmony_ci ws = net->ws_cell; 9528c2ecf20Sopenharmony_ci net->ws_cell = NULL; 9538c2ecf20Sopenharmony_ci up_write(&net->cells_lock); 9548c2ecf20Sopenharmony_ci afs_unuse_cell(net, ws, afs_cell_trace_unuse_ws); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci _debug("del timer"); 9578c2ecf20Sopenharmony_ci if (del_timer_sync(&net->cells_timer)) 9588c2ecf20Sopenharmony_ci atomic_dec(&net->cells_outstanding); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci _debug("kick mgr"); 9618c2ecf20Sopenharmony_ci afs_queue_cell_manager(net); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci _debug("wait"); 9648c2ecf20Sopenharmony_ci wait_var_event(&net->cells_outstanding, 9658c2ecf20Sopenharmony_ci !atomic_read(&net->cells_outstanding)); 9668c2ecf20Sopenharmony_ci _leave(""); 9678c2ecf20Sopenharmony_ci} 968