]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
lockd: Use xdrgen XDR functions for the NLMv4 TEST procedure
authorChuck Lever <chuck.lever@oracle.com>
Tue, 17 Feb 2026 22:06:55 +0000 (17:06 -0500)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 30 Mar 2026 01:25:09 +0000 (21:25 -0400)
The NLM TEST procedure requires host and file lookups to check
lock state, operations that will be common across multiple NLM
procedures being migrated to xdrgen. By introducing the helper
functions nlm4svc_lookup_host() and nlm4svc_lookup_file() now,
we establish reusable patterns for subsequent conversions in
this series.

This patch converts the TEST procedure to use xdrgen functions
nlm4_svc_decode_testargs and nlm4_svc_encode_testres generated
from the NLM version 4 protocol specification. The procedure
handler is rewritten to use xdrgen types through wrapper
structures that bridge between generated code and the legacy
nlm_lock representation still used by the core lockd logic.
TEST_MSG is to be converted in a subsequent patch.

The pc_argzero field is set to zero because xdrgen decoders
reliably initialize all arguments in the argp->xdrgen field,
making the early defensive memset unnecessary. Remaining argp
fields are cleared as needed.

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

index 4fcd66beb4dfc7b943e9bf76455a83de8a61ed86..b07ab4d60871c6b059abcbc50c5425fabe73a97f 100644 (file)
 
 #define NLMDBG_FACILITY                NLMDBG_CLIENT
 
+/*
+ * Wrapper structures combine xdrgen types with legacy nlm_lock.
+ * The xdrgen field must be first so the structure can be cast
+ * to its XDR type for the RPC dispatch layer.
+ */
+struct nlm4_testargs_wrapper {
+       struct nlm4_testargs            xdrgen;
+       struct nlm_lock                 lock;
+};
+
+static_assert(offsetof(struct nlm4_testargs_wrapper, xdrgen) == 0);
+
+struct nlm4_testres_wrapper {
+       struct nlm4_testres             xdrgen;
+       struct nlm_lock                 lock;
+};
+
+static_assert(offsetof(struct nlm4_testres_wrapper, xdrgen) == 0);
+
+static struct nlm_host *
+nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
+{
+       struct nlm_host *host;
+
+       if (!nlmsvc_ops)
+               return NULL;
+       host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
+       if (!host)
+               return NULL;
+       if (monitored && nsm_monitor(host) < 0) {
+               nlmsvc_release_host(host);
+               return NULL;
+       }
+       return host;
+}
+
+static __be32
+nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
+                   struct nlm_lock *lock, struct nlm_file **filp,
+                   struct nlm4_lock *xdr_lock, unsigned char type)
+{
+       struct file_lock *fl = &lock->fl;
+       struct nlm_file *file = NULL;
+       __be32 error;
+
+       if (xdr_lock->fh.len > NFS_MAXFHSIZE)
+               return nlm_lck_denied_nolocks;
+       lock->fh.size = xdr_lock->fh.len;
+       memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len);
+
+       lock->oh.len = xdr_lock->oh.len;
+       lock->oh.data = xdr_lock->oh.data;
+
+       lock->svid = xdr_lock->svid;
+       lock->lock_start = xdr_lock->l_offset;
+       lock->lock_len = xdr_lock->l_len;
+
+       if (lock->lock_start > OFFSET_MAX ||
+           (lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start))))
+               return nlm4_fbig;
+
+       locks_init_lock(fl);
+       fl->c.flc_type = type;
+       lockd_set_file_lock_range4(fl, lock->lock_start, lock->lock_len);
+
+       error = nlm_lookup_file(rqstp, &file, lock);
+       switch (error) {
+       case nlm_granted:
+               break;
+       case nlm__int__stale_fh:
+               return nlm4_stale_fh;
+       case nlm__int__failed:
+               return nlm4_failed;
+       default:
+               return error;
+       }
+       *filp = file;
+
+       fl->c.flc_flags = FL_POSIX;
+       fl->c.flc_file = file->f_file[lock_to_openmode(fl)];
+       fl->c.flc_pid = current->tgid;
+       fl->fl_lmops = &nlmsvc_lock_operations;
+       nlmsvc_locks_init_private(fl, host, (pid_t)lock->svid);
+       if (!fl->c.flc_owner)
+               return nlm_lck_denied_nolocks;
+
+       return nlm_granted;
+}
+
 /*
  * Obtain client and file from arguments
  */
