afs: Fix cell proc list
authorDavid Howells <dhowells@redhat.com>
Thu, 11 Oct 2018 21:45:49 +0000 (22:45 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 12 Oct 2018 11:18:57 +0000 (13:18 +0200)
Access to the list of cells by /proc/net/afs/cells has a couple of
problems:

 (1) It should be checking against SEQ_START_TOKEN for the keying the
     header line.

 (2) It's only holding the RCU read lock, so it can't just walk over the
     list without following the proper RCU methods.

Fix these by using an hlist instead of an ordinary list and using the
appropriate accessor functions to follow it with RCU.

Since the code that adds a cell to the list must also necessarily change,
sort the list on insertion whilst we're at it.

Fixes: 989782dcdc91 ("afs: Overhaul cell database management")
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/afs/cell.c
fs/afs/dynroot.c
fs/afs/internal.h
fs/afs/main.c
fs/afs/proc.c

index f3d0bef16d78b99291c28e39fc266fa59098572d..6127f0fcd62c4e376bd2554c1003aedb40aab471 100644 (file)
@@ -514,6 +514,8 @@ static int afs_alloc_anon_key(struct afs_cell *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) {
@@ -534,7 +536,18 @@ static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell)
                return ret;
 
        mutex_lock(&net->proc_cells_lock);
-       list_add_tail(&cell->proc_link, &net->proc_cells);
+       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;
@@ -550,7 +563,7 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
        afs_proc_cell_remove(cell);
 
        mutex_lock(&net->proc_cells_lock);
-       list_del_init(&cell->proc_link);
+       hlist_del_rcu(&cell->proc_link);
        afs_dynroot_rmdir(net, cell);
        mutex_unlock(&net->proc_cells_lock);
 
index 1cde710a80133bd4c9e2e88f48b1236fd1034992..f29c6dade7f6250348b886b44b8be150199f78f7 100644 (file)
@@ -265,7 +265,7 @@ int afs_dynroot_populate(struct super_block *sb)
                return -ERESTARTSYS;
 
        net->dynroot_sb = sb;
-       list_for_each_entry(cell, &net->proc_cells, proc_link) {
+       hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
                ret = afs_dynroot_mkdir(net, cell);
                if (ret < 0)
                        goto error;
index 871a228d7f37ce1b0b0d7122b41a8f83f134f391..34c02fdcc25f107ccceca1ca26a304eb37f6e247 100644 (file)
@@ -242,7 +242,7 @@ struct afs_net {
        seqlock_t               cells_lock;
 
        struct mutex            proc_cells_lock;
-       struct list_head        proc_cells;
+       struct hlist_head       proc_cells;
 
        /* Known servers.  Theoretically each fileserver can only be in one
         * cell, but in practice, people create aliases and subsets and there's
@@ -320,7 +320,7 @@ struct afs_cell {
        struct afs_net          *net;
        struct key              *anonymous_key; /* anonymous user key for this cell */
        struct work_struct      manager;        /* Manager for init/deinit/dns */
-       struct list_head        proc_link;      /* /proc cell list link */
+       struct hlist_node       proc_link;      /* /proc cell list link */
 #ifdef CONFIG_AFS_FSCACHE
        struct fscache_cookie   *cache;         /* caching cookie */
 #endif
index e84fe822a960714c8b274435dddbb66f4ba3275a..107427688edddf47fbaf2d865d9421825d0b5244 100644 (file)
@@ -87,7 +87,7 @@ static int __net_init afs_net_init(struct net *net_ns)
        timer_setup(&net->cells_timer, afs_cells_timer, 0);
 
        mutex_init(&net->proc_cells_lock);
-       INIT_LIST_HEAD(&net->proc_cells);
+       INIT_HLIST_HEAD(&net->proc_cells);
 
        seqlock_init(&net->fs_lock);
        net->fs_servers = RB_ROOT;
index 476dcbb79713d20d12023dfb265a0ef62e48e6fc..9101f62707af2da3dbff5e33c6067d0cafbb9013 100644 (file)
@@ -33,9 +33,8 @@ static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
 static int afs_proc_cells_show(struct seq_file *m, void *v)
 {
        struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
-       struct afs_net *net = afs_seq2net(m);
 
-       if (v == &net->proc_cells) {
+       if (v == SEQ_START_TOKEN) {
                /* display header on line 1 */
                seq_puts(m, "USE NAME\n");
                return 0;
@@ -50,12 +49,12 @@ static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
        __acquires(rcu)
 {
        rcu_read_lock();
-       return seq_list_start_head(&afs_seq2net(m)->proc_cells, *_pos);
+       return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
 }
 
 static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
 {
-       return seq_list_next(v, &afs_seq2net(m)->proc_cells, pos);
+       return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
 }
 
 static void afs_proc_cells_stop(struct seq_file *m, void *v)