mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
NFSD: Add NFSD_CMD_UNLOCK_EXPORT netlink command
When a filesystem is exported to NFS clients, NFSv4 state (opens, locks, delegations, layouts) holds references that prevent the underlying filesystem from being unmounted. NFSD_CMD_UNLOCK_FILESYSTEM addresses this at superblock granularity, but administrators unexporting a single path on a shared filesystem (e.g., one of several exports on the same device) need finer control. Add NFSD_CMD_UNLOCK_EXPORT, which revokes NFSv4 state acquired through exports of a specific path. Matching is by path identity (dentry + vfsmount) via the sc_export field on each nfs4_stid, so multiple svc_export objects for the same path -- one per auth_domain -- are handled correctly without requiring the caller to name a specific client. The command takes a single "path" attribute. Userspace (exportfs -u) sends this after removing the last client for a given path, enabling the underlying filesystem to be unmounted. When multiple clients share an export path, individual unexports do not trigger state revocation; only the final one does. Reviewed-by: Jeff Layton <jlayton@kernel.org> Tested-by: Dai Ngo <dai.ngo@oracle.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
parent
ba0cde5dc8
commit
2eac189bb0
|
|
@ -317,6 +317,19 @@ attribute-sets:
|
|||
name: path
|
||||
type: string
|
||||
doc: Filesystem path whose state should be released.
|
||||
-
|
||||
name: unlock-export
|
||||
attributes:
|
||||
-
|
||||
name: path
|
||||
type: string
|
||||
doc: >-
|
||||
Export path whose NFSv4 state should be revoked.
|
||||
All state (opens, locks, delegations, layouts) acquired
|
||||
through any export of this path is revoked, regardless
|
||||
of which client holds the state. Intended for use after
|
||||
all clients have been unexported from a given path,
|
||||
enabling the underlying filesystem to be unmounted.
|
||||
|
||||
operations:
|
||||
list:
|
||||
|
|
@ -489,6 +502,20 @@ operations:
|
|||
request:
|
||||
attributes:
|
||||
- path
|
||||
-
|
||||
name: unlock-export
|
||||
doc: >-
|
||||
Revoke NFSv4 state acquired through exports of a given path.
|
||||
Unlike unlock-filesystem, which operates at superblock granularity,
|
||||
this command targets only state associated with a specific export
|
||||
path. Userspace (exportfs -u) sends this after removing the last
|
||||
client for a path so the underlying filesystem can be unmounted.
|
||||
attribute-set: unlock-export
|
||||
flags: [admin-perm]
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- path
|
||||
|
||||
mcast-groups:
|
||||
list:
|
||||
|
|
|
|||
|
|
@ -113,6 +113,11 @@ static const struct nla_policy nfsd_unlock_filesystem_nl_policy[NFSD_A_UNLOCK_FI
|
|||
[NFSD_A_UNLOCK_FILESYSTEM_PATH] = { .type = NLA_NUL_STRING, },
|
||||
};
|
||||
|
||||
/* NFSD_CMD_UNLOCK_EXPORT - do */
|
||||
static const struct nla_policy nfsd_unlock_export_nl_policy[NFSD_A_UNLOCK_EXPORT_PATH + 1] = {
|
||||
[NFSD_A_UNLOCK_EXPORT_PATH] = { .type = NLA_NUL_STRING, },
|
||||
};
|
||||
|
||||
/* Ops table for nfsd */
|
||||
static const struct genl_split_ops nfsd_nl_ops[] = {
|
||||
{
|
||||
|
|
@ -213,6 +218,13 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
|
|||
.maxattr = NFSD_A_UNLOCK_FILESYSTEM_PATH,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = NFSD_CMD_UNLOCK_EXPORT,
|
||||
.doit = nfsd_nl_unlock_export_doit,
|
||||
.policy = nfsd_unlock_export_nl_policy,
|
||||
.maxattr = NFSD_A_UNLOCK_EXPORT_PATH,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group nfsd_nl_mcgrps[] = {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ 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);
|
||||
int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
enum {
|
||||
NFSD_NLGRP_NONE,
|
||||
|
|
|
|||
|
|
@ -1911,6 +1911,73 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
|
|||
spin_unlock(&nn->client_lock);
|
||||
}
|
||||
|
||||
static struct nfs4_stid *find_one_export_stid(struct nfs4_client *clp,
|
||||
const struct path *path,
|
||||
unsigned int sc_types)
|
||||
{
|
||||
unsigned long id = 0;
|
||||
struct nfs4_stid *stid;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
while ((stid = idr_get_next_ul(&clp->cl_stateids, &id)) != NULL) {
|
||||
if ((stid->sc_type & sc_types) &&
|
||||
stid->sc_status == 0 &&
|
||||
stid->sc_export &&
|
||||
path_equal(&stid->sc_export->ex_path, path)) {
|
||||
refcount_inc(&stid->sc_count);
|
||||
break;
|
||||
}
|
||||
id++;
|
||||
}
|
||||
spin_unlock(&clp->cl_lock);
|
||||
return stid;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd4_revoke_export_states - revoke nfsv4 states acquired through an export
|
||||
* @nn: used to identify instance of nfsd (there is one per net namespace)
|
||||
* @path: export path whose states should be revoked
|
||||
*
|
||||
* All nfs4 states (open, lock, delegation, layout) acquired through any
|
||||
* export matching @path are revoked, regardless of which client holds
|
||||
* them. Matching is by path identity (dentry + vfsmount), so multiple
|
||||
* svc_export objects for the same path -- one per auth_domain -- are
|
||||
* handled correctly.
|
||||
*
|
||||
* Userspace (exportfs -u) sends this after removing the last client
|
||||
* for a path, enabling the underlying filesystem to be unmounted.
|
||||
*/
|
||||
void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path)
|
||||
{
|
||||
unsigned int idhashval;
|
||||
unsigned int sc_types;
|
||||
|
||||
sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT;
|
||||
|
||||
spin_lock(&nn->client_lock);
|
||||
for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
|
||||
struct list_head *head = &nn->conf_id_hashtbl[idhashval];
|
||||
struct nfs4_client *clp;
|
||||
retry:
|
||||
list_for_each_entry(clp, head, cl_idhash) {
|
||||
struct nfs4_stid *stid = find_one_export_stid(
|
||||
clp, path,
|
||||
sc_types);
|
||||
if (stid) {
|
||||
spin_unlock(&nn->client_lock);
|
||||
revoke_one_stid(nn, clp, stid);
|
||||
nfs4_put_stid(stid);
|
||||
spin_lock(&nn->client_lock);
|
||||
if (clp->cl_minorversion == 0)
|
||||
nn->nfs40_last_revoke =
|
||||
ktime_get_boottime_seconds();
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&nn->client_lock);
|
||||
}
|
||||
|
||||
static inline int
|
||||
hash_sessionid(struct nfs4_sessionid *sessionid)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2354,6 +2354,51 @@ int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb,
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_nl_unlock_export_doit - revoke NFSv4 state for an export path
|
||||
* @skb: reply buffer
|
||||
* @info: netlink metadata and command arguments
|
||||
*
|
||||
* Revokes all NFSv4 state (opens, locks, delegations, layouts) acquired
|
||||
* through any export of the given path, regardless of which client holds
|
||||
* the state. Userspace (exportfs -u) sends this after removing the last
|
||||
* client for a path so the underlying filesystem can be unmounted.
|
||||
*
|
||||
* Unlike NFSD_CMD_UNLOCK_FILESYSTEM, which operates at superblock
|
||||
* granularity, this command revokes only the state associated with
|
||||
* exports of a specific path.
|
||||
*
|
||||
* Return: 0 on success or a negative errno.
|
||||
*/
|
||||
int nfsd_nl_unlock_export_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_EXPORT_PATH))
|
||||
return -EINVAL;
|
||||
|
||||
trace_nfsd_ctl_unlock_export(net,
|
||||
nla_data(info->attrs[NFSD_A_UNLOCK_EXPORT_PATH]));
|
||||
error = kern_path(
|
||||
nla_data(info->attrs[NFSD_A_UNLOCK_EXPORT_PATH]),
|
||||
0, &path);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
if (nn->nfsd_serv)
|
||||
nfsd4_revoke_export_states(nn, &path);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -863,6 +863,7 @@ struct nfsd_file *find_any_file(struct nfs4_file *f);
|
|||
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb);
|
||||
void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path);
|
||||
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb);
|
||||
int nfsd_net_cb_init(struct nfsd_net *nn);
|
||||
void nfsd_net_cb_shutdown(struct nfsd_net *nn);
|
||||
|
|
@ -870,6 +871,10 @@ void nfsd_net_cb_shutdown(struct nfsd_net *nn);
|
|||
static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
|
||||
{
|
||||
}
|
||||
static inline void nfsd4_revoke_export_states(struct nfsd_net *nn,
|
||||
const struct path *path)
|
||||
{
|
||||
}
|
||||
static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2021,6 +2021,25 @@ TRACE_EVENT(nfsd_ctl_unlock_fs,
|
|||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(nfsd_ctl_unlock_export,
|
||||
TP_PROTO(
|
||||
const struct net *net,
|
||||
const char *path
|
||||
),
|
||||
TP_ARGS(net, path),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, netns_ino)
|
||||
__string(path, path)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->netns_ino = net->ns.inum;
|
||||
__assign_str(path);
|
||||
),
|
||||
TP_printk("path=%s",
|
||||
__get_str(path)
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(nfsd_ctl_filehandle,
|
||||
TP_PROTO(
|
||||
const struct net *net,
|
||||
|
|
|
|||
|
|
@ -218,6 +218,13 @@ enum {
|
|||
NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
NFSD_A_UNLOCK_EXPORT_PATH = 1,
|
||||
|
||||
__NFSD_A_UNLOCK_EXPORT_MAX,
|
||||
NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
NFSD_CMD_RPC_STATUS_GET = 1,
|
||||
NFSD_CMD_THREADS_SET,
|
||||
|
|
@ -236,6 +243,7 @@ enum {
|
|||
NFSD_CMD_CACHE_FLUSH,
|
||||
NFSD_CMD_UNLOCK_IP,
|
||||
NFSD_CMD_UNLOCK_FILESYSTEM,
|
||||
NFSD_CMD_UNLOCK_EXPORT,
|
||||
|
||||
__NFSD_CMD_MAX,
|
||||
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user