diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index eb20e231d7ac..8d640f6537fc 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -286,12 +286,16 @@ static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ct _enter("%llu", ctx->pos); + rcu_read_lock(); for (;;) { unsigned int ix = ctx->pos >> 1; + u8 name_len; + char *name; + unsigned int dynroot_ino; cell = idr_get_next(&net->cells_dyn_ino, &ix); if (!cell) - return 0; + goto unlock; if (READ_ONCE(cell->state) == AFS_CELL_FAILED || READ_ONCE(cell->state) == AFS_CELL_REMOVED) { ctx->pos += 2; @@ -305,19 +309,29 @@ static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ct _debug("pos %llu -> cell %u", ctx->pos, cell->dynroot_ino); + name_len = cell->name_len; + name = cell->name; + dynroot_ino = cell->dynroot_ino; if ((ctx->pos & 1) == 0) { - if (!dir_emit(ctx, cell->name, cell->name_len, - cell->dynroot_ino, DT_DIR)) - return 0; + rcu_read_unlock(); + if (!dir_emit(ctx, name, name_len, + dynroot_ino, DT_DIR)) + goto out; + rcu_read_lock(); ctx->pos++; } if ((ctx->pos & 1) == 1) { - if (!dir_emit(ctx, cell->name - 1, cell->name_len + 1, - cell->dynroot_ino + 1, DT_DIR)) - return 0; + rcu_read_unlock(); + if (!dir_emit(ctx, name - 1, name_len + 1, + dynroot_ino + 1, DT_DIR)) + goto out; + rcu_read_lock(); ctx->pos++; } } +unlock: + rcu_read_unlock(); +out: return 0; } @@ -347,9 +361,7 @@ static int afs_dynroot_readdir(struct file *file, struct dir_context *ctx) } if ((unsigned long long)ctx->pos <= AFS_MAX_DYNROOT_CELL_INO) { - rcu_read_lock(); ret = afs_dynroot_readdir_cells(net, ctx); - rcu_read_unlock(); } return ret; }