Skip to content

Commit 823869e

Browse files
committed
afs: Fix afs_atcell_get_link() to handle RCU pathwalk
The ->get_link() method may be entered under RCU pathwalk conditions (in which case, the dentry pointer is NULL). This is not taken account of by afs_atcell_get_link() and lockdep will complain when it tries to lock an rwsem. Fix this by marking net->ws_cell as __rcu and using RCU access macros on it and by making afs_atcell_get_link() just return a pointer to the name in RCU pathwalk without taking net->cells_lock or a ref on the cell as RCU will protect the name storage (the cell is already freed via call_rcu()). Fixes: 30bca65 ("afs: Make /afs/@cell and /afs/.@cell symlinks") Reported-by: Alexander Viro <viro@zeniv.linux.org.uk> Signed-off-by: David Howells <dhowells@redhat.com> cc: Marc Dionne <marc.dionne@auristor.com> cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org Link: https://lore.kernel.org/r/20250310094206.801057-2-dhowells@redhat.com/ # v4
1 parent 1e15510 commit 823869e

File tree

4 files changed

+22
-10
lines changed

4 files changed

+22
-10
lines changed

fs/afs/cell.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ static struct afs_cell *afs_find_cell_locked(struct afs_net *net,
6464
return ERR_PTR(-ENAMETOOLONG);
6565

6666
if (!name) {
67-
cell = net->ws_cell;
67+
cell = rcu_dereference_protected(net->ws_cell,
68+
lockdep_is_held(&net->cells_lock));
6869
if (!cell)
6970
return ERR_PTR(-EDESTADDRREQ);
7071
goto found;
@@ -388,8 +389,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
388389
/* install the new cell */
389390
down_write(&net->cells_lock);
390391
afs_see_cell(new_root, afs_cell_trace_see_ws);
391-
old_root = net->ws_cell;
392-
net->ws_cell = new_root;
392+
old_root = rcu_replace_pointer(net->ws_cell, new_root,
393+
lockdep_is_held(&net->cells_lock));
393394
up_write(&net->cells_lock);
394395

395396
afs_unuse_cell(net, old_root, afs_cell_trace_unuse_ws);
@@ -945,8 +946,8 @@ void afs_cell_purge(struct afs_net *net)
945946
_enter("");
946947

947948
down_write(&net->cells_lock);
948-
ws = net->ws_cell;
949-
net->ws_cell = NULL;
949+
ws = rcu_replace_pointer(net->ws_cell, NULL,
950+
lockdep_is_held(&net->cells_lock));
950951
up_write(&net->cells_lock);
951952
afs_unuse_cell(net, ws, afs_cell_trace_unuse_ws);
952953

fs/afs/dynroot.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,23 @@ static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inod
314314
const char *name;
315315
bool dotted = vnode->fid.vnode == 3;
316316

317-
if (!net->ws_cell)
317+
if (!dentry) {
318+
/* We're in RCU-pathwalk. */
319+
cell = rcu_dereference(net->ws_cell);
320+
if (dotted)
321+
name = cell->name - 1;
322+
else
323+
name = cell->name;
324+
/* Shouldn't need to set a delayed call. */
325+
return name;
326+
}
327+
328+
if (!rcu_access_pointer(net->ws_cell))
318329
return ERR_PTR(-ENOENT);
319330

320331
down_read(&net->cells_lock);
321332

322-
cell = net->ws_cell;
333+
cell = rcu_dereference_protected(net->ws_cell, lockdep_is_held(&net->cells_lock));
323334
if (dotted)
324335
name = cell->name - 1;
325336
else

fs/afs/internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ struct afs_net {
287287

288288
/* Cell database */
289289
struct rb_root cells;
290-
struct afs_cell *ws_cell;
290+
struct afs_cell __rcu *ws_cell;
291291
struct work_struct cells_manager;
292292
struct timer_list cells_timer;
293293
atomic_t cells_outstanding;

fs/afs/proc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ static int afs_proc_rootcell_show(struct seq_file *m, void *v)
206206

207207
net = afs_seq2net_single(m);
208208
down_read(&net->cells_lock);
209-
cell = net->ws_cell;
209+
cell = rcu_dereference_protected(net->ws_cell, lockdep_is_held(&net->cells_lock));
210210
if (cell)
211211
seq_printf(m, "%s\n", cell->name);
212212
up_read(&net->cells_lock);
@@ -242,7 +242,7 @@ static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
242242

243243
ret = -EEXIST;
244244
inode_lock(file_inode(file));
245-
if (!net->ws_cell)
245+
if (!rcu_access_pointer(net->ws_cell))
246246
ret = afs_cell_init(net, buf);
247247
else
248248
printk("busy\n");

0 commit comments

Comments
 (0)