static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
static void nfs4_set_recdir(char *recdir);
-/* Locking:
- *
- * client_mutex:
- * protects clientid_hashtbl[], clientstr_hashtbl[],
- * unconfstr_hashtbl[], uncofid_hashtbl[].
- */
+/* Locking: */
+
+/* Currently used for almost all code touching nfsv4 state: */
static DEFINE_MUTEX(client_mutex);
+/*
+ * Currently used for the del_recall_lru and file hash table. In an
+ * effort to decrease the scope of the client_mutex, this spinlock may
+ * eventually cover more:
+ */
+static DEFINE_SPINLOCK(recall_lock);
+
static struct kmem_cache *stateowner_slab = NULL;
static struct kmem_cache *file_slab = NULL;
static struct kmem_cache *stateid_slab = NULL;
return x;
}
-/*
- * Delegation state
- */
-
-/* recall_lock protects the del_recall_lru */
-static DEFINE_SPINLOCK(recall_lock);
static struct list_head del_recall_lru;
-static void
-free_nfs4_file(struct kref *kref)
-{
- struct nfs4_file *fp = container_of(kref, struct nfs4_file, fi_ref);
- list_del(&fp->fi_hash);
- iput(fp->fi_inode);
- kmem_cache_free(file_slab, fp);
-}
-
static inline void
put_nfs4_file(struct nfs4_file *fi)
{
- kref_put(&fi->fi_ref, free_nfs4_file);
+ if (atomic_dec_and_lock(&fi->fi_ref, &recall_lock)) {
+ list_del(&fi->fi_hash);
+ spin_unlock(&recall_lock);
+ iput(fi->fi_inode);
+ kmem_cache_free(file_slab, fi);
+ }
}
static inline void
get_nfs4_file(struct nfs4_file *fi)
{
- kref_get(&fi->fi_ref);
+ atomic_inc(&fi->fi_ref);
}
static int num_delegations;
if (conf) {
/* RFC 3530 14.2.33 CASE 0: */
status = nfserr_clid_inuse;
- if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)
- || conf->cl_addr != sin->sin_addr.s_addr) {
- dprintk("NFSD: setclientid: string in use by clientat %pI4\n",
- &conf->cl_addr);
+ if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) {
+ dprintk("NFSD: setclientid: string in use by client"
+ " at %pI4\n", &conf->cl_addr);
goto out;
}
}
fp = kmem_cache_alloc(file_slab, GFP_KERNEL);
if (fp) {
- kref_init(&fp->fi_ref);
+ atomic_set(&fp->fi_ref, 1);
INIT_LIST_HEAD(&fp->fi_hash);
INIT_LIST_HEAD(&fp->fi_stateids);
INIT_LIST_HEAD(&fp->fi_delegations);
+ spin_lock(&recall_lock);
list_add(&fp->fi_hash, &file_hashtbl[hashval]);
+ spin_unlock(&recall_lock);
fp->fi_inode = igrab(ino);
fp->fi_id = current_fileid++;
fp->fi_had_conflict = false;
unsigned int hashval = file_hashval(ino);
struct nfs4_file *fp;
+ spin_lock(&recall_lock);
list_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) {
if (fp->fi_inode == ino) {
get_nfs4_file(fp);
+ spin_unlock(&recall_lock);
return fp;
}
}
+ spin_unlock(&recall_lock);
return NULL;
}
static inline __be32
check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags)
{
- /* Trying to call delegreturn with a special stateid? Yuch: */
- if (!(flags & (RD_STATE | WR_STATE)))
- return nfserr_bad_stateid;
- else if (ONE_STATEID(stateid) && (flags & RD_STATE))
+ if (ONE_STATEID(stateid) && (flags & RD_STATE))
return nfs_ok;
else if (locks_in_grace()) {
/* Answer in remaining cases depends on existance of
* that are not able to provide mandatory locking.
*/
static inline int
-io_during_grace_disallowed(struct inode *inode, int flags)
+grace_disallows_io(struct inode *inode)
{
- return locks_in_grace() && (flags & (RD_STATE | WR_STATE))
- && mandatory_lock(inode);
+ return locks_in_grace() && mandatory_lock(inode);
}
static int check_stateid_generation(stateid_t *in, stateid_t *ref)
return nfs_ok;
}
+static int is_delegation_stateid(stateid_t *stateid)
+{
+ return stateid->si_fileid == 0;
+}
+
/*
* Checks for stateid operations
*/
{
struct nfs4_stateid *stp = NULL;
struct nfs4_delegation *dp = NULL;
- stateid_t *stidp;
struct inode *ino = current_fh->fh_dentry->d_inode;
__be32 status;
- dprintk("NFSD: preprocess_stateid_op: stateid = (%08x/%08x/%08x/%08x)\n",
- stateid->si_boot, stateid->si_stateownerid,
- stateid->si_fileid, stateid->si_generation);
if (filpp)
*filpp = NULL;
- if (io_during_grace_disallowed(ino, flags))
+ if (grace_disallows_io(ino))
return nfserr_grace;
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
goto out;
status = nfserr_bad_stateid;
- if (!stateid->si_fileid) { /* delegation stateid */
+ if (is_delegation_stateid(stateid)) {
dp = find_delegation_stateid(ino, stateid);
- if (!dp) {
- dprintk("NFSD: delegation stateid not found\n");
+ if (!dp)
goto out;
- }
- stidp = &dp->dl_stateid;
- status = check_stateid_generation(stateid, stidp);
+ status = check_stateid_generation(stateid, &dp->dl_stateid);
if (status)
goto out;
status = nfs4_check_delegmode(dp, flags);
if (status)
goto out;
renew_client(dp->dl_client);
- if (flags & DELEG_RET)
- unhash_delegation(dp);
if (filpp)
*filpp = dp->dl_vfs_file;
} else { /* open or lock stateid */
stp = find_stateid(stateid, flags);
- if (!stp) {
- dprintk("NFSD: open or lock stateid not found\n");
+ if (!stp)
goto out;
- }
- if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp))
+ if (nfs4_check_fh(current_fh, stp))
goto out;
if (!stp->st_stateowner->so_confirmed)
goto out;
- stidp = &stp->st_stateid;
- status = check_stateid_generation(stateid, stidp);
+ status = check_stateid_generation(stateid, &stp->st_stateid);
if (status)
goto out;
status = nfs4_check_openmode(stp, flags);
nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_delegreturn *dr)
{
+ struct nfs4_delegation *dp;
+ stateid_t *stateid = &dr->dr_stateid;
+ struct inode *inode;
__be32 status;
if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
- goto out;
+ return status;
+ inode = cstate->current_fh.fh_dentry->d_inode;
nfs4_lock_state();
- status = nfs4_preprocess_stateid_op(&cstate->current_fh,
- &dr->dr_stateid, DELEG_RET, NULL);
- nfs4_unlock_state();
+ status = nfserr_bad_stateid;
+ if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
+ goto out;
+ status = nfserr_stale_stateid;
+ if (STALE_STATEID(stateid))
+ goto out;
+ status = nfserr_bad_stateid;
+ if (!is_delegation_stateid(stateid))
+ goto out;
+ dp = find_delegation_stateid(inode, stateid);
+ if (!dp)
+ goto out;
+ status = check_stateid_generation(stateid, &dp->dl_stateid);
+ if (status)
+ goto out;
+ renew_client(dp->dl_client);
+
+ unhash_delegation(dp);
out:
+ nfs4_unlock_state();
+
return status;
}