]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
nfs_common: fix localio to cope with racing nfs_local_probe()
authorMike Snitzer <snitzer@kernel.org>
Wed, 23 Oct 2024 20:34:42 +0000 (16:34 -0400)
committerAnna Schumaker <anna.schumaker@oracle.com>
Mon, 4 Nov 2024 15:24:19 +0000 (10:24 -0500)
Fix the possibility of racing nfs_local_probe() resulting in:
  list_add double add: new=ffff8b99707f9f58, prev=ffff8b99707f9f58, next=ffffffffc0f30000.
  ------------[ cut here ]------------
  kernel BUG at lib/list_debug.c:35!

Add nfs_uuid_init() to properly initialize all nfs_uuid_t members
(particularly its list_head).

Switch to returning bool from nfs_uuid_begin(), returns false if
nfs_uuid_t is already in-use (its list_head is on a list). Update
nfs_local_probe() to return early if the nfs_client's cl_uuid
(nfs_uuid_t) is in-use.

Also, switch nfs_uuid_begin() from using list_add_tail_rcu() to
list_add_tail() -- rculist was used in an earlier version of the
localio code that had a lockless nfs_uuid_lookup interface.

Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
fs/nfs/client.c
fs/nfs/localio.c
fs/nfs_common/nfslocalio.c
include/linux/nfslocalio.h

index 114282398716c3877ef0f49c3e50bc5a471ec388..03ecc77656151ef89aaf3fa063becec3612372fb 100644 (file)
@@ -181,8 +181,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
 #if IS_ENABLED(CONFIG_NFS_LOCALIO)
        seqlock_init(&clp->cl_boot_lock);
        ktime_get_real_ts64(&clp->cl_nfssvc_boot);
-       clp->cl_uuid.net = NULL;
-       clp->cl_uuid.dom = NULL;
+       nfs_uuid_init(&clp->cl_uuid);
        spin_lock_init(&clp->cl_localio_lock);
 #endif /* CONFIG_NFS_LOCALIO */
 
index d0aa680ec8168b7d281e46db64af68860a846a72..8f0ce82a677e1589092a30240d6e60a289d64a58 100644 (file)
@@ -205,7 +205,8 @@ void nfs_local_probe(struct nfs_client *clp)
                nfs_local_disable(clp);
        }
 
-       nfs_uuid_begin(&clp->cl_uuid);
+       if (!nfs_uuid_begin(&clp->cl_uuid))
+               return;
        if (nfs_server_uuid_is_local(clp))
                nfs_local_enable(clp);
        nfs_uuid_end(&clp->cl_uuid);
index 5c8ce5066c166c2c68e988a12f393db6440e52a5..09404d142d1ae68fd08f03972ba1cc200939b610 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 #include <linux/module.h>
-#include <linux/rculist.h>
+#include <linux/list.h>
 #include <linux/nfslocalio.h>
 #include <net/netns/generic.h>
 
@@ -20,15 +20,27 @@ static DEFINE_SPINLOCK(nfs_uuid_lock);
  */
 static LIST_HEAD(nfs_uuids);
 
-void nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
+void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
 {
        nfs_uuid->net = NULL;
        nfs_uuid->dom = NULL;
-       uuid_gen(&nfs_uuid->uuid);
+       INIT_LIST_HEAD(&nfs_uuid->list);
+}
+EXPORT_SYMBOL_GPL(nfs_uuid_init);
 
+bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
+{
        spin_lock(&nfs_uuid_lock);
-       list_add_tail_rcu(&nfs_uuid->list, &nfs_uuids);
+       /* Is this nfs_uuid already in use? */
+       if (!list_empty(&nfs_uuid->list)) {
+               spin_unlock(&nfs_uuid_lock);
+               return false;
+       }
+       uuid_gen(&nfs_uuid->uuid);
+       list_add_tail(&nfs_uuid->list, &nfs_uuids);
        spin_unlock(&nfs_uuid_lock);
+
+       return true;
 }
 EXPORT_SYMBOL_GPL(nfs_uuid_begin);
 
@@ -36,7 +48,8 @@ void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
 {
        if (nfs_uuid->net == NULL) {
                spin_lock(&nfs_uuid_lock);
-               list_del_init(&nfs_uuid->list);
+               if (nfs_uuid->net == NULL)
+                       list_del_init(&nfs_uuid->list);
                spin_unlock(&nfs_uuid_lock);
        }
 }
index b0dd9b1eef4f9cd9dfdb48e0eb1b75f349ea2520..3982fea799195e7d22d0ae76f826fa60a2e2f579 100644 (file)
@@ -32,7 +32,8 @@ typedef struct {
        struct auth_domain *dom; /* auth_domain for localio */
 } nfs_uuid_t;
 
-void nfs_uuid_begin(nfs_uuid_t *);
+void nfs_uuid_init(nfs_uuid_t *);
+bool nfs_uuid_begin(nfs_uuid_t *);
 void nfs_uuid_end(nfs_uuid_t *);
 void nfs_uuid_is_local(const uuid_t *, struct list_head *,
                       struct net *, struct auth_domain *, struct module *);