__nfs4_file_put_access(fp, oflag);
}
-static inline int get_new_stid(struct nfs4_stid *stid)
-{
- static int min_stateid = 0;
- struct idr *stateids = &stid->sc_client->cl_stateids;
- int new_stid;
- int error;
-
- error = idr_get_new_above(stateids, stid, min_stateid, &new_stid);
- /*
- * Note: the necessary preallocation was done in
- * nfs4_alloc_stateid(). The idr code caps the number of
- * preallocations that can exist at a time, but the state lock
- * prevents anyone from using ours before we get here:
- */
- WARN_ON_ONCE(error);
- /*
- * It shouldn't be a problem to reuse an opaque stateid value.
- * I don't think it is for 4.1. But with 4.0 I worry that, for
- * example, a stray write retransmission could be accepted by
- * the server when it should have been rejected. Therefore,
- * adopt a trick from the sctp code to attempt to maximize the
- * amount of time until an id is reused, by ensuring they always
- * "increase" (mod INT_MAX):
- */
-
- min_stateid = new_stid+1;
- if (min_stateid == INT_MAX)
- min_stateid = 0;
- return new_stid;
-}
-
static struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct
kmem_cache *slab)
{
if (!stid)
return NULL;
- if (!idr_pre_get(stateids, GFP_KERNEL))
- goto out_free;
- if (idr_get_new_above(stateids, stid, min_stateid, &new_id))
+ new_id = idr_alloc(stateids, stid, min_stateid, 0, GFP_KERNEL);
+ if (new_id < 0)
goto out_free;
stid->sc_client = cl;
stid->sc_type = 0;
list_del(&c->cn_persession);
free_conn(c);
}
- spin_unlock(&clp->cl_lock);
nfsd4_probe_callback(clp);
+ spin_unlock(&clp->cl_lock);
}
static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp, u32 flags)
__free_session(ses);
}
-void nfsd4_put_session(struct nfsd4_session *ses)
+static void nfsd4_put_session(struct nfsd4_session *ses)
{
struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id);
struct nfs4_client *clp = session->se_client;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ nfsd4_put_session(session);
if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock))
return;
- if (is_client_expired(clp)) {
+ /*
+ * At this point we also know all sessions have refcnt 1,
+ * so free_client will delete them all if necessary:
+ */
+ if (is_client_expired(clp))
free_client(clp);
- session->se_client = NULL;
- } else
+ else
renew_client_locked(clp);
spin_unlock(&nn->client_lock);
}
}
static struct nfs4_client *
-find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn)
+find_client_in_id_table(struct list_head *tbl, clientid_t *clid, bool sessions)
{
struct nfs4_client *clp;
unsigned int idhashval = clientid_hashval(clid->cl_id);
- list_for_each_entry(clp, &nn->conf_id_hashtbl[idhashval], cl_idhash) {
+ list_for_each_entry(clp, &tbl[idhashval], cl_idhash) {
if (same_clid(&clp->cl_clientid, clid)) {
if ((bool)clp->cl_minorversion != sessions)
return NULL;
return NULL;
}
+static struct nfs4_client *
+find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn)
+{
+ struct list_head *tbl = nn->conf_id_hashtbl;
+
+ return find_client_in_id_table(tbl, clid, sessions);
+}
+
static struct nfs4_client *
find_unconfirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn)
{
- struct nfs4_client *clp;
- unsigned int idhashval = clientid_hashval(clid->cl_id);
+ struct list_head *tbl = nn->unconf_id_hashtbl;
- list_for_each_entry(clp, &nn->unconf_id_hashtbl[idhashval], cl_idhash) {
- if (same_clid(&clp->cl_clientid, clid)) {
- if ((bool)clp->cl_minorversion != sessions)
- return NULL;
- return clp;
- }
- }
- return NULL;
+ return find_client_in_id_table(tbl, clid, sessions);
}
static bool clp_used_exchangeid(struct nfs4_client *clp)
nfs4_lock_state();
unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn);
conf = find_confirmed_client(&cr_ses->clientid, true, nn);
+ WARN_ON_ONCE(conf && unconf);
if (conf) {
cs_slot = &conf->cl_cs_slot;
/* cache solo and embedded create sessions under the state lock */
nfsd4_cache_create_session(cr_ses, cs_slot, status);
nfs4_unlock_state();
-out:
- dprintk("%s returns %d\n", __func__, ntohl(status));
return status;
out_free_conn:
nfs4_unlock_state();
free_conn(conn);
out_free_session:
__free_session(new);
- goto out;
+ return status;
}
static __be32 nfsd4_map_bcts_dir(u32 *dir)
struct nfsd4_destroy_session *sessionid)
{
struct nfsd4_session *ses;
- __be32 status = nfserr_badsession;
+ __be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id);
- /* Notes:
- * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
- * - Should we return nfserr_back_chan_busy if waiting for
- * callbacks on to-be-destroyed session?
- * - Do we need to clear any callback info from previous session?
- */
-
+ nfs4_lock_state();
+ status = nfserr_not_only_op;
if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) {
if (!nfsd4_last_compound_op(r))
- return nfserr_not_only_op;
+ goto out;
}
dump_sessionid(__func__, &sessionid->sessionid);
spin_lock(&nn->client_lock);
ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r));
- if (!ses) {
- spin_unlock(&nn->client_lock);
- goto out;
- }
+ status = nfserr_badsession;
+ if (!ses)
+ goto out_client_lock;
unhash_session(ses);
spin_unlock(&nn->client_lock);
- nfs4_lock_state();
nfsd4_probe_callback_sync(ses->se_client);
- nfs4_unlock_state();
spin_lock(&nn->client_lock);
nfsd4_del_conns(ses);
nfsd4_put_session_locked(ses);
- spin_unlock(&nn->client_lock);
status = nfs_ok;
+out_client_lock:
+ spin_unlock(&nn->client_lock);
out:
- dprintk("%s returns %d\n", __func__, ntohl(status));
+ nfs4_unlock_state();
return status;
}
}
kfree(conn);
spin_unlock(&nn->client_lock);
- dprintk("%s: return %d\n", __func__, ntohl(status));
return status;
}
nfs4_lock_state();
unconf = find_unconfirmed_client(&dc->clientid, true, nn);
conf = find_confirmed_client(&dc->clientid, true, nn);
+ WARN_ON_ONCE(conf && unconf);
if (conf) {
clp = conf;
- if (!is_client_expired(conf) && client_has_state(conf)) {
- status = nfserr_clientid_busy;
- goto out;
- }
-
- /* rfc5661 18.50.3 */
- if (cstate->session && conf == cstate->session->se_client) {
+ if (client_has_state(conf)) {
status = nfserr_clientid_busy;
goto out;
}
expire_client(clp);
out:
nfs4_unlock_state();
- dprintk("%s return %d\n", __func__, ntohl(status));
return status;
}
struct nfs4_ol_stateid *stp;
__be32 ret;
- dprintk("NFSD: nfs4_share_conflict\n");
-
fp = find_file(ino);
if (!fp)
return nfs_ok;
return nfs_ok;
}
-static int
-STALE_STATEID(stateid_t *stateid, struct nfsd_net *nn)
-{
- if (stateid->si_opaque.so_clid.cl_boot == nn->boot_time)
- return 0;
- dprintk("NFSD: stale stateid " STATEID_FMT "!\n",
- STATEID_VAL(stateid));
- return 1;
-}
-
static inline int
access_permit_read(struct nfs4_ol_stateid *stp)
{
struct nfsd_net *nn)
{
struct nfs4_client *cl;
+ __be32 status;
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return nfserr_bad_stateid;
- if (STALE_STATEID(stateid, nn))
+ status = lookup_clientid(&stateid->si_opaque.so_clid, sessions,
+ nn, &cl);
+ if (status == nfserr_stale_clientid)
return nfserr_stale_stateid;
- cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions, nn);
- if (!cl)
- return nfserr_expired;
+ if (status)
+ return status;
*s = find_stateid_by_type(cl, stateid, typemask);
if (!*s)
return nfserr_bad_stateid;
return nfs_ok;
-
}
/*
nfsd4_close_open_stateid(stp);
release_last_closed_stateid(oo);
+ oo->oo_flags &= ~NFS4_OO_PURGE_CLOSE;
oo->oo_last_closed_stid = stp;
if (list_empty(&oo->oo_owner.so_stateids)) {