]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
lockd: Use xdrgen XDR functions for the NLMv4 UNLOCK procedure
authorChuck Lever <chuck.lever@oracle.com>
Tue, 17 Feb 2026 22:06:58 +0000 (17:06 -0500)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 30 Mar 2026 01:25:09 +0000 (21:25 -0400)
UNLOCK releases locks acquired via the LOCK procedure. Conversion
of TEST, LOCK, CANCEL, and UNLOCK provides the complete set of
lock lifecycle operations required by the NLM protocol, enabling
clients to test for conflicts, acquire locks, abort pending lock
requests, and release held locks.

The procedure handler converts arguments from the xdrgen-generated
nlm4_unlockargs structure to the legacy nlm_lock representation
through nlm4_unlockargs_wrapper. This maintains compatibility with
core lockd logic while using XDR decoders and encoders generated
from the NLMv4 protocol specification.

The original __nlm4svc_proc_unlock function is retained because
the asynchronous callback path invokes it directly, bypassing
the RPC dispatch mechanism.

The pc_argzero field is zero because nlm4_svc_decode_nlm4_unlockargs
initializes all fields in argp->xdrgen, eliminating the need for
early memset of the argument buffer. Remaining argp fields outside
the xdrgen structure are cleared explicitly where needed.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/lockd/svc4proc.c

index 4a3815599a65097a5a83ffdeeaa3878a2b60f8b9..de1a9cf416ec63ce0d9bf6214763ef715053c967 100644 (file)
@@ -55,6 +55,13 @@ struct nlm4_cancargs_wrapper {
 
 static_assert(offsetof(struct nlm4_cancargs_wrapper, xdrgen) == 0);
 
+struct nlm4_unlockargs_wrapper {
+       struct nlm4_unlockargs          xdrgen;
+       struct nlm_lock                 lock;
+};
+
+static_assert(offsetof(struct nlm4_unlockargs_wrapper, xdrgen) == 0);
+
 struct nlm4_testres_wrapper {
        struct nlm4_testres             xdrgen;
        struct nlm_lock                 lock;
@@ -601,10 +608,66 @@ __nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp)
        return rpc_success;
 }
 
+/**
+ * nlm4svc_proc_unlock - UNLOCK: Remove a lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ *   %rpc_success:             RPC executed successfully.
+ *   %rpc_drop_reply:          Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ *   nlm4_res NLMPROC4_UNLOCK(nlm4_unlockargs) = 4;
+ *
+ * Permissible procedure status codes:
+ *   %NLM4_GRANTED:            The requested lock was released.
+ *   %NLM4_DENIED_GRACE_PERIOD:        The server has recently restarted and is
+ *                             re-establishing existing locks, and is not
+ *                             yet ready to accept normal service requests.
+ *
+ * The Linux NLM server implementation also returns:
+ *   %NLM4_DENIED_NOLOCKS:     A needed resource could not be allocated.
+ *   %NLM4_STALE_FH:           The request specified an invalid file handle.
+ *   %NLM4_FBIG:               The request specified a length or offset
+ *                             that exceeds the range supported by the
+ *                             server.
+ *   %NLM4_FAILED:             The request failed for an unspecified reason.
+ */
 static __be32
 nlm4svc_proc_unlock(struct svc_rqst *rqstp)
 {
-       return __nlm4svc_proc_unlock(rqstp, rqstp->rq_resp);
+       struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
+       struct nlm4_res_wrapper *resp = rqstp->rq_resp;
+       struct net *net = SVC_NET(rqstp);
+       struct nlm_host *host = NULL;
+       struct nlm_file *file = NULL;
+
+       resp->xdrgen.cookie = argp->xdrgen.cookie;
+
+       resp->xdrgen.stat.stat = nlm_lck_denied_grace_period;
+       if (locks_in_grace(net))
+               goto out;
+
+       resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
+       host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
+       if (!host)
+               goto out;
+
+       resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
+                                                    &file, &argp->xdrgen.alock,
+                                                    F_UNLCK);
+       if (resp->xdrgen.stat.stat)
+               goto out;
+
+       resp->xdrgen.stat.stat = nlmsvc_unlock(net, file, &argp->lock);
+       nlmsvc_release_lockowner(&argp->lock);
+
+out:
+       if (file)
+               nlm_release_file(file);
+       nlmsvc_release_host(host);
+       return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
+               rpc_drop_reply : rpc_success;
 }
 
 /*
@@ -914,15 +977,15 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
                .pc_xdrressize  = NLM4_nlm4_res_sz,
                .pc_name        = "CANCEL",
        },
-       [NLMPROC_UNLOCK] = {
-               .pc_func = nlm4svc_proc_unlock,
-               .pc_decode = nlm4svc_decode_unlockargs,
-               .pc_encode = nlm4svc_encode_res,
-               .pc_argsize = sizeof(struct nlm_args),
-               .pc_argzero = sizeof(struct nlm_args),
-               .pc_ressize = sizeof(struct nlm_res),
-               .pc_xdrressize = Ck+St,
-               .pc_name = "UNLOCK",
+       [NLMPROC4_UNLOCK] = {
+               .pc_func        = nlm4svc_proc_unlock,
+               .pc_decode      = nlm4_svc_decode_nlm4_unlockargs,
+               .pc_encode      = nlm4_svc_encode_nlm4_res,
+               .pc_argsize     = sizeof(struct nlm4_unlockargs_wrapper),
+               .pc_argzero     = 0,
+               .pc_ressize     = sizeof(struct nlm4_res_wrapper),
+               .pc_xdrressize  = NLM4_nlm4_res_sz,
+               .pc_name        = "UNLOCK",
        },
        [NLMPROC_GRANTED] = {
                .pc_func = nlm4svc_proc_granted,
@@ -1123,6 +1186,7 @@ union nlm4svc_xdrstore {
        struct nlm4_testargs_wrapper    testargs;
        struct nlm4_lockargs_wrapper    lockargs;
        struct nlm4_cancargs_wrapper    cancargs;
+       struct nlm4_unlockargs_wrapper  unlockargs;
        struct nlm4_testres_wrapper     testres;
        struct nlm4_res_wrapper         res;
        struct nlm_args                 args;