Skip to content

Commit

Permalink
afs: Fix cell proc list
Browse files Browse the repository at this point in the history
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: 989782d ("afs: Overhaul cell database management")
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
David Howells authored and Greg Kroah-Hartman committed Oct 12, 2018
1 parent 4ea07ab commit 6b3944e
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 10 deletions.
17 changes: 15 additions & 2 deletions fs/afs/cell.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion fs/afs/dynroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions fs/afs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion fs/afs/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 3 additions & 4 deletions fs/afs/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down

0 comments on commit 6b3944e

Please sign in to comment.