]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFSD: Add NFSD_CMD_UNLOCK_FILESYSTEM netlink command
authorChuck Lever <chuck.lever@oracle.com>
Sun, 19 Apr 2026 18:53:03 +0000 (14:53 -0400)
committerChuck Lever <cel@kernel.org>
Tue, 9 Jun 2026 20:32:59 +0000 (16:32 -0400)
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 <jlayton@kernel.org>
Tested-by: Dai Ngo <dai.ngo@oracle.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Documentation/netlink/specs/nfsd.yaml
fs/nfsd/netlink.c
fs/nfsd/netlink.h
fs/nfsd/nfsctl.c
include/uapi/linux/nfsd_netlink.h

index bf41682464c398b0bcb56423f2f1bedd9214cb9d..e121c54033a06cfcf6da246830448048ae9a034b 100644 (file)
@@ -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:
index 5830627c0288eb557c1a590f1116182117c726d7..63df3c4cf63a0e437d42efdd611e582ff3d23e82 100644 (file)
@@ -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[] = {
index 88edbbc68453d0d991a9a61528a5930068f210b7..29bd5468d40197b9b9579d3416d761dda74b094f 100644 (file)
@@ -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,
index cf246652e4784004e8e8343c2e5ef2fc420c9f54..f7f104cc457ddb679f0f7541f1dc6924765808c6 100644 (file)
@@ -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
index 90ef1e6867699f8259358a9a138e526fdca18253..d01096c06d724871b3f26f69922cdd926d8ef25b 100644 (file)
@@ -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)