Skip to content

Commit d6aa05c

Browse files
dhowellsgregkh
authored andcommitted
afs: Fix afs_server ref accounting
[ Upstream commit 4882ba7 ] The current way that afs_server refs are accounted and cleaned up sometimes cause rmmod to hang when it is waiting for cell records to be removed. The problem is that the cell cleanup might occasionally happen before the server cleanup and then there's nothing that causes the cell to garbage-collect the remaining servers as they become inactive. Partially fix this by: (1) Give each afs_server record its own management timer that rather than relying on the cell manager's central timer to drive each individual cell's maintenance work item to garbage collect servers. This timer is set when afs_unuse_server() reduces a server's activity count to zero and will schedule the server's destroyer work item upon firing. (2) Give each afs_server record its own destroyer work item that removes the record from the cell's database, shuts down the timer, cancels any pending work for itself, sends an RPC to the server to cancel outstanding callbacks. This change, in combination with the timer, obviates the need to try and coordinate so closely between the cell record and a bunch of other server records to try and tear everything down in a coordinated fashion. With this, the cell record is pinned until the server RCU is complete and namespace/module removal will wait until all the cell records are removed. (3) Now that incoming calls are mapped to servers (and thus cells) using data attached to an rxrpc_peer, the UUID-to-server mapping tree is moved from the namespace to the cell (cell->fs_servers). This means there can no longer be duplicates therein - and that allows the mapping tree to be simpler as there doesn't need to be a chain of same-UUID servers that are in different cells. (4) The lock protecting the UUID mapping tree is switched to an rw_semaphore on the cell rather than a seqlock on the namespace as it's now only used during mounting in contexts in which we're allowed to sleep. (5) When it comes time for a cell that is being removed to purge its set of servers, it just needs to iterate over them and wake them up. Once a server becomes inactive, its destroyer work item will observe the state of the cell and immediately remove that record. (6) When a server record is removed, it is marked AFS_SERVER_FL_EXPIRED to prevent reattempts at removal. The record will be dispatched to RCU for destruction once its refcount reaches 0. (7) The AFS_SERVER_FL_UNCREATED/CREATING flags are used to synchronise simultaneous creation attempts. If one attempt fails, it will abandon the attempt and allow another to try again. Note that the record can't just be abandoned when dead as it's bound into a server list attached to a volume and only subject to replacement if the server list obtained for the volume from the VLDB changes. 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/20250224234154.2014840-15-dhowells@redhat.com/ # v1 Link: https://lore.kernel.org/r/20250310094206.801057-11-dhowells@redhat.com/ # v4 Stable-dep-of: 330e2c5 ("afs: Fix dynamic lookup to fail on cell lookup failure") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 39ba6af commit d6aa05c

7 files changed

Lines changed: 289 additions & 357 deletions

File tree

fs/afs/cell.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
169169
INIT_HLIST_HEAD(&cell->proc_volumes);
170170
seqlock_init(&cell->volume_lock);
171171
cell->fs_servers = RB_ROOT;
172-
seqlock_init(&cell->fs_lock);
172+
init_rwsem(&cell->fs_lock);
173173
rwlock_init(&cell->vl_servers_lock);
174174
cell->flags = (1 << AFS_CELL_FL_CHECK_ALIAS);
175175

@@ -840,6 +840,7 @@ static void afs_manage_cell(struct afs_cell *cell)
840840
/* The root volume is pinning the cell */
841841
afs_put_volume(cell->root_volume, afs_volume_trace_put_cell_root);
842842
cell->root_volume = NULL;
843+
afs_purge_servers(cell);
843844
afs_put_cell(cell, afs_cell_trace_put_destroy);
844845
}
845846

