|
@@ -778,16 +778,68 @@ void nfs4_unhash_stid(struct nfs4_stid *s)
|
|
|
s->sc_type = 0;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
+/**
|
|
|
+ * nfs4_get_existing_delegation - Discover if this delegation already exists
|
|
|
+ * @clp: a pointer to the nfs4_client we're granting a delegation to
|
|
|
+ * @fp: a pointer to the nfs4_file we're granting a delegation on
|
|
|
+ *
|
|
|
+ * Return:
|
|
|
+ * On success: NULL if an existing delegation was not found.
|
|
|
+ *
|
|
|
+ * On error: -EAGAIN if one was previously granted to this nfs4_client
|
|
|
+ * for this nfs4_file.
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+static int
|
|
|
+nfs4_get_existing_delegation(struct nfs4_client *clp, struct nfs4_file *fp)
|
|
|
+{
|
|
|
+ struct nfs4_delegation *searchdp = NULL;
|
|
|
+ struct nfs4_client *searchclp = NULL;
|
|
|
+
|
|
|
+ lockdep_assert_held(&state_lock);
|
|
|
+ lockdep_assert_held(&fp->fi_lock);
|
|
|
+
|
|
|
+ list_for_each_entry(searchdp, &fp->fi_delegations, dl_perfile) {
|
|
|
+ searchclp = searchdp->dl_stid.sc_client;
|
|
|
+ if (clp == searchclp) {
|
|
|
+ return -EAGAIN;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * hash_delegation_locked - Add a delegation to the appropriate lists
|
|
|
+ * @dp: a pointer to the nfs4_delegation we are adding.
|
|
|
+ * @fp: a pointer to the nfs4_file we're granting a delegation on
|
|
|
+ *
|
|
|
+ * Return:
|
|
|
+ * On success: NULL if the delegation was successfully hashed.
|
|
|
+ *
|
|
|
+ * On error: -EAGAIN if one was previously granted to this
|
|
|
+ * nfs4_client for this nfs4_file. Delegation is not hashed.
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+static int
|
|
|
hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
|
|
|
{
|
|
|
+ int status;
|
|
|
+ struct nfs4_client *clp = dp->dl_stid.sc_client;
|
|
|
+
|
|
|
lockdep_assert_held(&state_lock);
|
|
|
lockdep_assert_held(&fp->fi_lock);
|
|
|
|
|
|
+ status = nfs4_get_existing_delegation(clp, fp);
|
|
|
+ if (status)
|
|
|
+ return status;
|
|
|
+ ++fp->fi_delegees;
|
|
|
atomic_inc(&dp->dl_stid.sc_count);
|
|
|
dp->dl_stid.sc_type = NFS4_DELEG_STID;
|
|
|
list_add(&dp->dl_perfile, &fp->fi_delegations);
|
|
|
- list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
|
|
|
+ list_add(&dp->dl_perclnt, &clp->cl_delegations);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static bool
|
|
@@ -3969,6 +4021,18 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_file *fp, int flag)
|
|
|
return fl;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * nfs4_setlease - Obtain a delegation by requesting lease from vfs layer
|
|
|
+ * @dp: a pointer to the nfs4_delegation we're adding.
|
|
|
+ *
|
|
|
+ * Return:
|
|
|
+ * On success: Return code will be 0 on success.
|
|
|
+ *
|
|
|
+ * On error: -EAGAIN if there was an existing delegation.
|
|
|
+ * nonzero if there is an error in other cases.
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
static int nfs4_setlease(struct nfs4_delegation *dp)
|
|
|
{
|
|
|
struct nfs4_file *fp = dp->dl_stid.sc_file;
|
|
@@ -4000,16 +4064,19 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
|
|
|
goto out_unlock;
|
|
|
/* Race breaker */
|
|
|
if (fp->fi_deleg_file) {
|
|
|
- status = 0;
|
|
|
- ++fp->fi_delegees;
|
|
|
- hash_delegation_locked(dp, fp);
|
|
|
+ status = hash_delegation_locked(dp, fp);
|
|
|
goto out_unlock;
|
|
|
}
|
|
|
fp->fi_deleg_file = filp;
|
|
|
- fp->fi_delegees = 1;
|
|
|
- hash_delegation_locked(dp, fp);
|
|
|
+ fp->fi_delegees = 0;
|
|
|
+ status = hash_delegation_locked(dp, fp);
|
|
|
spin_unlock(&fp->fi_lock);
|
|
|
spin_unlock(&state_lock);
|
|
|
+ if (status) {
|
|
|
+ /* Should never happen, this is a new fi_deleg_file */
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
+ goto out_fput;
|
|
|
+ }
|
|
|
return 0;
|
|
|
out_unlock:
|
|
|
spin_unlock(&fp->fi_lock);
|
|
@@ -4029,6 +4096,15 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
|
|
|
if (fp->fi_had_conflict)
|
|
|
return ERR_PTR(-EAGAIN);
|
|
|
|
|
|
+ spin_lock(&state_lock);
|
|
|
+ spin_lock(&fp->fi_lock);
|
|
|
+ status = nfs4_get_existing_delegation(clp, fp);
|
|
|
+ spin_unlock(&fp->fi_lock);
|
|
|
+ spin_unlock(&state_lock);
|
|
|
+
|
|
|
+ if (status)
|
|
|
+ return ERR_PTR(status);
|
|
|
+
|
|
|
dp = alloc_init_deleg(clp, fh, odstate);
|
|
|
if (!dp)
|
|
|
return ERR_PTR(-ENOMEM);
|
|
@@ -4047,9 +4123,7 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
|
|
|
status = -EAGAIN;
|
|
|
goto out_unlock;
|
|
|
}
|
|
|
- ++fp->fi_delegees;
|
|
|
- hash_delegation_locked(dp, fp);
|
|
|
- status = 0;
|
|
|
+ status = hash_delegation_locked(dp, fp);
|
|
|
out_unlock:
|
|
|
spin_unlock(&fp->fi_lock);
|
|
|
spin_unlock(&state_lock);
|