]> www.infradead.org Git - users/hch/misc.git/commitdiff
afs: Fix the server_list to unuse a displaced server rather than putting it
authorDavid Howells <dhowells@redhat.com>
Tue, 18 Feb 2025 19:22:47 +0000 (19:22 +0000)
committerJakub Kicinski <kuba@kernel.org>
Fri, 21 Feb 2025 23:06:29 +0000 (15:06 -0800)
When allocating and building an afs_server_list struct object from a VLDB
record, we look up each server address to get the server record for it -
but a server may have more than one entry in the record and we discard the
duplicate pointers.  Currently, however, when we discard, we only put a
server record, not unuse it - but the lookup got as an active-user count.

The active-user count on an afs_server_list object determines its lifetime
whereas the refcount keeps the memory backing it around.  Failing to reduce
the active-user counter prevents the record from being cleaned up and can
lead to multiple copied being seen - and pointing to deleted afs_cell
objects and other such things.

Fix this by switching the incorrect 'put' to an 'unuse' instead.

Without this, occasionally, a dead server record can be seen in
/proc/net/afs/servers and list corruption may be observed:

    list_del corruption. prev->next should be ffff888102423e40, but was 0000000000000000. (prev=ffff88810140cd38)

Fixes: 977e5f8ed0ab ("afs: Split the usage count on struct afs_server")
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Simon Horman <horms@kernel.org>
cc: linux-afs@lists.infradead.org
Link: https://patch.msgid.link/20250218192250.296870-5-dhowells@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
fs/afs/server_list.c

index 7e7e567a7f8a20269668f13f4a76c739eb29c607..d20cd902ef949ac89ee89709a5d3e7c995f66f31 100644 (file)
@@ -97,8 +97,8 @@ struct afs_server_list *afs_alloc_server_list(struct afs_volume *volume,
                                break;
                if (j < slist->nr_servers) {
                        if (slist->servers[j].server == server) {
-                               afs_put_server(volume->cell->net, server,
-                                              afs_server_trace_put_slist_isort);
+                               afs_unuse_server(volume->cell->net, server,
+                                                afs_server_trace_put_slist_isort);
                                continue;
                        }