fs/afs/fsclient.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,7 +1650,7 @@ int afs_fs_give_up_all_callbacks(struct afs_net *net, struct afs_server *server,
16501650
bp = call->request;
16511651
*bp++ = htonl(FSGIVEUPALLCALLBACKS);
16521652

1653-
call->server = afs_use_server(server, afs_server_trace_use_give_up_cb);
1653+
call->server = afs_use_server(server, false, afs_server_trace_use_give_up_cb);
16541654
afs_make_call(call, GFP_NOFS);
16551655
afs_wait_for_call_to_complete(call);
16561656
ret = call->error;
@@ -1756,7 +1756,7 @@ bool afs_fs_get_capabilities(struct afs_net *net, struct afs_server *server,
17561756
return false;
17571757

17581758
call->key = key;
1759-
call->server = afs_use_server(server, afs_server_trace_use_get_caps);
1759+
call->server = afs_use_server(server, false, afs_server_trace_use_get_caps);
17601760
call->peer = rxrpc_kernel_get_peer(estate->addresses->addrs[addr_index].peer);
17611761
call->probe = afs_get_endpoint_state(estate, afs_estate_trace_get_getcaps);
17621762
call->probe_index = addr_index;

fs/afs/internal.h

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -317,18 +317,11 @@ struct afs_net {
317317
* cell, but in practice, people create aliases and subsets and there's
318318
* no easy way to distinguish them.
319319
*/
320-
seqlock_t fs_lock; /* For fs_servers, fs_probe_*, fs_proc */
321-
struct rb_root fs_servers; /* afs_server (by server UUID or address) */
320+
seqlock_t fs_lock; /* For fs_probe_*, fs_proc */
322321
struct list_head fs_probe_fast; /* List of afs_server to probe at 30s intervals */
323322
struct list_head fs_probe_slow; /* List of afs_server to probe at 5m intervals */
324323
struct hlist_head fs_proc; /* procfs servers list */
325324

326-
struct hlist_head fs_addresses; /* afs_server (by lowest IPv6 addr) */
327-
seqlock_t fs_addr_lock; /* For fs_addresses[46] */
328-
329-
struct work_struct fs_manager;
330-
struct timer_list fs_timer;
331-
332325
struct work_struct fs_prober;
333326
struct timer_list fs_probe_timer;
334327
atomic_t servers_outstanding;
@@ -424,7 +417,7 @@ struct afs_cell {
424417

425418
/* Active fileserver interaction state. */
426419
struct rb_root fs_servers; /* afs_server (by server UUID) */
427-
seqlock_t fs_lock; /* For fs_servers */
420+
struct rw_semaphore fs_lock; /* For fs_servers */
428421

429422
/* VL server list. */
430423
rwlock_t vl_servers_lock; /* Lock on vl_servers */
@@ -559,22 +552,22 @@ struct afs_server {
559552
};
560553

561554
struct afs_cell *cell; /* Cell to which belongs (pins ref) */
562-
struct rb_node uuid_rb; /* Link in net->fs_servers */
563-
struct afs_server __rcu *uuid_next; /* Next server with same UUID */
564-
struct afs_server *uuid_prev; /* Previous server with same UUID */
565-
struct list_head probe_link; /* Link in net->fs_probe_list */
566-
struct hlist_node addr_link; /* Link in net->fs_addresses6 */
555+
struct rb_node uuid_rb; /* Link in cell->fs_servers */
556+
struct list_head probe_link; /* Link in net->fs_probe_* */
567557
struct hlist_node proc_link; /* Link in net->fs_proc */
568558
struct list_head volumes; /* RCU list of afs_server_entry objects */
569-
struct afs_server *gc_next; /* Next server in manager's list */
559+
struct work_struct destroyer; /* Work item to try and destroy a server */
560+
struct timer_list timer; /* Management timer */
570561
time64_t unuse_time; /* Time at which last unused */
571562
unsigned long flags;
572563
#define AFS_SERVER_FL_RESPONDING 0 /* The server is responding */
573564
#define AFS_SERVER_FL_UPDATING 1
574565
#define AFS_SERVER_FL_NEEDS_UPDATE 2 /* Fileserver address list is out of date */
575-
#define AFS_SERVER_FL_NOT_READY 4 /* The record is not ready for use */
576-
#define AFS_SERVER_FL_NOT_FOUND 5 /* VL server says no such server */
577-
#define AFS_SERVER_FL_VL_FAIL 6 /* Failed to access VL server */
566+
#define AFS_SERVER_FL_UNCREATED 3 /* The record needs creating */
567+
#define AFS_SERVER_FL_CREATING 4 /* The record is being created */
568+
#define AFS_SERVER_FL_EXPIRED 5 /* The record has expired */
569+
#define AFS_SERVER_FL_NOT_FOUND 6 /* VL server says no such server */
570+
#define AFS_SERVER_FL_VL_FAIL 7 /* Failed to access VL server */
578571
#define AFS_SERVER_FL_MAY_HAVE_CB 8 /* May have callbacks on this fileserver */
579572
#define AFS_SERVER_FL_IS_YFS 16 /* Server is YFS not AFS */
580573
#define AFS_SERVER_FL_NO_IBULK 17 /* Fileserver doesn't support FS.InlineBulkStatus */
@@ -584,6 +577,7 @@ struct afs_server {
584577
atomic_t active; /* Active user count */
585578
u32 addr_version; /* Address list version */
586579
u16 service_id; /* Service ID we're using. */
580+
short create_error; /* Creation error */
587581
unsigned int rtt; /* Server's current RTT in uS */
588582
unsigned int debug_id; /* Debugging ID for traces */
589583

@@ -1478,19 +1472,29 @@ extern void __exit afs_clean_up_permit_cache(void);
14781472
extern spinlock_t afs_server_peer_lock;
14791473

14801474
struct afs_server *afs_find_server(const struct rxrpc_peer *peer);
1481-
extern struct afs_server *afs_find_server_by_uuid(struct afs_net *, const uuid_t *);
14821475
extern struct afs_server *afs_lookup_server(struct afs_cell *, struct key *, const uuid_t *, u32);
14831476
extern struct afs_server *afs_get_server(struct afs_server *, enum afs_server_trace);
1484-
extern struct afs_server *afs_use_server(struct afs_server *, enum afs_server_trace);
1485-
extern void afs_unuse_server(struct afs_net *, struct afs_server *, enum afs_server_trace);
1486-
extern void afs_unuse_server_notime(struct afs_net *, struct afs_server *, enum afs_server_trace);
1477+
struct afs_server *afs_use_server(struct afs_server *server, bool activate,
1478+
enum afs_server_trace reason);
1479+
void afs_unuse_server(struct afs_net *net, struct afs_server *server,
1480+
enum afs_server_trace reason);
1481+
void afs_unuse_server_notime(struct afs_net *net, struct afs_server *server,
1482+
enum afs_server_trace reason);
14871483
extern void afs_put_server(struct afs_net *, struct afs_server *, enum afs_server_trace);
1488-
extern void afs_manage_servers(struct work_struct *);
1489-
extern void afs_servers_timer(struct timer_list *);
1484+
void afs_purge_servers(struct afs_cell *cell);
14901485
extern void afs_fs_probe_timer(struct timer_list *);
1491-
extern void __net_exit afs_purge_servers(struct afs_net *);
1486+
void __net_exit afs_wait_for_servers(struct afs_net *net);
14921487
bool afs_check_server_record(struct afs_operation *op, struct afs_server *server, struct key *key);
14931488

1489+
static inline void afs_see_server(struct afs_server *server, enum afs_server_trace trace)
1490+
{
1491+
int r = refcount_read(&server->ref);
1492+
int a = atomic_read(&server->active);
1493+
1494+
trace_afs_server(server->debug_id, r, a, trace);
1495+
1496+
}
1497+
14941498
static inline void afs_inc_servers_outstanding(struct afs_net *net)
14951499
{
14961500
atomic_inc(&net->servers_outstanding);

fs/afs/main.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,10 @@ static int __net_init afs_net_init(struct net *net_ns)
8686
INIT_HLIST_HEAD(&net->proc_cells);
8787

8888
seqlock_init(&net->fs_lock);
89-
net->fs_servers = RB_ROOT;
9089
INIT_LIST_HEAD(&net->fs_probe_fast);
9190
INIT_LIST_HEAD(&net->fs_probe_slow);
9291
INIT_HLIST_HEAD(&net->fs_proc);
9392

94-
INIT_HLIST_HEAD(&net->fs_addresses);
95-
seqlock_init(&net->fs_addr_lock);
96-
97-
INIT_WORK(&net->fs_manager, afs_manage_servers);
98-
timer_setup(&net->fs_timer, afs_servers_timer, 0);
9993
INIT_WORK(&net->fs_prober, afs_fs_probe_dispatcher);
10094
timer_setup(&net->fs_probe_timer, afs_fs_probe_timer, 0);
10195
atomic_set(&net->servers_outstanding, 1);
@@ -131,7 +125,7 @@ static int __net_init afs_net_init(struct net *net_ns)
131125
net->live = false;
132126
afs_fs_probe_cleanup(net);
133127
afs_cell_purge(net);
134-
afs_purge_servers(net);
128+
afs_wait_for_servers(net);
135129
error_cell_init:
136130
net->live = false;
137131
afs_proc_cleanup(net);
@@ -153,7 +147,7 @@ static void __net_exit afs_net_exit(struct net *net_ns)
153147
net->live = false;
154148
afs_fs_probe_cleanup(net);
155149
afs_cell_purge(net);
156-
afs_purge_servers(net);
150+
afs_wait_for_servers(net);
157151
afs_close_socket(net);
158152
afs_proc_cleanup(net);
159153
afs_put_sysnames(net->sysnames);

0 commit comments

Comments
 (0)