From: Chuck Lever Date: Sun, 19 Apr 2026 18:53:03 +0000 (-0400) Subject: NFSD: Add NFSD_CMD_UNLOCK_FILESYSTEM netlink command X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=327c5168eff2e7f1760ca67fcb0f9053019fbfee;p=thirdparty%2Fkernel%2Flinux.git NFSD: Add NFSD_CMD_UNLOCK_FILESYSTEM netlink command Add NFSD_CMD_UNLOCK_FILESYSTEM as a dedicated netlink command for revoking NFS state under a filesystem path, providing a netlink equivalent of /proc/fs/nfsd/unlock_fs. The command requires a "path" string attribute containing the filesystem path whose state should be released. The handler resolves the path to its superblock, then cancels async copies, releases NLM locks, and revokes NFSv4 state on that superblock. 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 bf41682464c39..e121c54033a06 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -310,6 +310,13 @@ attribute-sets: doc: struct sockaddr_in or struct sockaddr_in6. checks: min-len: 16 + - + name: unlock-filesystem + attributes: + - + name: path + type: string + doc: Filesystem path whose state should be released. operations: list: @@ -473,6 +480,15 @@ operations: request: attributes: - address + - + name: unlock-filesystem + doc: revoke NFS state under a filesystem path + attribute-set: unlock-filesystem + flags: [admin-perm] + do: + request: + attributes: + - path mcast-groups: list: diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 5830627c0288e..63df3c4cf63a0 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -108,6 +108,11 @@ static const struct nla_policy nfsd_unlock_ip_nl_policy[NFSD_A_UNLOCK_IP_ADDRESS [NFSD_A_UNLOCK_IP_ADDRESS] = NLA_POLICY_MIN_LEN(16), }; +/* NFSD_CMD_UNLOCK_FILESYSTEM - do */ +static const struct nla_policy nfsd_unlock_filesystem_nl_policy[NFSD_A_UNLOCK_FILESYSTEM_PATH + 1] = { + [NFSD_A_UNLOCK_FILESYSTEM_PATH] = { .type = NLA_NUL_STRING, }, +}; + /* Ops table for nfsd */ static const struct genl_split_ops nfsd_nl_ops[] = { { @@ -201,6 +206,13 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .maxattr = NFSD_A_UNLOCK_IP_ADDRESS, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, + { + .cmd = NFSD_CMD_UNLOCK_FILESYSTEM, + .doit = nfsd_nl_unlock_filesystem_doit, + .policy = nfsd_unlock_filesystem_nl_policy, + .maxattr = NFSD_A_UNLOCK_FILESYSTEM_PATH, + .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 88edbbc68453d..29bd5468d4019 100644 --- a/fs/nfsd/netlink.h +++ b/fs/nfsd/netlink.h @@ -40,6 +40,7 @@ int nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb, 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); +int nfsd_nl_unlock_filesystem_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 cf246652e4784..f7f104cc457dd 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2314,6 +2314,46 @@ int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info) return nlmsvc_unlock_all_by_ip(sap); } +/** + * nfsd_nl_unlock_filesystem_doit - revoke NFS state under a filesystem path + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Return: 0 on success or a negative errno. + */ +int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct path path; + int error; + + if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_FILESYSTEM_PATH)) + return -EINVAL; + + trace_nfsd_ctl_unlock_fs(net, + nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH])); + error = kern_path( + nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH]), + 0, &path); + if (error) + return error; + + nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb); + error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); + + mutex_lock(&nfsd_mutex); + if (nn->nfsd_serv) + nfsd4_revoke_states(nn, path.dentry->d_sb); + else + error = -EINVAL; + mutex_unlock(&nfsd_mutex); + + path_put(&path); + return error; +} + /** * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace * @net: a freshly-created network namespace diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index 90ef1e6867699..d01096c06d724 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -211,6 +211,13 @@ enum { NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1) }; +enum { + NFSD_A_UNLOCK_FILESYSTEM_PATH = 1, + + __NFSD_A_UNLOCK_FILESYSTEM_MAX, + NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1) +}; + enum { NFSD_CMD_RPC_STATUS_GET = 1, NFSD_CMD_THREADS_SET, @@ -228,6 +235,7 @@ enum { NFSD_CMD_EXPKEY_SET_REQS, NFSD_CMD_CACHE_FLUSH, NFSD_CMD_UNLOCK_IP, + NFSD_CMD_UNLOCK_FILESYSTEM, __NFSD_CMD_MAX, NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)