123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805 |
- /* AFS cell and server record management
- *
- * Copyright (C) 2002, 2017 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
- #include <linux/slab.h>
- #include <linux/key.h>
- #include <linux/ctype.h>
- #include <linux/dns_resolver.h>
- #include <linux/sched.h>
- #include <linux/inet.h>
- #include <linux/namei.h>
- #include <keys/rxrpc-type.h>
- #include "internal.h"
- static unsigned __read_mostly afs_cell_gc_delay = 10;
- static void afs_manage_cell(struct work_struct *);
- static void afs_dec_cells_outstanding(struct afs_net *net)
- {
- if (atomic_dec_and_test(&net->cells_outstanding))
- wake_up_var(&net->cells_outstanding);
- }
- /*
- * Set the cell timer to fire after a given delay, assuming it's not already
- * set for an earlier time.
- */
- static void afs_set_cell_timer(struct afs_net *net, time64_t delay)
- {
- if (net->live) {
- atomic_inc(&net->cells_outstanding);
- if (timer_reduce(&net->cells_timer, jiffies + delay * HZ))
- afs_dec_cells_outstanding(net);
- }
- }
- /*
- * Look up and get an activation reference on a cell record under RCU
- * conditions. The caller must hold the RCU read lock.
- */
- struct afs_cell *afs_lookup_cell_rcu(struct afs_net *net,
- const char *name, unsigned int namesz)
- {
- struct afs_cell *cell = NULL;
- struct rb_node *p;
- int n, seq = 0, ret = 0;
- _enter("%*.*s", namesz, namesz, name);
- if (name && namesz == 0)
- return ERR_PTR(-EINVAL);
- if (namesz > AFS_MAXCELLNAME)
- return ERR_PTR(-ENAMETOOLONG);
- do {
- /* Unfortunately, rbtree walking doesn't give reliable results
- * under just the RCU read lock, so we have to check for
- * changes.
- */
- if (cell)
- afs_put_cell(net, cell);
- cell = NULL;
- ret = -ENOENT;
- read_seqbegin_or_lock(&net->cells_lock, &seq);
- if (!name) {
- cell = rcu_dereference_raw(net->ws_cell);
- if (cell) {
- afs_get_cell(cell);
- ret = 0;
- break;
- }
- ret = -EDESTADDRREQ;
- continue;
- }
- p = rcu_dereference_raw(net->cells.rb_node);
- while (p) {
- cell = rb_entry(p, struct afs_cell, net_node);
- n = strncasecmp(cell->name, name,
- min_t(size_t, cell->name_len, namesz));
- if (n == 0)
- n = cell->name_len - namesz;
- if (n < 0) {
- p = rcu_dereference_raw(p->rb_left);
- } else if (n > 0) {
- p = rcu_dereference_raw(p->rb_right);
- } else {
- if (atomic_inc_not_zero(&cell->usage)) {
- ret = 0;
- break;
- }
- /* We want to repeat the search, this time with
- * the lock properly locked.
- */
- }
- cell = NULL;
- }
- } while (need_seqretry(&net->cells_lock, seq));
- done_seqretry(&net->cells_lock, seq);
- if (ret != 0 && cell)
- afs_put_cell(net, cell);
- return ret == 0 ? cell : ERR_PTR(ret);
- }
- /*
- * Set up a cell record and fill in its name, VL server address list and
- * allocate an anonymous key
- */
- static struct afs_cell *afs_alloc_cell(struct afs_net *net,
- const char *name, unsigned int namelen,
- const char *vllist)
- {
- struct afs_cell *cell;
- int i, ret;
- ASSERT(name);
- if (namelen == 0)
- return ERR_PTR(-EINVAL);
- if (namelen > AFS_MAXCELLNAME) {
- _leave(" = -ENAMETOOLONG");
- return ERR_PTR(-ENAMETOOLONG);
- }
- /* Prohibit cell names that contain unprintable chars, '/' and '@' or
- * that begin with a dot. This also precludes "@cell".
- */
- if (name[0] == '.')
- return ERR_PTR(-EINVAL);
- for (i = 0; i < namelen; i++) {
- char ch = name[i];
- if (!isprint(ch) || ch == '/' || ch == '@')
- return ERR_PTR(-EINVAL);
- }
- _enter("%*.*s,%s", namelen, namelen, name, vllist);
- cell = kzalloc(sizeof(struct afs_cell), GFP_KERNEL);
- if (!cell) {
- _leave(" = -ENOMEM");
- return ERR_PTR(-ENOMEM);
- }
- cell->net = net;
- cell->name_len = namelen;
- for (i = 0; i < namelen; i++)
- cell->name[i] = tolower(name[i]);
- atomic_set(&cell->usage, 2);
- INIT_WORK(&cell->manager, afs_manage_cell);
- cell->flags = ((1 << AFS_CELL_FL_NOT_READY) |
- (1 << AFS_CELL_FL_NO_LOOKUP_YET));
- INIT_LIST_HEAD(&cell->proc_volumes);
- rwlock_init(&cell->proc_lock);
- rwlock_init(&cell->vl_addrs_lock);
- /* Fill in the VL server list if we were given a list of addresses to
- * use.
- */
- if (vllist) {
- struct afs_addr_list *alist;
- alist = afs_parse_text_addrs(vllist, strlen(vllist), ':',
- VL_SERVICE, AFS_VL_PORT);
- if (IS_ERR(alist)) {
- ret = PTR_ERR(alist);
- goto parse_failed;
- }
- rcu_assign_pointer(cell->vl_addrs, alist);
- cell->dns_expiry = TIME64_MAX;
- }
- _leave(" = %p", cell);
- return cell;
- parse_failed:
- if (ret == -EINVAL)
- printk(KERN_ERR "kAFS: bad VL server IP address\n");
- kfree(cell);
- _leave(" = %d", ret);
- return ERR_PTR(ret);
- }
- /*
- * afs_lookup_cell - Look up or create a cell record.
- * @net: The network namespace
- * @name: The name of the cell.
- * @namesz: The strlen of the cell name.
- * @vllist: A colon/comma separated list of numeric IP addresses or NULL.
- * @excl: T if an error should be given if the cell name already exists.
- *
- * Look up a cell record by name and query the DNS for VL server addresses if
- * needed. Note that that actual DNS query is punted off to the manager thread
- * so that this function can return immediately if interrupted whilst allowing
- * cell records to be shared even if not yet fully constructed.
- */
- struct afs_cell *afs_lookup_cell(struct afs_net *net,
- const char *name, unsigned int namesz,
- const char *vllist, bool excl)
- {
- struct afs_cell *cell, *candidate, *cursor;
- struct rb_node *parent, **pp;
- int ret, n;
- _enter("%s,%s", name, vllist);
- if (!excl) {
- rcu_read_lock();
- cell = afs_lookup_cell_rcu(net, name, namesz);
- rcu_read_unlock();
- if (!IS_ERR(cell))
- goto wait_for_cell;
- }
- /* Assume we're probably going to create a cell and preallocate and
- * mostly set up a candidate record. We can then use this to stash the
- * name, the net namespace and VL server addresses.
- *
- * We also want to do this before we hold any locks as it may involve
- * upcalling to userspace to make DNS queries.
- */
- candidate = afs_alloc_cell(net, name, namesz, vllist);
- if (IS_ERR(candidate)) {
- _leave(" = %ld", PTR_ERR(candidate));
- return candidate;
- }
- /* Find the insertion point and check to see if someone else added a
- * cell whilst we were allocating.
- */
- write_seqlock(&net->cells_lock);
- pp = &net->cells.rb_node;
- parent = NULL;
- while (*pp) {
- parent = *pp;
- cursor = rb_entry(parent, struct afs_cell, net_node);
- n = strncasecmp(cursor->name, name,
- min_t(size_t, cursor->name_len, namesz));
- if (n == 0)
- n = cursor->name_len - namesz;
- if (n < 0)
- pp = &(*pp)->rb_left;
- else if (n > 0)
- pp = &(*pp)->rb_right;
- else
- goto cell_already_exists;
- }
- cell = candidate;
- candidate = NULL;
- rb_link_node_rcu(&cell->net_node, parent, pp);
- rb_insert_color(&cell->net_node, &net->cells);
- atomic_inc(&net->cells_outstanding);
- write_sequnlock(&net->cells_lock);
- queue_work(afs_wq, &cell->manager);
- wait_for_cell:
- _debug("wait_for_cell");
- ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NOT_READY, TASK_INTERRUPTIBLE);
- smp_rmb();
- switch (READ_ONCE(cell->state)) {
- case AFS_CELL_FAILED:
- ret = cell->error;
- goto error;
- default:
- _debug("weird %u %d", cell->state, cell->error);
- goto error;
- case AFS_CELL_ACTIVE:
- break;
- }
- _leave(" = %p [cell]", cell);
- return cell;
- cell_already_exists:
- _debug("cell exists");
- cell = cursor;
- if (excl) {
- ret = -EEXIST;
- } else {
- afs_get_cell(cursor);
- ret = 0;
- }
- write_sequnlock(&net->cells_lock);
- kfree(candidate);
- if (ret == 0)
- goto wait_for_cell;
- goto error_noput;
- error:
- afs_put_cell(net, cell);
- error_noput:
- _leave(" = %d [error]", ret);
- return ERR_PTR(ret);
- }
- /*
- * set the root cell information
- * - can be called with a module parameter string
- * - can be called from a write to /proc/fs/afs/rootcell
- */
- int afs_cell_init(struct afs_net *net, const char *rootcell)
- {
- struct afs_cell *old_root, *new_root;
- const char *cp, *vllist;
- size_t len;
- _enter("");
- if (!rootcell) {
- /* module is loaded with no parameters, or built statically.
- * - in the future we might initialize cell DB here.
- */
- _leave(" = 0 [no root]");
- return 0;
- }
- cp = strchr(rootcell, ':');
- if (!cp) {
- _debug("kAFS: no VL server IP addresses specified");
- vllist = NULL;
- len = strlen(rootcell);
- } else {
- vllist = cp + 1;
- len = cp - rootcell;
- }
- /* allocate a cell record for the root cell */
- new_root = afs_lookup_cell(net, rootcell, len, vllist, false);
- if (IS_ERR(new_root)) {
- _leave(" = %ld", PTR_ERR(new_root));
- return PTR_ERR(new_root);
- }
- if (!test_and_set_bit(AFS_CELL_FL_NO_GC, &new_root->flags))
- afs_get_cell(new_root);
- /* install the new cell */
- write_seqlock(&net->cells_lock);
- old_root = rcu_access_pointer(net->ws_cell);
- rcu_assign_pointer(net->ws_cell, new_root);
- write_sequnlock(&net->cells_lock);
- afs_put_cell(net, old_root);
- _leave(" = 0");
- return 0;
- }
- /*
- * Update a cell's VL server address list from the DNS.
- */
- static void afs_update_cell(struct afs_cell *cell)
- {
- struct afs_addr_list *alist, *old;
- time64_t now, expiry;
- _enter("%s", cell->name);
- alist = afs_dns_query(cell, &expiry);
- if (IS_ERR(alist)) {
- switch (PTR_ERR(alist)) {
- case -ENODATA:
- /* The DNS said that the cell does not exist */
- set_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- cell->dns_expiry = ktime_get_real_seconds() + 61;
- break;
- case -EAGAIN:
- case -ECONNREFUSED:
- default:
- set_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- cell->dns_expiry = ktime_get_real_seconds() + 10;
- break;
- }
- cell->error = -EDESTADDRREQ;
- } else {
- clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- clear_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- /* Exclusion on changing vl_addrs is achieved by a
- * non-reentrant work item.
- */
- old = rcu_dereference_protected(cell->vl_addrs, true);
- rcu_assign_pointer(cell->vl_addrs, alist);
- cell->dns_expiry = expiry;
- if (old)
- afs_put_addrlist(old);
- }
- if (test_and_clear_bit(AFS_CELL_FL_NO_LOOKUP_YET, &cell->flags))
- wake_up_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET);
- now = ktime_get_real_seconds();
- afs_set_cell_timer(cell->net, cell->dns_expiry - now);
- _leave("");
- }
- /*
- * Destroy a cell record
- */
- static void afs_cell_destroy(struct rcu_head *rcu)
- {
- struct afs_cell *cell = container_of(rcu, struct afs_cell, rcu);
- _enter("%p{%s}", cell, cell->name);
- ASSERTCMP(atomic_read(&cell->usage), ==, 0);
- afs_put_addrlist(rcu_access_pointer(cell->vl_addrs));
- key_put(cell->anonymous_key);
- kfree(cell);
- _leave(" [destroyed]");
- }
- /*
- * Queue the cell manager.
- */
- static void afs_queue_cell_manager(struct afs_net *net)
- {
- int outstanding = atomic_inc_return(&net->cells_outstanding);
- _enter("%d", outstanding);
- if (!queue_work(afs_wq, &net->cells_manager))
- afs_dec_cells_outstanding(net);
- }
- /*
- * Cell management timer. We have an increment on cells_outstanding that we
- * need to pass along to the work item.
- */
- void afs_cells_timer(struct timer_list *timer)
- {
- struct afs_net *net = container_of(timer, struct afs_net, cells_timer);
- _enter("");
- if (!queue_work(afs_wq, &net->cells_manager))
- afs_dec_cells_outstanding(net);
- }
- /*
- * Get a reference on a cell record.
- */
- struct afs_cell *afs_get_cell(struct afs_cell *cell)
- {
- atomic_inc(&cell->usage);
- return cell;
- }
- /*
- * Drop a reference on a cell record.
- */
- void afs_put_cell(struct afs_net *net, struct afs_cell *cell)
- {
- time64_t now, expire_delay;
- if (!cell)
- return;
- _enter("%s", cell->name);
- now = ktime_get_real_seconds();
- cell->last_inactive = now;
- expire_delay = 0;
- if (!test_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags) &&
- !test_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags))
- expire_delay = afs_cell_gc_delay;
- if (atomic_dec_return(&cell->usage) > 1)
- return;
- /* 'cell' may now be garbage collected. */
- afs_set_cell_timer(net, expire_delay);
- }
- /*
- * Allocate a key to use as a placeholder for anonymous user security.
- */
- static int afs_alloc_anon_key(struct afs_cell *cell)
- {
- struct key *key;
- char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp;
- /* Create a key to represent an anonymous user. */
- memcpy(keyname, "afs@", 4);
- dp = keyname + 4;
- cp = cell->name;
- do {
- *dp++ = tolower(*cp);
- } while (*cp++);
- key = rxrpc_get_null_key(keyname);
- if (IS_ERR(key))
- return PTR_ERR(key);
- cell->anonymous_key = key;
- _debug("anon key %p{%x}",
- cell->anonymous_key, key_serial(cell->anonymous_key));
- return 0;
- }
- /*
- * Activate a cell.
- */
- static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell)
- {
- struct hlist_node **p;
- struct afs_cell *pcell;
- int ret;
- if (!cell->anonymous_key) {
- ret = afs_alloc_anon_key(cell);
- if (ret < 0)
- return ret;
- }
- #ifdef CONFIG_AFS_FSCACHE
- cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index,
- &afs_cell_cache_index_def,
- cell->name, strlen(cell->name),
- NULL, 0,
- cell, 0, true);
- #endif
- ret = afs_proc_cell_setup(cell);
- if (ret < 0)
- return ret;
- mutex_lock(&net->proc_cells_lock);
- for (p = &net->proc_cells.first; *p; p = &(*p)->next) {
- pcell = hlist_entry(*p, struct afs_cell, proc_link);
- if (strcmp(cell->name, pcell->name) < 0)
- break;
- }
- cell->proc_link.pprev = p;
- cell->proc_link.next = *p;
- rcu_assign_pointer(*p, &cell->proc_link.next);
- if (cell->proc_link.next)
- cell->proc_link.next->pprev = &cell->proc_link.next;
- afs_dynroot_mkdir(net, cell);
- mutex_unlock(&net->proc_cells_lock);
- return 0;
- }
- /*
- * Deactivate a cell.
- */
- static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
- {
- _enter("%s", cell->name);
- afs_proc_cell_remove(cell);
- mutex_lock(&net->proc_cells_lock);
- hlist_del_rcu(&cell->proc_link);
- afs_dynroot_rmdir(net, cell);
- mutex_unlock(&net->proc_cells_lock);
- #ifdef CONFIG_AFS_FSCACHE
- fscache_relinquish_cookie(cell->cache, NULL, false);
- cell->cache = NULL;
- #endif
- _leave("");
- }
- /*
- * Manage a cell record, initialising and destroying it, maintaining its DNS
- * records.
- */
- static void afs_manage_cell(struct work_struct *work)
- {
- struct afs_cell *cell = container_of(work, struct afs_cell, manager);
- struct afs_net *net = cell->net;
- bool deleted;
- int ret, usage;
- _enter("%s", cell->name);
- again:
- _debug("state %u", cell->state);
- switch (cell->state) {
- case AFS_CELL_INACTIVE:
- case AFS_CELL_FAILED:
- write_seqlock(&net->cells_lock);
- usage = 1;
- deleted = atomic_try_cmpxchg_relaxed(&cell->usage, &usage, 0);
- if (deleted)
- rb_erase(&cell->net_node, &net->cells);
- write_sequnlock(&net->cells_lock);
- if (deleted)
- goto final_destruction;
- if (cell->state == AFS_CELL_FAILED)
- goto done;
- cell->state = AFS_CELL_UNSET;
- goto again;
- case AFS_CELL_UNSET:
- cell->state = AFS_CELL_ACTIVATING;
- goto again;
- case AFS_CELL_ACTIVATING:
- ret = afs_activate_cell(net, cell);
- if (ret < 0)
- goto activation_failed;
- cell->state = AFS_CELL_ACTIVE;
- smp_wmb();
- clear_bit(AFS_CELL_FL_NOT_READY, &cell->flags);
- wake_up_bit(&cell->flags, AFS_CELL_FL_NOT_READY);
- goto again;
- case AFS_CELL_ACTIVE:
- if (atomic_read(&cell->usage) > 1) {
- time64_t now = ktime_get_real_seconds();
- if (cell->dns_expiry <= now && net->live)
- afs_update_cell(cell);
- goto done;
- }
- cell->state = AFS_CELL_DEACTIVATING;
- goto again;
- case AFS_CELL_DEACTIVATING:
- set_bit(AFS_CELL_FL_NOT_READY, &cell->flags);
- if (atomic_read(&cell->usage) > 1)
- goto reverse_deactivation;
- afs_deactivate_cell(net, cell);
- cell->state = AFS_CELL_INACTIVE;
- goto again;
- default:
- break;
- }
- _debug("bad state %u", cell->state);
- BUG(); /* Unhandled state */
- activation_failed:
- cell->error = ret;
- afs_deactivate_cell(net, cell);
- cell->state = AFS_CELL_FAILED;
- smp_wmb();
- if (test_and_clear_bit(AFS_CELL_FL_NOT_READY, &cell->flags))
- wake_up_bit(&cell->flags, AFS_CELL_FL_NOT_READY);
- goto again;
- reverse_deactivation:
- cell->state = AFS_CELL_ACTIVE;
- smp_wmb();
- clear_bit(AFS_CELL_FL_NOT_READY, &cell->flags);
- wake_up_bit(&cell->flags, AFS_CELL_FL_NOT_READY);
- _leave(" [deact->act]");
- return;
- done:
- _leave(" [done %u]", cell->state);
- return;
- final_destruction:
- call_rcu(&cell->rcu, afs_cell_destroy);
- afs_dec_cells_outstanding(net);
- _leave(" [destruct %d]", atomic_read(&net->cells_outstanding));
- }
- /*
- * Manage the records of cells known to a network namespace. This includes
- * updating the DNS records and garbage collecting unused cells that were
- * automatically added.
- *
- * Note that constructed cell records may only be removed from net->cells by
- * this work item, so it is safe for this work item to stash a cursor pointing
- * into the tree and then return to caller (provided it skips cells that are
- * still under construction).
- *
- * Note also that we were given an increment on net->cells_outstanding by
- * whoever queued us that we need to deal with before returning.
- */
- void afs_manage_cells(struct work_struct *work)
- {
- struct afs_net *net = container_of(work, struct afs_net, cells_manager);
- struct rb_node *cursor;
- time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX;
- bool purging = !net->live;
- _enter("");
- /* Trawl the cell database looking for cells that have expired from
- * lack of use and cells whose DNS results have expired and dispatch
- * their managers.
- */
- read_seqlock_excl(&net->cells_lock);
- for (cursor = rb_first(&net->cells); cursor; cursor = rb_next(cursor)) {
- struct afs_cell *cell =
- rb_entry(cursor, struct afs_cell, net_node);
- unsigned usage;
- bool sched_cell = false;
- usage = atomic_read(&cell->usage);
- _debug("manage %s %u", cell->name, usage);
- ASSERTCMP(usage, >=, 1);
- if (purging) {
- if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags))
- usage = atomic_dec_return(&cell->usage);
- ASSERTCMP(usage, ==, 1);
- }
- if (usage == 1) {
- time64_t expire_at = cell->last_inactive;
- if (!test_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags) &&
- !test_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags))
- expire_at += afs_cell_gc_delay;
- if (purging || expire_at <= now)
- sched_cell = true;
- else if (expire_at < next_manage)
- next_manage = expire_at;
- }
- if (!purging) {
- if (cell->dns_expiry <= now)
- sched_cell = true;
- else if (cell->dns_expiry <= next_manage)
- next_manage = cell->dns_expiry;
- }
- if (sched_cell)
- queue_work(afs_wq, &cell->manager);
- }
- read_sequnlock_excl(&net->cells_lock);
- /* Update the timer on the way out. We have to pass an increment on
- * cells_outstanding in the namespace that we are in to the timer or
- * the work scheduler.
- */
- if (!purging && next_manage < TIME64_MAX) {
- now = ktime_get_real_seconds();
- if (next_manage - now <= 0) {
- if (queue_work(afs_wq, &net->cells_manager))
- atomic_inc(&net->cells_outstanding);
- } else {
- afs_set_cell_timer(net, next_manage - now);
- }
- }
- afs_dec_cells_outstanding(net);
- _leave(" [%d]", atomic_read(&net->cells_outstanding));
- }
- /*
- * Purge in-memory cell database.
- */
- void afs_cell_purge(struct afs_net *net)
- {
- struct afs_cell *ws;
- _enter("");
- write_seqlock(&net->cells_lock);
- ws = rcu_access_pointer(net->ws_cell);
- RCU_INIT_POINTER(net->ws_cell, NULL);
- write_sequnlock(&net->cells_lock);
- afs_put_cell(net, ws);
- _debug("del timer");
- if (del_timer_sync(&net->cells_timer))
- atomic_dec(&net->cells_outstanding);
- _debug("kick mgr");
- afs_queue_cell_manager(net);
- _debug("wait");
- wait_var_event(&net->cells_outstanding,
- !atomic_read(&net->cells_outstanding));
- _leave("");
- }
|