+static __be32
+nfsd4_free_delegation_stateid(stateid_t *stateid)
+{
+ struct nfs4_delegation *dp = search_for_delegation(stateid);
+ if (dp)
+ return nfserr_locks_held;
+ return nfserr_bad_stateid;
+}
+
+static __be32
+nfsd4_free_lock_stateid(struct nfs4_stateid *stp)
+{
+ if (check_for_locks(stp->st_file, stp->st_stateowner))
+ return nfserr_locks_held;
+ release_lock_stateid(stp);
+ return nfs_ok;
+}
+
+/*
+ * Test if the stateid is valid
+ */
+__be32
+nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_test_stateid *test_stateid)
+{
+ test_stateid->ts_has_session = nfsd4_has_session(cstate);
+ return nfs_ok;
+}
+
+/*
+ * Free a state id
+ */
+__be32
+nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_free_stateid *free_stateid)
+{
+ stateid_t *stateid = &free_stateid->fr_stateid;
+ struct nfs4_stateid *stp;
+ __be32 ret;
+
+ nfs4_lock_state();
+ if (is_delegation_stateid(stateid)) {
+ ret = nfsd4_free_delegation_stateid(stateid);
+ goto out;
+ }
+
+ stp = search_for_stateid(stateid);
+ if (!stp) {
+ ret = nfserr_bad_stateid;
+ goto out;
+ }
+ if (stateid->si_generation != 0) {
+ if (stateid->si_generation < stp->st_stateid.si_generation) {
+ ret = nfserr_old_stateid;
+ goto out;
+ }
+ if (stateid->si_generation > stp->st_stateid.si_generation) {
+ ret = nfserr_bad_stateid;
+ goto out;
+ }
+ }
+
+ if (is_open_stateid(stp)) {
+ ret = nfserr_locks_held;
+ goto out;
+ } else {
+ ret = nfsd4_free_lock_stateid(stp);
+ goto out;
+ }
+
+out:
+ nfs4_unlock_state();
+ return ret;
+}
+