diff options
| author | Chuck Lever <chuck.lever@oracle.com> | 2026-04-19 14:53:02 -0400 |
|---|---|---|
| committer | Chuck Lever <chuck.lever@oracle.com> | 2026-05-28 11:31:26 -0400 |
| commit | 36643445f95472d58d0eda166df64ae99ed56e24 (patch) | |
| tree | f603ac5f3473b3b28232eca3dd5ce2fd93fb2fc0 /fs | |
| parent | 5d48a744df70b5c8b54b1900439a8b25edc82b47 (diff) | |
| download | linux-next-history-36643445f95472d58d0eda166df64ae99ed56e24.tar.gz | |
NFSD: Add NFSD_CMD_UNLOCK_IP netlink command
The existing write_unlock_ip procfs interface releases NLM file
locks held by a specific client IP address, but procfs provides
no structured way to extend that operation to other scopes such as
revoking NFSv4 state.
Add NFSD_CMD_UNLOCK_IP as a dedicated netlink command for
releasing NLM locks by client address. The command accepts a
binary sockaddr_in or sockaddr_in6 in its address attribute.
The handler validates the address family and length, then calls
nlmsvc_unlock_all_by_ip() to release matching NLM locks. Because
lockd is a single global instance, that call operates across
all network namespaces regardless of which namespace the caller
inhabits.
A separate netlink command for filesystem-scoped unlock is added in
a subsequent commit.
The nfsd_ctl_unlock_ip tracepoint is updated from string-based
address logging to __sockaddr, which stores the binary sockaddr
and formats it with %pISpc. This affects both the new netlink path
and the existing procfs write_unlock_ip path, giving consistent
structured output in both cases.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Tested-by: Dai Ngo <dai.ngo@oracle.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/nfsd/netlink.c | 12 | ||||
| -rw-r--r-- | fs/nfsd/netlink.h | 1 | ||||
| -rw-r--r-- | fs/nfsd/nfsctl.c | 40 | ||||
| -rw-r--r-- | fs/nfsd/trace.h | 13 |
4 files changed, 59 insertions, 7 deletions
diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index f99add477cc7a..5830627c0288e 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -103,6 +103,11 @@ static const struct nla_policy nfsd_cache_flush_nl_policy[NFSD_A_CACHE_FLUSH_MAS [NFSD_A_CACHE_FLUSH_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3), }; +/* NFSD_CMD_UNLOCK_IP - do */ +static const struct nla_policy nfsd_unlock_ip_nl_policy[NFSD_A_UNLOCK_IP_ADDRESS + 1] = { + [NFSD_A_UNLOCK_IP_ADDRESS] = NLA_POLICY_MIN_LEN(16), +}; + /* Ops table for nfsd */ static const struct genl_split_ops nfsd_nl_ops[] = { { @@ -189,6 +194,13 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .maxattr = NFSD_A_CACHE_FLUSH_MASK, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, + { + .cmd = NFSD_CMD_UNLOCK_IP, + .doit = nfsd_nl_unlock_ip_doit, + .policy = nfsd_unlock_ip_nl_policy, + .maxattr = NFSD_A_UNLOCK_IP_ADDRESS, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, }; static const struct genl_multicast_group nfsd_nl_mcgrps[] = { diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h index cc89732ed71bd..88edbbc68453d 100644 --- a/fs/nfsd/netlink.h +++ b/fs/nfsd/netlink.h @@ -39,6 +39,7 @@ int nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb, struct netlink_callback *cb); int nfsd_nl_expkey_set_reqs_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info); +int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info); enum { NFSD_NLGRP_NONE, diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 92f958509399f..c0ada177bb82a 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -247,7 +247,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) if (rpc_pton(net, fo_path, size, sap, salen) == 0) return -EINVAL; - trace_nfsd_ctl_unlock_ip(net, buf); + trace_nfsd_ctl_unlock_ip(net, sap, svc_addr_len(sap)); return nlmsvc_unlock_all_by_ip(sap); } @@ -2267,6 +2267,44 @@ int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_t } /** + * nfsd_nl_unlock_ip_doit - release NLM locks held by an IP address + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Return: 0 on success or a negative errno. + */ +int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct sockaddr *sap; + + if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_IP_ADDRESS)) + return -EINVAL; + sap = nla_data(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]); + switch (sap->sa_family) { + case AF_INET: + if (nla_len(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]) < + sizeof(struct sockaddr_in)) + return -EINVAL; + break; + case AF_INET6: + if (nla_len(info->attrs[NFSD_A_UNLOCK_IP_ADDRESS]) < + sizeof(struct sockaddr_in6)) + return -EINVAL; + break; + default: + return -EAFNOSUPPORT; + } + /* + * nlmsvc_unlock_all_by_ip() releases matching locks + * across all network namespaces because lockd operates + * a single global instance. + */ + trace_nfsd_ctl_unlock_ip(genl_info_net(info), sap, + svc_addr_len(sap)); + return nlmsvc_unlock_all_by_ip(sap); +} + +/** * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace * @net: a freshly-created network namespace * diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 5ad38f50836d7..976815f6f30fa 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -1985,19 +1985,20 @@ TRACE_EVENT(nfsd_cb_recall_any_done, TRACE_EVENT(nfsd_ctl_unlock_ip, TP_PROTO( const struct net *net, - const char *address + const struct sockaddr *addr, + const unsigned int addrlen ), - TP_ARGS(net, address), + TP_ARGS(net, addr, addrlen), TP_STRUCT__entry( __field(unsigned int, netns_ino) - __string(address, address) + __sockaddr(addr, addrlen) ), TP_fast_assign( __entry->netns_ino = net->ns.inum; - __assign_str(address); + __assign_sockaddr(addr, addr, addrlen); ), - TP_printk("address=%s", - __get_str(address) + TP_printk("addr=%pISpc", + __get_sockaddr(addr) ) ); |
