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 <snitzer@kernel.org> Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
This commit is contained in:
parent
40f45ab381
commit
bc29408695
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,6 +48,7 @@ void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
|
||||
{
|
||||
if (nfs_uuid->net == NULL) {
|
||||
spin_lock(&nfs_uuid_lock);
|
||||
if (nfs_uuid->net == NULL)
|
||||
list_del_init(&nfs_uuid->list);
|
||||
spin_unlock(&nfs_uuid_lock);
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
Loading…
Reference in New Issue
Block a user