lockd: Have nlm_fopen() return errno values

The nlm_fopen() function is part of the API between nfsd and lockd.

Currently its return value is an on-the-wire NLM status code. But
that forces NFSD to include NLM wire protocol definitions despite
having no other dependency on the NLM wire protocol.

In addition, a CONFIG_LOCKD_V4 Kconfig symbol appears in the middle
of NFSD source code.

Refactor: Let's not use on-the-wire values as part of a high-level
API between two Linux kernel modules. That's what we have errno for,
right?

And, instead of simply moving the CONFIG_LOCKD_V4 check, we can get
rid of it entirely and let the decision of what actual NLM status
code goes on the wire to be left up to NLM version-specific code.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
Chuck Lever 2026-01-28 10:19:25 -05:00
parent 9e0d0c6194
commit 7db001e03d
6 changed files with 82 additions and 37 deletions

View File

@ -73,9 +73,21 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
no_locks:
nlmsvc_release_host(host);
if (error)
return error;
return nlm_lck_denied_nolocks;
switch (error) {
case nlm_granted:
return nlm_lck_denied_nolocks;
case nlm__int__stale_fh:
return nlm4_stale_fh;
case nlm__int__failed:
return nlm4_failed;
default:
if (be32_to_cpu(error) >= 30000) {
pr_warn_once("lockd: unhandled internal status %u\n",
be32_to_cpu(error));
return nlm4_failed;
}
return error;
}
}
/*

View File

@ -39,8 +39,20 @@ static inline __be32 cast_status(__be32 status)
#else
static inline __be32 cast_status(__be32 status)
{
if (status == nlm__int__deadlock)
switch (status) {
case nlm__int__deadlock:
status = nlm_lck_denied;
break;
case nlm__int__stale_fh:
case nlm__int__failed:
status = nlm_lck_denied_nolocks;
break;
default:
if (be32_to_cpu(status) >= 30000)
pr_warn_once("lockd: unhandled internal status %u\n",
be32_to_cpu(status));
break;
}
return status;
}
#endif

View File

@ -87,14 +87,29 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
struct nlm_file *file, int mode)
{
struct file **fp = &file->f_file[mode];
__be32 nfserr;
__be32 nlmerr = nlm_granted;
int error;
if (*fp)
return 0;
nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
if (nfserr)
dprintk("lockd: open failed (error %d)\n", nfserr);
return nfserr;
return nlmerr;
error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
if (error) {
dprintk("lockd: open failed (errno %d)\n", error);
switch (error) {
case -EWOULDBLOCK:
nlmerr = nlm__int__drop_reply;
break;
case -ESTALE:
nlmerr = nlm__int__stale_fh;
break;
default:
nlmerr = nlm__int__failed;
break;
}
}
return nlmerr;
}
/*

View File

@ -14,19 +14,20 @@
#define NFSDDBG_FACILITY NFSDDBG_LOCKD
#ifdef CONFIG_LOCKD_V4
#define nlm_stale_fh nlm4_stale_fh
#define nlm_failed nlm4_failed
#else
#define nlm_stale_fh nlm_lck_denied_nolocks
#define nlm_failed nlm_lck_denied_nolocks
#endif
/*
* Note: we hold the dentry use count while the file is open.
/**
* nlm_fopen - Open an NFSD file
* @rqstp: NLM RPC procedure execution context
* @f: NFS file handle to be opened
* @filp: OUT: an opened struct file
* @flags: the POSIX open flags to use
*
* nlm_fopen() holds the dentry reference until nlm_fclose() releases it.
*
* Returns zero on success or a negative errno value if the file
* cannot be opened.
*/
static __be32
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp,
int mode)
static int nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f,
struct file **filp, int flags)
{
__be32 nfserr;
int access;
@ -47,18 +48,17 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp,
* if NFSEXP_NOAUTHNLM is set. Some older clients use AUTH_NULL
* for NLM requests.
*/
access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
access = (flags == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS;
nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
fh_put(&fh);
/* We return nlm error codes as nlm doesn't know
* about nfsd, but nfsd does know about nlm..
*/
switch (nfserr) {
case nfs_ok:
return 0;
break;
case nfserr_jukebox:
/* this error can indicate a presence of a conflicting
/*
* This error can indicate a presence of a conflicting
* delegation to an NLM lock request. Options are:
* (1) For now, drop this request and make the client
* retry. When delegation is returned, client's lock retry
@ -66,19 +66,25 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp,
* (2) NLM4_DENIED as per "spec" signals to the client
* that the lock is unavailable now but client can retry.
* Linux client implementation does not. It treats
* NLM4_DENIED same as NLM4_FAILED and errors the request.
* NLM4_DENIED same as NLM4_FAILED and fails the request.
* (3) For the future, treat this as blocked lock and try
* to callback when the delegation is returned but might
* not have a proper lock request to block on.
*/
return nlm__int__drop_reply;
return -EWOULDBLOCK;
case nfserr_stale:
return nlm_stale_fh;
return -ESTALE;
default:
return nlm_failed;
return -ENOLCK;
}
return 0;
}
/**
* nlm_fclose - Close an NFSD file
* @filp: a struct file that was opened by nlm_fopen()
*/
static void
nlm_fclose(struct file *filp)
{

View File

@ -26,11 +26,9 @@ struct rpc_clnt;
* This is the set of functions for lockd->nfsd communication
*/
struct nlmsvc_binding {
__be32 (*fopen)(struct svc_rqst *,
struct nfs_fh *,
struct file **,
int mode);
void (*fclose)(struct file *);
int (*fopen)(struct svc_rqst *rqstp, struct nfs_fh *f,
struct file **filp, int flags);
void (*fclose)(struct file *filp);
};
extern const struct nlmsvc_binding *nlmsvc_ops;

View File

@ -44,6 +44,8 @@
*/
#define nlm__int__drop_reply cpu_to_be32(30000)
#define nlm__int__deadlock cpu_to_be32(30001)
#define nlm__int__stale_fh cpu_to_be32(30002)
#define nlm__int__failed cpu_to_be32(30003)
/*
* Lockd host handle (used both by the client and server personality).