From: Chuck Lever Add NFSD_UNLOCK_TYPE_FILESYSTEM to the NFSD_CMD_UNLOCK netlink command, providing a netlink equivalent of /proc/fs/nfsd/unlock_fs. The filesystem scope 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 -- the same operations performed by write_unlock_fs(). Signed-off-by: Chuck Lever --- Documentation/netlink/specs/nfsd.yaml | 11 ++++++++-- fs/nfsd/netlink.c | 7 +++--- fs/nfsd/nfsctl.c | 41 ++++++++++++++++++++++++++++++++++- include/uapi/linux/nfsd_netlink.h | 2 ++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index 02fadfca22ba..1083ef60cac3 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -11,7 +11,7 @@ definitions: type: enum name: unlock-type render-max: true - entries: [ip] + entries: [ip, filesystem] attribute-sets: - @@ -149,6 +149,12 @@ attribute-sets: Required when type is ip. checks: min-len: 16 + - + name: path + type: string + doc: >- + Filesystem path whose state should be released. + Required when type is filesystem. operations: list: @@ -251,7 +257,7 @@ operations: - npools - name: unlock - doc: release NLM locks by scope + doc: release locks or revoke NFS state by scope attribute-set: unlock flags: [admin-perm] do: @@ -259,3 +265,4 @@ operations: attributes: - type - address + - path diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 9ec0d56eaa21..8367d4e3fe4f 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -48,9 +48,10 @@ static const struct nla_policy nfsd_pool_mode_set_nl_policy[NFSD_A_POOL_MODE_MOD }; /* NFSD_CMD_UNLOCK - do */ -static const struct nla_policy nfsd_unlock_nl_policy[NFSD_A_UNLOCK_ADDRESS + 1] = { - [NFSD_A_UNLOCK_TYPE] = NLA_POLICY_MAX(NLA_U32, 0), +static const struct nla_policy nfsd_unlock_nl_policy[NFSD_A_UNLOCK_PATH + 1] = { + [NFSD_A_UNLOCK_TYPE] = NLA_POLICY_MAX(NLA_U32, NFSD_UNLOCK_TYPE_MAX), [NFSD_A_UNLOCK_ADDRESS] = NLA_POLICY_MIN_LEN(16), + [NFSD_A_UNLOCK_PATH] = { .type = NLA_NUL_STRING, .len = PATH_MAX - 1, }, }; /* Ops table for nfsd */ @@ -112,7 +113,7 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .cmd = NFSD_CMD_UNLOCK, .doit = nfsd_nl_unlock_doit, .policy = nfsd_unlock_nl_policy, - .maxattr = NFSD_A_UNLOCK_ADDRESS, + .maxattr = NFSD_A_UNLOCK_PATH, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, }; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 858f3803c490..d3ed343699bd 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2180,7 +2180,44 @@ static int nfsd_nl_unlock_by_ip(struct genl_info *info) } /** - * nfsd_nl_unlock_doit - release NLM locks by scope + * nfsd_nl_unlock_by_filesystem - release locks and state on a filesystem + * @info: netlink metadata and command arguments + * + * Return: 0 on success or a negative errno. + */ +static int nfsd_nl_unlock_by_filesystem(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_PATH)) + return -EINVAL; + + trace_nfsd_ctl_unlock_fs(net, + nla_data(info->attrs[NFSD_A_UNLOCK_PATH])); + error = kern_path(nla_data(info->attrs[NFSD_A_UNLOCK_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_nl_unlock_doit - release locks or revoke NFS state * @skb: reply buffer * @info: netlink metadata and command arguments * @@ -2198,6 +2235,8 @@ int nfsd_nl_unlock_doit(struct sk_buff *skb, struct genl_info *info) switch (type) { case NFSD_UNLOCK_TYPE_IP: return nfsd_nl_unlock_by_ip(info); + case NFSD_UNLOCK_TYPE_FILESYSTEM: + return nfsd_nl_unlock_by_filesystem(info); default: return -EINVAL; } diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index 8edd75590f31..340ad36080fe 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -12,6 +12,7 @@ enum nfsd_unlock_type { NFSD_UNLOCK_TYPE_IP, + NFSD_UNLOCK_TYPE_FILESYSTEM, /* private: */ __NFSD_UNLOCK_TYPE_MAX, @@ -91,6 +92,7 @@ enum { enum { NFSD_A_UNLOCK_TYPE = 1, NFSD_A_UNLOCK_ADDRESS, + NFSD_A_UNLOCK_PATH, __NFSD_A_UNLOCK_MAX, NFSD_A_UNLOCK_MAX = (__NFSD_A_UNLOCK_MAX - 1) -- 2.53.0