|
@@ -758,7 +758,7 @@ static void nfsd4_put_drc_mem(int slotsize, int num)
|
|
|
spin_unlock(&nfsd_drc_lock);
|
|
|
}
|
|
|
|
|
|
-static struct nfsd4_session *alloc_session(int slotsize, int numslots)
|
|
|
+static struct nfsd4_session *__alloc_session(int slotsize, int numslots)
|
|
|
{
|
|
|
struct nfsd4_session *new;
|
|
|
int mem, i;
|
|
@@ -852,35 +852,28 @@ static int nfsd4_register_conn(struct nfsd4_conn *conn)
|
|
|
return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
|
|
|
}
|
|
|
|
|
|
-static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, u32 dir)
|
|
|
+static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses)
|
|
|
{
|
|
|
- struct nfsd4_conn *conn;
|
|
|
int ret;
|
|
|
|
|
|
- conn = alloc_conn(rqstp, dir);
|
|
|
- if (!conn)
|
|
|
- return nfserr_jukebox;
|
|
|
nfsd4_hash_conn(conn, ses);
|
|
|
ret = nfsd4_register_conn(conn);
|
|
|
if (ret)
|
|
|
/* oops; xprt is already down: */
|
|
|
nfsd4_conn_lost(&conn->cn_xpt_user);
|
|
|
- if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN &&
|
|
|
- dir & NFS4_CDFC4_BACK) {
|
|
|
+ if (conn->cn_flags & NFS4_CDFC4_BACK) {
|
|
|
/* callback channel may be back up */
|
|
|
nfsd4_probe_callback(ses->se_client);
|
|
|
}
|
|
|
- return nfs_ok;
|
|
|
}
|
|
|
|
|
|
-static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses)
|
|
|
+static struct nfsd4_conn *alloc_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_create_session *cses)
|
|
|
{
|
|
|
u32 dir = NFS4_CDFC4_FORE;
|
|
|
|
|
|
- if (ses->se_flags & SESSION4_BACK_CHAN)
|
|
|
+ if (cses->flags & SESSION4_BACK_CHAN)
|
|
|
dir |= NFS4_CDFC4_BACK;
|
|
|
-
|
|
|
- return nfsd4_new_conn(rqstp, ses, dir);
|
|
|
+ return alloc_conn(rqstp, dir);
|
|
|
}
|
|
|
|
|
|
/* must be called under client_lock */
|
|
@@ -903,20 +896,21 @@ static void nfsd4_del_conns(struct nfsd4_session *s)
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
}
|
|
|
|
|
|
+static void __free_session(struct nfsd4_session *ses)
|
|
|
+{
|
|
|
+ nfsd4_put_drc_mem(slot_bytes(&ses->se_fchannel), ses->se_fchannel.maxreqs);
|
|
|
+ free_session_slots(ses);
|
|
|
+ kfree(ses);
|
|
|
+}
|
|
|
+
|
|
|
static void free_session(struct kref *kref)
|
|
|
{
|
|
|
struct nfsd4_session *ses;
|
|
|
- int mem;
|
|
|
|
|
|
lockdep_assert_held(&client_lock);
|
|
|
ses = container_of(kref, struct nfsd4_session, se_ref);
|
|
|
nfsd4_del_conns(ses);
|
|
|
- spin_lock(&nfsd_drc_lock);
|
|
|
- mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
|
|
|
- nfsd_drc_mem_used -= mem;
|
|
|
- spin_unlock(&nfsd_drc_lock);
|
|
|
- free_session_slots(ses);
|
|
|
- kfree(ses);
|
|
|
+ __free_session(ses);
|
|
|
}
|
|
|
|
|
|
void nfsd4_put_session(struct nfsd4_session *ses)
|
|
@@ -926,14 +920,10 @@ void nfsd4_put_session(struct nfsd4_session *ses)
|
|
|
spin_unlock(&client_lock);
|
|
|
}
|
|
|
|
|
|
-static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
|
|
|
+static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan)
|
|
|
{
|
|
|
struct nfsd4_session *new;
|
|
|
- struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
|
|
|
int numslots, slotsize;
|
|
|
- __be32 status;
|
|
|
- int idx;
|
|
|
-
|
|
|
/*
|
|
|
* Note decreasing slot size below client's request may
|
|
|
* make it difficult for client to function correctly, whereas
|
|
@@ -946,12 +936,18 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
|
|
|
if (numslots < 1)
|
|
|
return NULL;
|
|
|
|
|
|
- new = alloc_session(slotsize, numslots);
|
|
|
+ new = __alloc_session(slotsize, numslots);
|
|
|
if (!new) {
|
|
|
nfsd4_put_drc_mem(slotsize, fchan->maxreqs);
|
|
|
return NULL;
|
|
|
}
|
|
|
init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize);
|
|
|
+ return new;
|
|
|
+}
|
|
|
+
|
|
|
+static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses)
|
|
|
+{
|
|
|
+ int idx;
|
|
|
|
|
|
new->se_client = clp;
|
|
|
gen_sessionid(new);
|
|
@@ -970,14 +966,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
spin_unlock(&client_lock);
|
|
|
|
|
|
- status = nfsd4_new_conn_from_crses(rqstp, new);
|
|
|
- /* whoops: benny points out, status is ignored! (err, or bogus) */
|
|
|
- if (status) {
|
|
|
- spin_lock(&client_lock);
|
|
|
- free_session(&new->se_ref);
|
|
|
- spin_unlock(&client_lock);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
if (cses->flags & SESSION4_BACK_CHAN) {
|
|
|
struct sockaddr *sa = svc_addr(rqstp);
|
|
|
/*
|
|
@@ -990,7 +978,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
|
|
|
rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
|
|
|
clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
|
|
|
}
|
|
|
- nfsd4_probe_callback(clp);
|
|
|
return new;
|
|
|
}
|
|
|
|
|
@@ -1131,7 +1118,7 @@ unhash_client_locked(struct nfs4_client *clp)
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-expire_client(struct nfs4_client *clp)
|
|
|
+destroy_client(struct nfs4_client *clp)
|
|
|
{
|
|
|
struct nfs4_openowner *oo;
|
|
|
struct nfs4_delegation *dp;
|
|
@@ -1165,6 +1152,12 @@ expire_client(struct nfs4_client *clp)
|
|
|
spin_unlock(&client_lock);
|
|
|
}
|
|
|
|
|
|
+static void expire_client(struct nfs4_client *clp)
|
|
|
+{
|
|
|
+ nfsd4_client_record_remove(clp);
|
|
|
+ destroy_client(clp);
|
|
|
+}
|
|
|
+
|
|
|
static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)
|
|
|
{
|
|
|
memcpy(target->cl_verifier.data, source->data,
|
|
@@ -1223,10 +1216,26 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * RFC 3530 language requires clid_inuse be returned when the
|
|
|
+ * "principal" associated with a requests differs from that previously
|
|
|
+ * used. We use uid, gid's, and gss principal string as our best
|
|
|
+ * approximation. We also don't want to allow non-gss use of a client
|
|
|
+ * established using gss: in theory cr_principal should catch that
|
|
|
+ * change, but in practice cr_principal can be null even in the gss case
|
|
|
+ * since gssd doesn't always pass down a principal string.
|
|
|
+ */
|
|
|
+static bool is_gss_cred(struct svc_cred *cr)
|
|
|
+{
|
|
|
+ /* Is cr_flavor one of the gss "pseudoflavors"?: */
|
|
|
+ return (cr->cr_flavor > RPC_AUTH_MAXFLAVOR);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static bool
|
|
|
same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
|
|
|
{
|
|
|
- if ((cr1->cr_flavor != cr2->cr_flavor)
|
|
|
+ if ((is_gss_cred(cr1) != is_gss_cred(cr2))
|
|
|
|| (cr1->cr_uid != cr2->cr_uid)
|
|
|
|| (cr1->cr_gid != cr2->cr_gid)
|
|
|
|| !groups_equal(cr1->cr_group_info, cr2->cr_group_info))
|
|
@@ -1340,13 +1349,15 @@ move_to_confirmed(struct nfs4_client *clp)
|
|
|
}
|
|
|
|
|
|
static struct nfs4_client *
|
|
|
-find_confirmed_client(clientid_t *clid)
|
|
|
+find_confirmed_client(clientid_t *clid, bool sessions)
|
|
|
{
|
|
|
struct nfs4_client *clp;
|
|
|
unsigned int idhashval = clientid_hashval(clid->cl_id);
|
|
|
|
|
|
list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
|
|
|
if (same_clid(&clp->cl_clientid, clid)) {
|
|
|
+ if ((bool)clp->cl_minorversion != sessions)
|
|
|
+ return NULL;
|
|
|
renew_client(clp);
|
|
|
return clp;
|
|
|
}
|
|
@@ -1355,14 +1366,17 @@ find_confirmed_client(clientid_t *clid)
|
|
|
}
|
|
|
|
|
|
static struct nfs4_client *
|
|
|
-find_unconfirmed_client(clientid_t *clid)
|
|
|
+find_unconfirmed_client(clientid_t *clid, bool sessions)
|
|
|
{
|
|
|
struct nfs4_client *clp;
|
|
|
unsigned int idhashval = clientid_hashval(clid->cl_id);
|
|
|
|
|
|
list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) {
|
|
|
- if (same_clid(&clp->cl_clientid, clid))
|
|
|
+ if (same_clid(&clp->cl_clientid, clid)) {
|
|
|
+ if ((bool)clp->cl_minorversion != sessions)
|
|
|
+ return NULL;
|
|
|
return clp;
|
|
|
+ }
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
@@ -1651,6 +1665,7 @@ out_new:
|
|
|
status = nfserr_jukebox;
|
|
|
goto out;
|
|
|
}
|
|
|
+ new->cl_minorversion = 1;
|
|
|
|
|
|
gen_clid(new);
|
|
|
add_to_unconfirmed(new, strhashval);
|
|
@@ -1743,67 +1758,71 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|
|
struct sockaddr *sa = svc_addr(rqstp);
|
|
|
struct nfs4_client *conf, *unconf;
|
|
|
struct nfsd4_session *new;
|
|
|
+ struct nfsd4_conn *conn;
|
|
|
struct nfsd4_clid_slot *cs_slot = NULL;
|
|
|
- bool confirm_me = false;
|
|
|
__be32 status = 0;
|
|
|
|
|
|
if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
|
|
|
return nfserr_inval;
|
|
|
+ if (check_forechannel_attrs(cr_ses->fore_channel))
|
|
|
+ return nfserr_toosmall;
|
|
|
+ new = alloc_session(&cr_ses->fore_channel);
|
|
|
+ if (!new)
|
|
|
+ return nfserr_jukebox;
|
|
|
+ status = nfserr_jukebox;
|
|
|
+ conn = alloc_conn_from_crses(rqstp, cr_ses);
|
|
|
+ if (!conn)
|
|
|
+ goto out_free_session;
|
|
|
|
|
|
nfs4_lock_state();
|
|
|
- unconf = find_unconfirmed_client(&cr_ses->clientid);
|
|
|
- conf = find_confirmed_client(&cr_ses->clientid);
|
|
|
+ unconf = find_unconfirmed_client(&cr_ses->clientid, true);
|
|
|
+ conf = find_confirmed_client(&cr_ses->clientid, true);
|
|
|
|
|
|
if (conf) {
|
|
|
cs_slot = &conf->cl_cs_slot;
|
|
|
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
|
|
|
if (status == nfserr_replay_cache) {
|
|
|
status = nfsd4_replay_create_session(cr_ses, cs_slot);
|
|
|
- goto out;
|
|
|
+ goto out_free_conn;
|
|
|
} else if (cr_ses->seqid != cs_slot->sl_seqid + 1) {
|
|
|
status = nfserr_seq_misordered;
|
|
|
- goto out;
|
|
|
+ goto out_free_conn;
|
|
|
}
|
|
|
} else if (unconf) {
|
|
|
+ unsigned int hash;
|
|
|
+ struct nfs4_client *old;
|
|
|
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
|
|
|
!rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
|
|
|
status = nfserr_clid_inuse;
|
|
|
- goto out;
|
|
|
+ goto out_free_conn;
|
|
|
}
|
|
|
cs_slot = &unconf->cl_cs_slot;
|
|
|
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
|
|
|
if (status) {
|
|
|
/* an unconfirmed replay returns misordered */
|
|
|
status = nfserr_seq_misordered;
|
|
|
- goto out;
|
|
|
+ goto out_free_conn;
|
|
|
}
|
|
|
- confirm_me = true;
|
|
|
+ hash = clientstr_hashval(unconf->cl_recdir);
|
|
|
+ old = find_confirmed_client_by_str(unconf->cl_recdir, hash);
|
|
|
+ if (old)
|
|
|
+ expire_client(old);
|
|
|
+ move_to_confirmed(unconf);
|
|
|
conf = unconf;
|
|
|
} else {
|
|
|
status = nfserr_stale_clientid;
|
|
|
- goto out;
|
|
|
+ goto out_free_conn;
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- * XXX: we should probably set this at creation time, and check
|
|
|
- * for consistent minorversion use throughout:
|
|
|
- */
|
|
|
- conf->cl_minorversion = 1;
|
|
|
+ status = nfs_ok;
|
|
|
/*
|
|
|
* We do not support RDMA or persistent sessions
|
|
|
*/
|
|
|
cr_ses->flags &= ~SESSION4_PERSIST;
|
|
|
cr_ses->flags &= ~SESSION4_RDMA;
|
|
|
|
|
|
- status = nfserr_toosmall;
|
|
|
- if (check_forechannel_attrs(cr_ses->fore_channel))
|
|
|
- goto out;
|
|
|
+ init_session(rqstp, new, conf, cr_ses);
|
|
|
+ nfsd4_init_conn(rqstp, conn, new);
|
|
|
|
|
|
- status = nfserr_jukebox;
|
|
|
- new = alloc_init_session(rqstp, conf, cr_ses);
|
|
|
- if (!new)
|
|
|
- goto out;
|
|
|
- status = nfs_ok;
|
|
|
memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
|
|
|
NFS4_MAX_SESSIONID_LEN);
|
|
|
memcpy(&cr_ses->fore_channel, &new->se_fchannel,
|
|
@@ -1813,18 +1832,15 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|
|
|
|
|
/* cache solo and embedded create sessions under the state lock */
|
|
|
nfsd4_cache_create_session(cr_ses, cs_slot, status);
|
|
|
- if (confirm_me) {
|
|
|
- unsigned int hash = clientstr_hashval(unconf->cl_recdir);
|
|
|
- struct nfs4_client *old =
|
|
|
- find_confirmed_client_by_str(conf->cl_recdir, hash);
|
|
|
- if (old)
|
|
|
- expire_client(old);
|
|
|
- move_to_confirmed(conf);
|
|
|
- }
|
|
|
out:
|
|
|
nfs4_unlock_state();
|
|
|
dprintk("%s returns %d\n", __func__, ntohl(status));
|
|
|
return status;
|
|
|
+out_free_conn:
|
|
|
+ free_conn(conn);
|
|
|
+out_free_session:
|
|
|
+ __free_session(new);
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
static bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
|
|
@@ -1854,6 +1870,7 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
|
|
|
struct nfsd4_bind_conn_to_session *bcts)
|
|
|
{
|
|
|
__be32 status;
|
|
|
+ struct nfsd4_conn *conn;
|
|
|
|
|
|
if (!nfsd4_last_compound_op(rqstp))
|
|
|
return nfserr_not_only_op;
|
|
@@ -1870,9 +1887,13 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
|
|
|
return nfserr_badsession;
|
|
|
|
|
|
status = nfsd4_map_bcts_dir(&bcts->dir);
|
|
|
- if (!status)
|
|
|
- nfsd4_new_conn(rqstp, cstate->session, bcts->dir);
|
|
|
- return status;
|
|
|
+ if (status)
|
|
|
+ return status;
|
|
|
+ conn = alloc_conn(rqstp, bcts->dir);
|
|
|
+ if (!conn)
|
|
|
+ return nfserr_jukebox;
|
|
|
+ nfsd4_init_conn(rqstp, conn, cstate->session);
|
|
|
+ return nfs_ok;
|
|
|
}
|
|
|
|
|
|
static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid)
|
|
@@ -2085,8 +2106,8 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
|
|
|
__be32 status = 0;
|
|
|
|
|
|
nfs4_lock_state();
|
|
|
- unconf = find_unconfirmed_client(&dc->clientid);
|
|
|
- conf = find_confirmed_client(&dc->clientid);
|
|
|
+ unconf = find_unconfirmed_client(&dc->clientid, true);
|
|
|
+ conf = find_confirmed_client(&dc->clientid, true);
|
|
|
|
|
|
if (conf) {
|
|
|
clp = conf;
|
|
@@ -2200,10 +2221,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
copy_clid(new, conf);
|
|
|
else /* case 4 (new client) or cases 2, 3 (client reboot): */
|
|
|
gen_clid(new);
|
|
|
- /*
|
|
|
- * XXX: we should probably set this at creation time, and check
|
|
|
- * for consistent minorversion use throughout:
|
|
|
- */
|
|
|
new->cl_minorversion = 0;
|
|
|
gen_callback(new, setclid, rqstp);
|
|
|
add_to_unconfirmed(new, strhashval);
|
|
@@ -2232,8 +2249,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
|
|
|
return nfserr_stale_clientid;
|
|
|
nfs4_lock_state();
|
|
|
|
|
|
- conf = find_confirmed_client(clid);
|
|
|
- unconf = find_unconfirmed_client(clid);
|
|
|
+ conf = find_confirmed_client(clid, false);
|
|
|
+ unconf = find_unconfirmed_client(clid, false);
|
|
|
/*
|
|
|
* We try hard to give out unique clientid's, so if we get an
|
|
|
* attempt to confirm the same clientid with a different cred,
|
|
@@ -2262,10 +2279,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
|
|
|
unsigned int hash = clientstr_hashval(unconf->cl_recdir);
|
|
|
|
|
|
conf = find_confirmed_client_by_str(unconf->cl_recdir, hash);
|
|
|
- if (conf) {
|
|
|
- nfsd4_client_record_remove(conf);
|
|
|
+ if (conf)
|
|
|
expire_client(conf);
|
|
|
- }
|
|
|
move_to_confirmed(unconf);
|
|
|
nfsd4_probe_callback(unconf);
|
|
|
}
|
|
@@ -2447,16 +2462,20 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner,
|
|
|
}
|
|
|
|
|
|
static struct nfs4_openowner *
|
|
|
-find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open)
|
|
|
+find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, bool sessions)
|
|
|
{
|
|
|
struct nfs4_stateowner *so;
|
|
|
struct nfs4_openowner *oo;
|
|
|
+ struct nfs4_client *clp;
|
|
|
|
|
|
list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) {
|
|
|
if (!so->so_is_open_owner)
|
|
|
continue;
|
|
|
if (same_owner_str(so, &open->op_owner, &open->op_clientid)) {
|
|
|
oo = openowner(so);
|
|
|
+ clp = oo->oo_owner.so_client;
|
|
|
+ if ((bool)clp->cl_minorversion != sessions)
|
|
|
+ return NULL;
|
|
|
renew_client(oo->oo_owner.so_client);
|
|
|
return oo;
|
|
|
}
|
|
@@ -2600,10 +2619,10 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
|
|
|
return nfserr_jukebox;
|
|
|
|
|
|
strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner);
|
|
|
- oo = find_openstateowner_str(strhashval, open);
|
|
|
+ oo = find_openstateowner_str(strhashval, open, cstate->minorversion);
|
|
|
open->op_openowner = oo;
|
|
|
if (!oo) {
|
|
|
- clp = find_confirmed_client(clientid);
|
|
|
+ clp = find_confirmed_client(clientid, cstate->minorversion);
|
|
|
if (clp == NULL)
|
|
|
return nfserr_expired;
|
|
|
goto new_owner;
|
|
@@ -2705,11 +2724,6 @@ nfs4_check_open(struct nfs4_file *fp, struct nfsd4_open *open, struct nfs4_ol_st
|
|
|
return nfs_ok;
|
|
|
}
|
|
|
|
|
|
-static void nfs4_free_stateid(struct nfs4_ol_stateid *s)
|
|
|
-{
|
|
|
- kmem_cache_free(stateid_slab, s);
|
|
|
-}
|
|
|
-
|
|
|
static inline int nfs4_access_to_access(u32 nfs4_access)
|
|
|
{
|
|
|
int flags = 0;
|
|
@@ -3087,7 +3101,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
|
|
|
if (open->op_file)
|
|
|
nfsd4_free_file(open->op_file);
|
|
|
if (open->op_stp)
|
|
|
- nfs4_free_stateid(open->op_stp);
|
|
|
+ free_generic_stateid(open->op_stp);
|
|
|
}
|
|
|
|
|
|
__be32
|
|
@@ -3104,7 +3118,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
status = nfserr_stale_clientid;
|
|
|
if (STALE_CLIENTID(clid, nn))
|
|
|
goto out;
|
|
|
- clp = find_confirmed_client(clid);
|
|
|
+ clp = find_confirmed_client(clid, cstate->minorversion);
|
|
|
status = nfserr_expired;
|
|
|
if (clp == NULL) {
|
|
|
/* We assume the client took too long to RENEW. */
|
|
@@ -3180,7 +3194,6 @@ nfs4_laundromat(void)
|
|
|
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
|
|
dprintk("NFSD: purging unused client (clientid %08x)\n",
|
|
|
clp->cl_clientid.cl_id);
|
|
|
- nfsd4_client_record_remove(clp);
|
|
|
expire_client(clp);
|
|
|
}
|
|
|
spin_lock(&recall_lock);
|
|
@@ -3372,7 +3385,7 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
|
|
|
return nfs_ok;
|
|
|
}
|
|
|
|
|
|
-static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s)
|
|
|
+static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions)
|
|
|
{
|
|
|
struct nfs4_client *cl;
|
|
|
struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
|
|
@@ -3381,7 +3394,7 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, s
|
|
|
return nfserr_bad_stateid;
|
|
|
if (STALE_STATEID(stateid, nn))
|
|
|
return nfserr_stale_stateid;
|
|
|
- cl = find_confirmed_client(&stateid->si_opaque.so_clid);
|
|
|
+ cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions);
|
|
|
if (!cl)
|
|
|
return nfserr_expired;
|
|
|
*s = find_stateid_by_type(cl, stateid, typemask);
|
|
@@ -3414,7 +3427,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
|
|
|
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
|
|
|
return check_special_stateids(net, current_fh, stateid, flags);
|
|
|
|
|
|
- status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s);
|
|
|
+ status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion);
|
|
|
if (status)
|
|
|
return status;
|
|
|
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
|
|
@@ -3564,7 +3577,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
|
|
|
seqid, STATEID_VAL(stateid));
|
|
|
|
|
|
*stpp = NULL;
|
|
|
- status = nfsd4_lookup_stateid(stateid, typemask, &s);
|
|
|
+ status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion);
|
|
|
if (status)
|
|
|
return status;
|
|
|
*stpp = openlockstateid(s);
|
|
@@ -3765,6 +3778,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
|
|
|
|
|
|
nfsd4_close_open_stateid(stp);
|
|
|
+ release_last_closed_stateid(oo);
|
|
|
oo->oo_last_closed_stid = stp;
|
|
|
|
|
|
if (list_empty(&oo->oo_owner.so_stateids)) {
|
|
@@ -3801,7 +3815,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
inode = cstate->current_fh.fh_dentry->d_inode;
|
|
|
|
|
|
nfs4_lock_state();
|
|
|
- status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s);
|
|
|
+ status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion);
|
|
|
if (status)
|
|
|
goto out;
|
|
|
dp = delegstateid(s);
|
|
@@ -4045,8 +4059,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
struct nfs4_lockowner *lock_sop = NULL;
|
|
|
struct nfs4_ol_stateid *lock_stp;
|
|
|
struct file *filp = NULL;
|
|
|
- struct file_lock file_lock;
|
|
|
- struct file_lock conflock;
|
|
|
+ struct file_lock *file_lock = NULL;
|
|
|
+ struct file_lock *conflock = NULL;
|
|
|
__be32 status = 0;
|
|
|
bool new_state = false;
|
|
|
int lkflg;
|
|
@@ -4116,21 +4130,28 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim)
|
|
|
goto out;
|
|
|
|
|
|
- locks_init_lock(&file_lock);
|
|
|
+ file_lock = locks_alloc_lock();
|
|
|
+ if (!file_lock) {
|
|
|
+ dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
|
|
|
+ status = nfserr_jukebox;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ locks_init_lock(file_lock);
|
|
|
switch (lock->lk_type) {
|
|
|
case NFS4_READ_LT:
|
|
|
case NFS4_READW_LT:
|
|
|
filp = find_readable_file(lock_stp->st_file);
|
|
|
if (filp)
|
|
|
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
|
|
|
- file_lock.fl_type = F_RDLCK;
|
|
|
+ file_lock->fl_type = F_RDLCK;
|
|
|
break;
|
|
|
case NFS4_WRITE_LT:
|
|
|
case NFS4_WRITEW_LT:
|
|
|
filp = find_writeable_file(lock_stp->st_file);
|
|
|
if (filp)
|
|
|
get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
|
|
|
- file_lock.fl_type = F_WRLCK;
|
|
|
+ file_lock->fl_type = F_WRLCK;
|
|
|
break;
|
|
|
default:
|
|
|
status = nfserr_inval;
|
|
@@ -4140,22 +4161,23 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
status = nfserr_openmode;
|
|
|
goto out;
|
|
|
}
|
|
|
- file_lock.fl_owner = (fl_owner_t)lock_sop;
|
|
|
- file_lock.fl_pid = current->tgid;
|
|
|
- file_lock.fl_file = filp;
|
|
|
- file_lock.fl_flags = FL_POSIX;
|
|
|
- file_lock.fl_lmops = &nfsd_posix_mng_ops;
|
|
|
-
|
|
|
- file_lock.fl_start = lock->lk_offset;
|
|
|
- file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
|
|
|
- nfs4_transform_lock_offset(&file_lock);
|
|
|
-
|
|
|
- /*
|
|
|
- * Try to lock the file in the VFS.
|
|
|
- * Note: locks.c uses the BKL to protect the inode's lock list.
|
|
|
- */
|
|
|
+ file_lock->fl_owner = (fl_owner_t)lock_sop;
|
|
|
+ file_lock->fl_pid = current->tgid;
|
|
|
+ file_lock->fl_file = filp;
|
|
|
+ file_lock->fl_flags = FL_POSIX;
|
|
|
+ file_lock->fl_lmops = &nfsd_posix_mng_ops;
|
|
|
+ file_lock->fl_start = lock->lk_offset;
|
|
|
+ file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
|
|
|
+ nfs4_transform_lock_offset(file_lock);
|
|
|
+
|
|
|
+ conflock = locks_alloc_lock();
|
|
|
+ if (!conflock) {
|
|
|
+ dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
|
|
|
+ status = nfserr_jukebox;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock);
|
|
|
+ err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
|
|
|
switch (-err) {
|
|
|
case 0: /* success! */
|
|
|
update_stateid(&lock_stp->st_stid.sc_stateid);
|
|
@@ -4166,7 +4188,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
case (EAGAIN): /* conflock holds conflicting lock */
|
|
|
status = nfserr_denied;
|
|
|
dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
|
|
|
- nfs4_set_lock_denied(&conflock, &lock->lk_denied);
|
|
|
+ nfs4_set_lock_denied(conflock, &lock->lk_denied);
|
|
|
break;
|
|
|
case (EDEADLK):
|
|
|
status = nfserr_deadlock;
|
|
@@ -4181,6 +4203,10 @@ out:
|
|
|
release_lockowner(lock_sop);
|
|
|
if (!cstate->replay_owner)
|
|
|
nfs4_unlock_state();
|
|
|
+ if (file_lock)
|
|
|
+ locks_free_lock(file_lock);
|
|
|
+ if (conflock)
|
|
|
+ locks_free_lock(conflock);
|
|
|
return status;
|
|
|
}
|
|
|
|
|
@@ -4209,7 +4235,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
struct nfsd4_lockt *lockt)
|
|
|
{
|
|
|
struct inode *inode;
|
|
|
- struct file_lock file_lock;
|
|
|
+ struct file_lock *file_lock = NULL;
|
|
|
struct nfs4_lockowner *lo;
|
|
|
__be32 status;
|
|
|
struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
|
|
@@ -4230,15 +4256,21 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
goto out;
|
|
|
|
|
|
inode = cstate->current_fh.fh_dentry->d_inode;
|
|
|
- locks_init_lock(&file_lock);
|
|
|
+ file_lock = locks_alloc_lock();
|
|
|
+ if (!file_lock) {
|
|
|
+ dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
|
|
|
+ status = nfserr_jukebox;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ locks_init_lock(file_lock);
|
|
|
switch (lockt->lt_type) {
|
|
|
case NFS4_READ_LT:
|
|
|
case NFS4_READW_LT:
|
|
|
- file_lock.fl_type = F_RDLCK;
|
|
|
+ file_lock->fl_type = F_RDLCK;
|
|
|
break;
|
|
|
case NFS4_WRITE_LT:
|
|
|
case NFS4_WRITEW_LT:
|
|
|
- file_lock.fl_type = F_WRLCK;
|
|
|
+ file_lock->fl_type = F_WRLCK;
|
|
|
break;
|
|
|
default:
|
|
|
dprintk("NFSD: nfs4_lockt: bad lock type!\n");
|
|
@@ -4248,25 +4280,27 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
|
|
|
lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner);
|
|
|
if (lo)
|
|
|
- file_lock.fl_owner = (fl_owner_t)lo;
|
|
|
- file_lock.fl_pid = current->tgid;
|
|
|
- file_lock.fl_flags = FL_POSIX;
|
|
|
+ file_lock->fl_owner = (fl_owner_t)lo;
|
|
|
+ file_lock->fl_pid = current->tgid;
|
|
|
+ file_lock->fl_flags = FL_POSIX;
|
|
|
|
|
|
- file_lock.fl_start = lockt->lt_offset;
|
|
|
- file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
|
|
|
+ file_lock->fl_start = lockt->lt_offset;
|
|
|
+ file_lock->fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
|
|
|
|
|
|
- nfs4_transform_lock_offset(&file_lock);
|
|
|
+ nfs4_transform_lock_offset(file_lock);
|
|
|
|
|
|
- status = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock);
|
|
|
+ status = nfsd_test_lock(rqstp, &cstate->current_fh, file_lock);
|
|
|
if (status)
|
|
|
goto out;
|
|
|
|
|
|
- if (file_lock.fl_type != F_UNLCK) {
|
|
|
+ if (file_lock->fl_type != F_UNLCK) {
|
|
|
status = nfserr_denied;
|
|
|
- nfs4_set_lock_denied(&file_lock, &lockt->lt_denied);
|
|
|
+ nfs4_set_lock_denied(file_lock, &lockt->lt_denied);
|
|
|
}
|
|
|
out:
|
|
|
nfs4_unlock_state();
|
|
|
+ if (file_lock)
|
|
|
+ locks_free_lock(file_lock);
|
|
|
return status;
|
|
|
}
|
|
|
|
|
@@ -4276,7 +4310,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
{
|
|
|
struct nfs4_ol_stateid *stp;
|
|
|
struct file *filp = NULL;
|
|
|
- struct file_lock file_lock;
|
|
|
+ struct file_lock *file_lock = NULL;
|
|
|
__be32 status;
|
|
|
int err;
|
|
|
|
|
@@ -4298,23 +4332,29 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
status = nfserr_lock_range;
|
|
|
goto out;
|
|
|
}
|
|
|
- BUG_ON(!filp);
|
|
|
- locks_init_lock(&file_lock);
|
|
|
- file_lock.fl_type = F_UNLCK;
|
|
|
- file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
|
|
|
- file_lock.fl_pid = current->tgid;
|
|
|
- file_lock.fl_file = filp;
|
|
|
- file_lock.fl_flags = FL_POSIX;
|
|
|
- file_lock.fl_lmops = &nfsd_posix_mng_ops;
|
|
|
- file_lock.fl_start = locku->lu_offset;
|
|
|
-
|
|
|
- file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length);
|
|
|
- nfs4_transform_lock_offset(&file_lock);
|
|
|
+ file_lock = locks_alloc_lock();
|
|
|
+ if (!file_lock) {
|
|
|
+ dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
|
|
|
+ status = nfserr_jukebox;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ locks_init_lock(file_lock);
|
|
|
+ file_lock->fl_type = F_UNLCK;
|
|
|
+ file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
|
|
|
+ file_lock->fl_pid = current->tgid;
|
|
|
+ file_lock->fl_file = filp;
|
|
|
+ file_lock->fl_flags = FL_POSIX;
|
|
|
+ file_lock->fl_lmops = &nfsd_posix_mng_ops;
|
|
|
+ file_lock->fl_start = locku->lu_offset;
|
|
|
+
|
|
|
+ file_lock->fl_end = last_byte_offset(locku->lu_offset,
|
|
|
+ locku->lu_length);
|
|
|
+ nfs4_transform_lock_offset(file_lock);
|
|
|
|
|
|
/*
|
|
|
* Try to unlock the file in the VFS.
|
|
|
*/
|
|
|
- err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL);
|
|
|
+ err = vfs_lock_file(filp, F_SETLK, file_lock, NULL);
|
|
|
if (err) {
|
|
|
dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n");
|
|
|
goto out_nfserr;
|
|
@@ -4328,6 +4368,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
out:
|
|
|
if (!cstate->replay_owner)
|
|
|
nfs4_unlock_state();
|
|
|
+ if (file_lock)
|
|
|
+ locks_free_lock(file_lock);
|
|
|
return status;
|
|
|
|
|
|
out_nfserr:
|
|
@@ -4501,12 +4543,12 @@ nfsd4_find_reclaim_client(struct nfs4_client *clp)
|
|
|
* Called from OPEN. Look for clientid in reclaim list.
|
|
|
*/
|
|
|
__be32
|
|
|
-nfs4_check_open_reclaim(clientid_t *clid)
|
|
|
+nfs4_check_open_reclaim(clientid_t *clid, bool sessions)
|
|
|
{
|
|
|
struct nfs4_client *clp;
|
|
|
|
|
|
/* find clientid in conf_id_hashtbl */
|
|
|
- clp = find_confirmed_client(clid);
|
|
|
+ clp = find_confirmed_client(clid, sessions);
|
|
|
if (clp == NULL)
|
|
|
return nfserr_reclaim_bad;
|
|
|
|
|
@@ -4522,7 +4564,6 @@ void nfsd_forget_clients(u64 num)
|
|
|
|
|
|
nfs4_lock_state();
|
|
|
list_for_each_entry_safe(clp, next, &client_lru, cl_lru) {
|
|
|
- nfsd4_client_record_remove(clp);
|
|
|
expire_client(clp);
|
|
|
if (++count == num)
|
|
|
break;
|
|
@@ -4582,7 +4623,7 @@ void nfsd_forget_openowners(u64 num)
|
|
|
printk(KERN_INFO "NFSD: Forgot %d open owners", count);
|
|
|
}
|
|
|
|
|
|
-int nfsd_process_n_delegations(u64 num, struct list_head *list)
|
|
|
+static int nfsd_process_n_delegations(u64 num, struct list_head *list)
|
|
|
{
|
|
|
int i, count = 0;
|
|
|
struct nfs4_file *fp, *fnext;
|
|
@@ -4747,11 +4788,11 @@ __nfs4_state_shutdown(void)
|
|
|
for (i = 0; i < CLIENT_HASH_SIZE; i++) {
|
|
|
while (!list_empty(&conf_id_hashtbl[i])) {
|
|
|
clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash);
|
|
|
- expire_client(clp);
|
|
|
+ destroy_client(clp);
|
|
|
}
|
|
|
while (!list_empty(&unconf_str_hashtbl[i])) {
|
|
|
clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash);
|
|
|
- expire_client(clp);
|
|
|
+ destroy_client(clp);
|
|
|
}
|
|
|
}
|
|
|
INIT_LIST_HEAD(&reaplist);
|