aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorChuck Lever <chuck.lever@oracle.com>2026-04-19 14:53:02 -0400
committerChuck Lever <chuck.lever@oracle.com>2026-05-28 11:31:26 -0400
commit36643445f95472d58d0eda166df64ae99ed56e24 (patch)
treef603ac5f3473b3b28232eca3dd5ce2fd93fb2fc0 /fs
parent5d48a744df70b5c8b54b1900439a8b25edc82b47 (diff)
downloadlinux-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.c12
-rw-r--r--fs/nfsd/netlink.h1
-rw-r--r--fs/nfsd/nfsctl.c40
-rw-r--r--fs/nfsd/trace.h13
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)
)
);