@@ -151,10 +240,81 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
        return rc;
 }
 
-static __be32
-nlm4svc_proc_test(struct svc_rqst *rqstp)
+/**
+ * nlm4svc_proc_test - TEST: Check for conflicting lock
+ * @rqstp: RPC transaction context
+ *
+ * Returns:
+ *   %rpc_success:             RPC executed successfully.
+ *   %rpc_drop_reply:          Do not send an RPC reply.
+ *
+ * RPC synopsis:
+ *   nlm4_testres NLMPROC4_TEST(nlm4_testargs) = 1;
+ *
+ * Permissible procedure status codes:
+ *   %NLM4_GRANTED:            The server would be able to grant the
+ *                             requested lock.
+ *   %NLM4_DENIED:             The requested lock conflicted with existing
+ *                             lock reservations for the file.
+ *   %NLM4_DENIED_NOLOCKS:     The server could not allocate the resources
+ *                             needed to process the request.
+ *   %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_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_test(struct svc_rqst *rqstp)
 {
-       return __nlm4svc_proc_test(rqstp, rqstp->rq_resp);
+       struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
+       unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
+       struct nlm4_testres_wrapper *resp = rqstp->rq_resp;
+       struct nlm_file *file = NULL;
+       struct nlm_host *host;
+
+       resp->xdrgen.cookie = argp->xdrgen.cookie;
+
+       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,
+                                                    type);
+       if (resp->xdrgen.stat.stat)
+               goto out;
+
+       resp->xdrgen.stat.stat = nlmsvc_testlock(rqstp, file, host,
+                                                &argp->lock, &resp->lock);
+       nlmsvc_release_lockowner(&argp->lock);
+
+       if (resp->xdrgen.stat.stat == nlm_lck_denied) {
+               struct nlm_lock *conf = &resp->lock;
+               struct nlm4_holder *holder = &resp->xdrgen.stat.u.holder;
+
+               holder->exclusive = (conf->fl.c.flc_type != F_RDLCK);
+               holder->svid = conf->svid;
+               holder->oh.len = conf->oh.len;
+               holder->oh.data = conf->oh.data;
+               holder->l_offset = conf->fl.fl_start;
+               if (conf->fl.fl_end == OFFSET_MAX)
+                       holder->l_len = 0;
+               else
+                       holder->l_len = conf->fl.fl_end - conf->fl.fl_start + 1;
+       }
+
+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;
 }
 
 static __be32
@@ -557,15 +717,15 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
                .pc_xdrressize  = XDR_void,
                .pc_name        = "NULL",
        },
-       [NLMPROC_TEST] = {
-               .pc_func = nlm4svc_proc_test,
-               .pc_decode = nlm4svc_decode_testargs,
-               .pc_encode = nlm4svc_encode_testres,
-               .pc_argsize = sizeof(struct nlm_args),
-               .pc_argzero = sizeof(struct nlm_args),
-               .pc_ressize = sizeof(struct nlm_res),
-               .pc_xdrressize = Ck+St+2+No+Rg,
-               .pc_name = "TEST",
+       [NLMPROC4_TEST] = {
+               .pc_func        = nlm4svc_proc_test,
+               .pc_decode      = nlm4_svc_decode_nlm4_testargs,
+               .pc_encode      = nlm4_svc_encode_nlm4_testres,
+               .pc_argsize     = sizeof(struct nlm4_testargs_wrapper),
+               .pc_argzero     = 0,
+               .pc_ressize     = sizeof(struct nlm4_testres_wrapper),
+               .pc_xdrressize  = NLM4_nlm4_testres_sz,
+               .pc_name        = "TEST",
        },
        [NLMPROC_LOCK] = {
                .pc_func = nlm4svc_proc_lock,
@@ -793,6 +953,8 @@ static const struct svc_procedure nlm4svc_procedures[24] = {
  * Storage requirements for XDR arguments and results
  */
 union nlm4svc_xdrstore {
+       struct nlm4_testargs_wrapper    testargs;
+       struct nlm4_testres_wrapper     testres;
        struct nlm_args                 args;
        struct nlm_res                  res;
        struct nlm_reboot               reboot;