Commit d057cfec authored by NeilBrown's avatar NeilBrown Committed by Chuck Lever

NFSD: simplify locking for network notifier.

nfsd currently maintains an open-coded read/write semaphore (refcount
and wait queue) for each network namespace to ensure the nfs service
isn't shut down while the notifier is running.

This is excessive.  As there is unlikely to be contention between
notifiers and they run without sleeping, a single spinlock is sufficient
to avoid problems.
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
[ cel: ensure nfsd_notifier_lock is static ]
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 3ebdbe52
...@@ -131,9 +131,6 @@ struct nfsd_net { ...@@ -131,9 +131,6 @@ struct nfsd_net {
*/ */
int keep_active; int keep_active;
wait_queue_head_t ntf_wq;
atomic_t ntf_refcnt;
/* /*
* clientid and stateid data for construction of net unique COPY * clientid and stateid data for construction of net unique COPY
* stateids. * stateids.
......
...@@ -1483,8 +1483,6 @@ static __net_init int nfsd_init_net(struct net *net) ...@@ -1483,8 +1483,6 @@ static __net_init int nfsd_init_net(struct net *net)
nn->clientid_counter = nn->clientid_base + 1; nn->clientid_counter = nn->clientid_base + 1;
nn->s2s_cp_cl_id = nn->clientid_counter++; nn->s2s_cp_cl_id = nn->clientid_counter++;
atomic_set(&nn->ntf_refcnt, 0);
init_waitqueue_head(&nn->ntf_wq);
seqlock_init(&nn->boot_lock); seqlock_init(&nn->boot_lock);
return 0; return 0;
......
...@@ -434,6 +434,7 @@ static void nfsd_shutdown_net(struct net *net) ...@@ -434,6 +434,7 @@ static void nfsd_shutdown_net(struct net *net)
nfsd_shutdown_generic(); nfsd_shutdown_generic();
} }
static DEFINE_SPINLOCK(nfsd_notifier_lock);
static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event, static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
void *ptr) void *ptr)
{ {
...@@ -443,18 +444,17 @@ static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event, ...@@ -443,18 +444,17 @@ static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct sockaddr_in sin; struct sockaddr_in sin;
if ((event != NETDEV_DOWN) || if (event != NETDEV_DOWN || !nn->nfsd_serv)
!atomic_inc_not_zero(&nn->ntf_refcnt))
goto out; goto out;
spin_lock(&nfsd_notifier_lock);
if (nn->nfsd_serv) { if (nn->nfsd_serv) {
dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local); dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local);
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ifa->ifa_local; sin.sin_addr.s_addr = ifa->ifa_local;
svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin); svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin);
} }
atomic_dec(&nn->ntf_refcnt); spin_unlock(&nfsd_notifier_lock);
wake_up(&nn->ntf_wq);
out: out:
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -474,10 +474,10 @@ static int nfsd_inet6addr_event(struct notifier_block *this, ...@@ -474,10 +474,10 @@ static int nfsd_inet6addr_event(struct notifier_block *this,
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
if ((event != NETDEV_DOWN) || if (event != NETDEV_DOWN || !nn->nfsd_serv)
!atomic_inc_not_zero(&nn->ntf_refcnt))
goto out; goto out;
spin_lock(&nfsd_notifier_lock);
if (nn->nfsd_serv) { if (nn->nfsd_serv) {
dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr); dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr);
sin6.sin6_family = AF_INET6; sin6.sin6_family = AF_INET6;
...@@ -486,8 +486,8 @@ static int nfsd_inet6addr_event(struct notifier_block *this, ...@@ -486,8 +486,8 @@ static int nfsd_inet6addr_event(struct notifier_block *this,
sin6.sin6_scope_id = ifa->idev->dev->ifindex; sin6.sin6_scope_id = ifa->idev->dev->ifindex;
svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6); svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6);
} }
atomic_dec(&nn->ntf_refcnt); spin_unlock(&nfsd_notifier_lock);
wake_up(&nn->ntf_wq);
out: out:
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -504,7 +504,6 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net) ...@@ -504,7 +504,6 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
{ {
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
atomic_dec(&nn->ntf_refcnt);
/* check if the notifier still has clients */ /* check if the notifier still has clients */
if (atomic_dec_return(&nfsd_notifier_refcount) == 0) { if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
unregister_inetaddr_notifier(&nfsd_inetaddr_notifier); unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
...@@ -512,7 +511,6 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net) ...@@ -512,7 +511,6 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
unregister_inet6addr_notifier(&nfsd_inet6addr_notifier); unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
#endif #endif
} }
wait_event(nn->ntf_wq, atomic_read(&nn->ntf_refcnt) == 0);
/* /*
* write_ports can create the server without actually starting * write_ports can create the server without actually starting
...@@ -624,6 +622,7 @@ int nfsd_create_serv(struct net *net) ...@@ -624,6 +622,7 @@ int nfsd_create_serv(struct net *net)
{ {
int error; int error;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
WARN_ON(!mutex_is_locked(&nfsd_mutex)); WARN_ON(!mutex_is_locked(&nfsd_mutex));
if (nn->nfsd_serv) { if (nn->nfsd_serv) {
...@@ -633,21 +632,23 @@ int nfsd_create_serv(struct net *net) ...@@ -633,21 +632,23 @@ int nfsd_create_serv(struct net *net)
if (nfsd_max_blksize == 0) if (nfsd_max_blksize == 0)
nfsd_max_blksize = nfsd_get_default_max_blksize(); nfsd_max_blksize = nfsd_get_default_max_blksize();
nfsd_reset_versions(nn); nfsd_reset_versions(nn);
nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
&nfsd_thread_sv_ops); &nfsd_thread_sv_ops);
if (nn->nfsd_serv == NULL) if (serv == NULL)
return -ENOMEM; return -ENOMEM;
nn->nfsd_serv->sv_maxconn = nn->max_connections; serv->sv_maxconn = nn->max_connections;
error = svc_bind(nn->nfsd_serv, net); error = svc_bind(serv, net);
if (error < 0) { if (error < 0) {
/* NOT nfsd_put() as notifiers (see below) haven't /* NOT nfsd_put() as notifiers (see below) haven't
* been set up yet. * been set up yet.
*/ */
svc_put(nn->nfsd_serv); svc_put(serv);
nn->nfsd_serv = NULL;
return error; return error;
} }
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = serv;
spin_unlock(&nfsd_notifier_lock);
set_max_drc(); set_max_drc();
/* check if the notifier is already set */ /* check if the notifier is already set */
...@@ -657,7 +658,6 @@ int nfsd_create_serv(struct net *net) ...@@ -657,7 +658,6 @@ int nfsd_create_serv(struct net *net)
register_inet6addr_notifier(&nfsd_inet6addr_notifier); register_inet6addr_notifier(&nfsd_inet6addr_notifier);
#endif #endif
} }
atomic_inc(&nn->ntf_refcnt);
nfsd_reset_boot_verifier(nn); nfsd_reset_boot_verifier(nn);
return 0; return 0;
} }
...@@ -701,7 +701,9 @@ void nfsd_put(struct net *net) ...@@ -701,7 +701,9 @@ void nfsd_put(struct net *net)
if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) { if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) {
svc_shutdown_net(nn->nfsd_serv, net); svc_shutdown_net(nn->nfsd_serv, net);
svc_destroy(&nn->nfsd_serv->sv_refcnt); svc_destroy(&nn->nfsd_serv->sv_refcnt);
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = NULL; nn->nfsd_serv = NULL;
spin_unlock(&nfsd_notifier_lock);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment