Skip to content

Commit bc29408

Browse files
Mike SnitzerAnna Schumaker
authored andcommitted
nfs_common: fix localio to cope with racing nfs_local_probe()
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 <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent 40f45ab commit bc29408

File tree

4 files changed

+23
-9
lines changed

4 files changed

+23
-9
lines changed

fs/nfs/client.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
181181
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
182182
seqlock_init(&clp->cl_boot_lock);
183183
ktime_get_real_ts64(&clp->cl_nfssvc_boot);
184-
clp->cl_uuid.net = NULL;
185-
clp->cl_uuid.dom = NULL;
184+
nfs_uuid_init(&clp->cl_uuid);
186185
spin_lock_init(&clp->cl_localio_lock);
187186
#endif /* CONFIG_NFS_LOCALIO */
188187

fs/nfs/localio.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ void nfs_local_probe(struct nfs_client *clp)
205205
nfs_local_disable(clp);
206206
}
207207

208-
nfs_uuid_begin(&clp->cl_uuid);
208+
if (!nfs_uuid_begin(&clp->cl_uuid))
209+
return;
209210
if (nfs_server_uuid_is_local(clp))
210211
nfs_local_enable(clp);
211212
nfs_uuid_end(&clp->cl_uuid);

fs/nfs_common/nfslocalio.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
#include <linux/module.h>
8-
#include <linux/rculist.h>
8+
#include <linux/list.h>
99
#include <linux/nfslocalio.h>
1010
#include <net/netns/generic.h>
1111

@@ -20,23 +20,36 @@ static DEFINE_SPINLOCK(nfs_uuid_lock);
2020
*/
2121
static LIST_HEAD(nfs_uuids);
2222

23-
void nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
23+
void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
2424
{
2525
nfs_uuid->net = NULL;
2626
nfs_uuid->dom = NULL;
27-
uuid_gen(&nfs_uuid->uuid);
27+
INIT_LIST_HEAD(&nfs_uuid->list);
28+
}
29+
EXPORT_SYMBOL_GPL(nfs_uuid_init);
2830

31+
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
32+
{
2933
spin_lock(&nfs_uuid_lock);
30-
list_add_tail_rcu(&nfs_uuid->list, &nfs_uuids);
34+
/* Is this nfs_uuid already in use? */
35+
if (!list_empty(&nfs_uuid->list)) {
36+
spin_unlock(&nfs_uuid_lock);
37+
return false;
38+
}
39+
uuid_gen(&nfs_uuid->uuid);
40+
list_add_tail(&nfs_uuid->list, &nfs_uuids);
3141
spin_unlock(&nfs_uuid_lock);
42+
43+
return true;
3244
}
3345
EXPORT_SYMBOL_GPL(nfs_uuid_begin);
3446

3547
void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
3648
{
3749
if (nfs_uuid->net == NULL) {
3850
spin_lock(&nfs_uuid_lock);
39-
list_del_init(&nfs_uuid->list);
51+
if (nfs_uuid->net == NULL)
52+
list_del_init(&nfs_uuid->list);
4053
spin_unlock(&nfs_uuid_lock);
4154
}
4255
}

include/linux/nfslocalio.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ typedef struct {
3232
struct auth_domain *dom; /* auth_domain for localio */
3333
} nfs_uuid_t;
3434

35-
void nfs_uuid_begin(nfs_uuid_t *);
35+
void nfs_uuid_init(nfs_uuid_t *);
36+
bool nfs_uuid_begin(nfs_uuid_t *);
3637
void nfs_uuid_end(nfs_uuid_t *);
3738
void nfs_uuid_is_local(const uuid_t *, struct list_head *,
3839
struct net *, struct auth_domain *, struct module *);

0 commit comments

Comments
 (0)