nfsd: make client_lock per net
This lock protects the client lru list and session hash table, which are allocated per network namespace already. Signed-off-by: Stanislav Kinsbursky <skinsbursky@parallels.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
parent
ec28e02ca5
commit
c9a4962881
|
@ -81,6 +81,9 @@ struct nfsd_net {
|
||||||
struct list_head close_lru;
|
struct list_head close_lru;
|
||||||
|
|
||||||
struct delayed_work laundromat_work;
|
struct delayed_work laundromat_work;
|
||||||
|
|
||||||
|
/* client_lock protects the client lru list and session hash table */
|
||||||
|
spinlock_t client_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int nfsd_net_id;
|
extern int nfsd_net_id;
|
||||||
|
|
|
@ -388,9 +388,6 @@ unhash_delegation(struct nfs4_delegation *dp)
|
||||||
* SETCLIENTID state
|
* SETCLIENTID state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* client_lock protects the client lru list and session hash table */
|
|
||||||
static DEFINE_SPINLOCK(client_lock);
|
|
||||||
|
|
||||||
static unsigned int clientid_hashval(u32 id)
|
static unsigned int clientid_hashval(u32 id)
|
||||||
{
|
{
|
||||||
return id & CLIENT_HASH_MASK;
|
return id & CLIENT_HASH_MASK;
|
||||||
|
@ -872,18 +869,23 @@ static void __free_session(struct nfsd4_session *ses)
|
||||||
static void free_session(struct kref *kref)
|
static void free_session(struct kref *kref)
|
||||||
{
|
{
|
||||||
struct nfsd4_session *ses;
|
struct nfsd4_session *ses;
|
||||||
|
struct nfsd_net *nn;
|
||||||
|
|
||||||
lockdep_assert_held(&client_lock);
|
|
||||||
ses = container_of(kref, struct nfsd4_session, se_ref);
|
ses = container_of(kref, struct nfsd4_session, se_ref);
|
||||||
|
nn = net_generic(ses->se_client->net, nfsd_net_id);
|
||||||
|
|
||||||
|
lockdep_assert_held(&nn->client_lock);
|
||||||
nfsd4_del_conns(ses);
|
nfsd4_del_conns(ses);
|
||||||
__free_session(ses);
|
__free_session(ses);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfsd4_put_session(struct nfsd4_session *ses)
|
void nfsd4_put_session(struct nfsd4_session *ses)
|
||||||
{
|
{
|
||||||
spin_lock(&client_lock);
|
struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id);
|
||||||
|
|
||||||
|
spin_lock(&nn->client_lock);
|
||||||
nfsd4_put_session_locked(ses);
|
nfsd4_put_session_locked(ses);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan)
|
static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan)
|
||||||
|
@ -927,12 +929,12 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
|
||||||
new->se_cb_sec = cses->cb_sec;
|
new->se_cb_sec = cses->cb_sec;
|
||||||
kref_init(&new->se_ref);
|
kref_init(&new->se_ref);
|
||||||
idx = hash_sessionid(&new->se_sessionid);
|
idx = hash_sessionid(&new->se_sessionid);
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
|
list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
|
||||||
spin_lock(&clp->cl_lock);
|
spin_lock(&clp->cl_lock);
|
||||||
list_add(&new->se_perclnt, &clp->cl_sessions);
|
list_add(&new->se_perclnt, &clp->cl_sessions);
|
||||||
spin_unlock(&clp->cl_lock);
|
spin_unlock(&clp->cl_lock);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
|
|
||||||
if (cses->flags & SESSION4_BACK_CHAN) {
|
if (cses->flags & SESSION4_BACK_CHAN) {
|
||||||
struct sockaddr *sa = svc_addr(rqstp);
|
struct sockaddr *sa = svc_addr(rqstp);
|
||||||
|
@ -1005,9 +1007,11 @@ renew_client_locked(struct nfs4_client *clp)
|
||||||
static inline void
|
static inline void
|
||||||
renew_client(struct nfs4_client *clp)
|
renew_client(struct nfs4_client *clp)
|
||||||
{
|
{
|
||||||
spin_lock(&client_lock);
|
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
|
||||||
|
|
||||||
|
spin_lock(&nn->client_lock);
|
||||||
renew_client_locked(clp);
|
renew_client_locked(clp);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
|
/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
|
||||||
|
@ -1045,7 +1049,9 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
|
||||||
static inline void
|
static inline void
|
||||||
free_client(struct nfs4_client *clp)
|
free_client(struct nfs4_client *clp)
|
||||||
{
|
{
|
||||||
lockdep_assert_held(&client_lock);
|
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
|
||||||
|
|
||||||
|
lockdep_assert_held(&nn->client_lock);
|
||||||
while (!list_empty(&clp->cl_sessions)) {
|
while (!list_empty(&clp->cl_sessions)) {
|
||||||
struct nfsd4_session *ses;
|
struct nfsd4_session *ses;
|
||||||
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
|
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
|
||||||
|
@ -1062,15 +1068,16 @@ void
|
||||||
release_session_client(struct nfsd4_session *session)
|
release_session_client(struct nfsd4_session *session)
|
||||||
{
|
{
|
||||||
struct nfs4_client *clp = session->se_client;
|
struct nfs4_client *clp = session->se_client;
|
||||||
|
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
|
||||||
|
|
||||||
if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock))
|
if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock))
|
||||||
return;
|
return;
|
||||||
if (is_client_expired(clp)) {
|
if (is_client_expired(clp)) {
|
||||||
free_client(clp);
|
free_client(clp);
|
||||||
session->se_client = NULL;
|
session->se_client = NULL;
|
||||||
} else
|
} else
|
||||||
renew_client_locked(clp);
|
renew_client_locked(clp);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* must be called under the client_lock */
|
/* must be called under the client_lock */
|
||||||
|
@ -1119,11 +1126,11 @@ destroy_client(struct nfs4_client *clp)
|
||||||
rb_erase(&clp->cl_namenode, &nn->conf_name_tree);
|
rb_erase(&clp->cl_namenode, &nn->conf_name_tree);
|
||||||
else
|
else
|
||||||
rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
|
rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
unhash_client_locked(clp);
|
unhash_client_locked(clp);
|
||||||
if (atomic_read(&clp->cl_refcount) == 0)
|
if (atomic_read(&clp->cl_refcount) == 0)
|
||||||
free_client(clp);
|
free_client(clp);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expire_client(struct nfs4_client *clp)
|
static void expire_client(struct nfs4_client *clp)
|
||||||
|
@ -1274,6 +1281,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
|
||||||
struct sockaddr *sa = svc_addr(rqstp);
|
struct sockaddr *sa = svc_addr(rqstp);
|
||||||
int ret;
|
int ret;
|
||||||
struct net *net = SVC_NET(rqstp);
|
struct net *net = SVC_NET(rqstp);
|
||||||
|
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||||
|
|
||||||
clp = alloc_client(name);
|
clp = alloc_client(name);
|
||||||
if (clp == NULL)
|
if (clp == NULL)
|
||||||
|
@ -1282,9 +1290,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
|
||||||
INIT_LIST_HEAD(&clp->cl_sessions);
|
INIT_LIST_HEAD(&clp->cl_sessions);
|
||||||
ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
|
ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
free_client(clp);
|
free_client(clp);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
idr_init(&clp->cl_stateids);
|
idr_init(&clp->cl_stateids);
|
||||||
|
@ -1873,11 +1881,12 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir)
|
||||||
__be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc)
|
__be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc)
|
||||||
{
|
{
|
||||||
struct nfsd4_session *session = cstate->session;
|
struct nfsd4_session *session = cstate->session;
|
||||||
|
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||||
|
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
session->se_cb_prog = bc->bc_cb_program;
|
session->se_cb_prog = bc->bc_cb_program;
|
||||||
session->se_cb_sec = bc->bc_cb_sec;
|
session->se_cb_sec = bc->bc_cb_sec;
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
|
|
||||||
nfsd4_probe_callback(session->se_client);
|
nfsd4_probe_callback(session->se_client);
|
||||||
|
|
||||||
|
@ -1890,10 +1899,11 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
|
||||||
{
|
{
|
||||||
__be32 status;
|
__be32 status;
|
||||||
struct nfsd4_conn *conn;
|
struct nfsd4_conn *conn;
|
||||||
|
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||||
|
|
||||||
if (!nfsd4_last_compound_op(rqstp))
|
if (!nfsd4_last_compound_op(rqstp))
|
||||||
return nfserr_not_only_op;
|
return nfserr_not_only_op;
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp));
|
cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp));
|
||||||
/* Sorta weird: we only need the refcnt'ing because new_conn acquires
|
/* Sorta weird: we only need the refcnt'ing because new_conn acquires
|
||||||
* client_lock iself: */
|
* client_lock iself: */
|
||||||
|
@ -1901,7 +1911,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
|
||||||
nfsd4_get_session(cstate->session);
|
nfsd4_get_session(cstate->session);
|
||||||
atomic_inc(&cstate->session->se_client->cl_refcount);
|
atomic_inc(&cstate->session->se_client->cl_refcount);
|
||||||
}
|
}
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
if (!cstate->session)
|
if (!cstate->session)
|
||||||
return nfserr_badsession;
|
return nfserr_badsession;
|
||||||
|
|
||||||
|
@ -1929,6 +1939,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
|
||||||
{
|
{
|
||||||
struct nfsd4_session *ses;
|
struct nfsd4_session *ses;
|
||||||
__be32 status = nfserr_badsession;
|
__be32 status = nfserr_badsession;
|
||||||
|
struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id);
|
||||||
|
|
||||||
/* Notes:
|
/* Notes:
|
||||||
* - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
|
* - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
|
||||||
|
@ -1942,24 +1953,24 @@ nfsd4_destroy_session(struct svc_rqst *r,
|
||||||
return nfserr_not_only_op;
|
return nfserr_not_only_op;
|
||||||
}
|
}
|
||||||
dump_sessionid(__func__, &sessionid->sessionid);
|
dump_sessionid(__func__, &sessionid->sessionid);
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r));
|
ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r));
|
||||||
if (!ses) {
|
if (!ses) {
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
unhash_session(ses);
|
unhash_session(ses);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
|
|
||||||
nfs4_lock_state();
|
nfs4_lock_state();
|
||||||
nfsd4_probe_callback_sync(ses->se_client);
|
nfsd4_probe_callback_sync(ses->se_client);
|
||||||
nfs4_unlock_state();
|
nfs4_unlock_state();
|
||||||
|
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
nfsd4_del_conns(ses);
|
nfsd4_del_conns(ses);
|
||||||
nfsd4_put_session_locked(ses);
|
nfsd4_put_session_locked(ses);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
status = nfs_ok;
|
status = nfs_ok;
|
||||||
out:
|
out:
|
||||||
dprintk("%s returns %d\n", __func__, ntohl(status));
|
dprintk("%s returns %d\n", __func__, ntohl(status));
|
||||||
|
@ -2025,6 +2036,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
|
||||||
struct nfsd4_slot *slot;
|
struct nfsd4_slot *slot;
|
||||||
struct nfsd4_conn *conn;
|
struct nfsd4_conn *conn;
|
||||||
__be32 status;
|
__be32 status;
|
||||||
|
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||||
|
|
||||||
if (resp->opcnt != 1)
|
if (resp->opcnt != 1)
|
||||||
return nfserr_sequence_pos;
|
return nfserr_sequence_pos;
|
||||||
|
@ -2037,7 +2049,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return nfserr_jukebox;
|
return nfserr_jukebox;
|
||||||
|
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
status = nfserr_badsession;
|
status = nfserr_badsession;
|
||||||
session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp));
|
session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp));
|
||||||
if (!session)
|
if (!session)
|
||||||
|
@ -2113,7 +2125,7 @@ out:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kfree(conn);
|
kfree(conn);
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
dprintk("%s: return %d\n", __func__, ntohl(status));
|
dprintk("%s: return %d\n", __func__, ntohl(status));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -3191,7 +3203,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
dprintk("NFSD: laundromat service - starting\n");
|
dprintk("NFSD: laundromat service - starting\n");
|
||||||
nfsd4_end_grace(nn);
|
nfsd4_end_grace(nn);
|
||||||
INIT_LIST_HEAD(&reaplist);
|
INIT_LIST_HEAD(&reaplist);
|
||||||
spin_lock(&client_lock);
|
spin_lock(&nn->client_lock);
|
||||||
list_for_each_safe(pos, next, &nn->client_lru) {
|
list_for_each_safe(pos, next, &nn->client_lru) {
|
||||||
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
||||||
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
|
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
|
||||||
|
@ -3208,7 +3220,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||||
unhash_client_locked(clp);
|
unhash_client_locked(clp);
|
||||||
list_add(&clp->cl_lru, &reaplist);
|
list_add(&clp->cl_lru, &reaplist);
|
||||||
}
|
}
|
||||||
spin_unlock(&client_lock);
|
spin_unlock(&nn->client_lock);
|
||||||
list_for_each_safe(pos, next, &reaplist) {
|
list_for_each_safe(pos, next, &reaplist) {
|
||||||
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
||||||
dprintk("NFSD: purging unused client (clientid %08x)\n",
|
dprintk("NFSD: purging unused client (clientid %08x)\n",
|
||||||
|
@ -4796,6 +4808,7 @@ static int nfs4_state_start_net(struct net *net)
|
||||||
nn->unconf_name_tree = RB_ROOT;
|
nn->unconf_name_tree = RB_ROOT;
|
||||||
INIT_LIST_HEAD(&nn->client_lru);
|
INIT_LIST_HEAD(&nn->client_lru);
|
||||||
INIT_LIST_HEAD(&nn->close_lru);
|
INIT_LIST_HEAD(&nn->close_lru);
|
||||||
|
spin_lock_init(&nn->client_lock);
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
|
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue