From: Chuck Lever Date: Sun, 19 Apr 2026 18:53:02 +0000 (-0400) Subject: NFSD: Add NFSD_CMD_UNLOCK_IP netlink command X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=978cda83de411fcbff22ac5b2b0024cae7df806f;p=thirdparty%2Fkernel%2Flinux.git 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 Tested-by: Dai Ngo Signed-off-by: Chuck Lever --- diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index 25497b5331857..bf41682464c39 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -301,6 +301,15 @@ attribute-sets: type: u32 enum: cache-type enum-as-flags: true + - + name: unlock-ip + attributes: + - + name: address + type: binary + doc: struct sockaddr_in or struct sockaddr_in6. + checks: + min-len: 16 operations: list: @@ -455,6 +464,15 @@ operations: request: attributes: - mask + - + name: unlock-ip + doc: release NLM locks held by an IP address + attribute-set: unlock-ip + flags: [admin-perm] + do: + request: + attributes: + - address mcast-groups: list: 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 bfb937e8ad7ca..cf246652e4784 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); } @@ -2276,6 +2276,44 @@ int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_t NFSD_NLGRP_EXPORTD, GFP_KERNEL); } +/** + * 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 b631a472222be..9a73e7a1acf2a 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) ) ); diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index 060c43675599f..90ef1e6867699 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -204,6 +204,13 @@ enum { NFSD_A_CACHE_FLUSH_MAX = (__NFSD_A_CACHE_FLUSH_MAX - 1) }; +enum { + NFSD_A_UNLOCK_IP_ADDRESS = 1, + + __NFSD_A_UNLOCK_IP_MAX, + NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1) +}; + enum { NFSD_CMD_RPC_STATUS_GET = 1, NFSD_CMD_THREADS_SET, @@ -220,6 +227,7 @@ enum { NFSD_CMD_EXPKEY_GET_REQS, NFSD_CMD_EXPKEY_SET_REQS, NFSD_CMD_CACHE_FLUSH, + NFSD_CMD_UNLOCK_IP, __NFSD_CMD_MAX, NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)