lockd: Use xdrgen XDR functions for the NLMv4 TEST procedure

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>
This commit is contained in:
Chuck Lever
2026-02-17 17:06:55 -05:00
parent 3b4839f09c
commit 3de744ee4e

View File

@@ -28,6 +28,95 @@
#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;