mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 07:03:28 -04:00
nfsd: cancel async COPY operations when admin revokes filesystem state
Async COPY operations hold copy stateids that represent NFSv4 state. Thus, when the NFS server administrator revokes all NFSv4 state for a filesystem via the unlock_fs interface, ongoing async COPY operations referencing that filesystem must also be canceled. Each cancelled copy triggers a CB_OFFLOAD callback carrying the NFS4ERR_ADMIN_REVOKED status to notify the client that the server terminated the operation. The static drop_client() function is renamed to nfsd4_put_client() and exported. The function must be exported because both the new nfsd4_cancel_copy_by_sb() and the CB_OFFLOAD release callback in nfs4proc.c need to release client references. Reviewed-by: NeilBrown <neil@brown.name> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
@@ -1427,14 +1427,26 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
|
|||||||
kfree(copy);
|
kfree(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void release_copy_files(struct nfsd4_copy *copy);
|
||||||
|
|
||||||
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
|
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
|
||||||
{
|
{
|
||||||
trace_nfsd_copy_async_cancel(copy);
|
trace_nfsd_copy_async_cancel(copy);
|
||||||
if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) {
|
if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) {
|
||||||
kthread_stop(copy->copy_task);
|
kthread_stop(copy->copy_task);
|
||||||
copy->nfserr = nfs_ok;
|
if (!test_bit(NFSD4_COPY_F_CB_ERROR, ©->cp_flags))
|
||||||
|
copy->nfserr = nfs_ok;
|
||||||
set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags);
|
set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The copy was removed from async_copies before this function
|
||||||
|
* was called, so the reaper cannot clean it up. Release files
|
||||||
|
* here regardless of who won the STOPPED race. If the thread
|
||||||
|
* set STOPPED, it has finished using the files. If STOPPED
|
||||||
|
* was set here, kthread_stop() waited for the thread to exit.
|
||||||
|
*/
|
||||||
|
release_copy_files(copy);
|
||||||
nfs4_put_copy(copy);
|
nfs4_put_copy(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1462,6 +1474,72 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp)
|
|||||||
while ((copy = nfsd4_unhash_copy(clp)) != NULL)
|
while ((copy = nfsd4_unhash_copy(clp)) != NULL)
|
||||||
nfsd4_stop_copy(copy);
|
nfsd4_stop_copy(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy,
|
||||||
|
const struct super_block *sb)
|
||||||
|
{
|
||||||
|
if (copy->nf_src &&
|
||||||
|
file_inode(copy->nf_src->nf_file)->i_sb == sb)
|
||||||
|
return true;
|
||||||
|
if (copy->nf_dst &&
|
||||||
|
file_inode(copy->nf_dst->nf_file)->i_sb == sb)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb
|
||||||
|
* @net: net namespace containing the copy operations
|
||||||
|
* @sb: targeted superblock
|
||||||
|
*/
|
||||||
|
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
|
||||||
|
{
|
||||||
|
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||||
|
struct nfsd4_copy *copy, *tmp;
|
||||||
|
struct nfs4_client *clp;
|
||||||
|
unsigned int idhashval;
|
||||||
|
LIST_HEAD(to_cancel);
|
||||||
|
|
||||||
|
spin_lock(&nn->client_lock);
|
||||||
|
for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
|
||||||
|
struct list_head *head = &nn->conf_id_hashtbl[idhashval];
|
||||||
|
|
||||||
|
list_for_each_entry(clp, head, cl_idhash) {
|
||||||
|
spin_lock(&clp->async_lock);
|
||||||
|
list_for_each_entry_safe(copy, tmp,
|
||||||
|
&clp->async_copies, copies) {
|
||||||
|
if (nfsd4_copy_on_sb(copy, sb)) {
|
||||||
|
refcount_inc(©->refcount);
|
||||||
|
/*
|
||||||
|
* Hold a reference on the client while
|
||||||
|
* nfsd4_stop_copy() runs. Unlike
|
||||||
|
* nfsd4_unhash_copy(), cp_clp is not
|
||||||
|
* NULLed here because nfsd4_send_cb_offload()
|
||||||
|
* needs a valid client to send CB_OFFLOAD.
|
||||||
|
* That function takes its own reference to
|
||||||
|
* survive callback flight.
|
||||||
|
*/
|
||||||
|
kref_get(&clp->cl_nfsdfs.cl_ref);
|
||||||
|
copy->nfserr = nfserr_admin_revoked;
|
||||||
|
set_bit(NFSD4_COPY_F_CB_ERROR,
|
||||||
|
©->cp_flags);
|
||||||
|
list_move(©->copies, &to_cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&clp->async_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&nn->client_lock);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(copy, tmp, &to_cancel, copies) {
|
||||||
|
struct nfs4_client *clp = copy->cp_clp;
|
||||||
|
|
||||||
|
list_del_init(©->copies);
|
||||||
|
nfsd4_stop_copy(copy);
|
||||||
|
nfsd4_put_client(clp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||||
|
|
||||||
extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
|
extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
|
||||||
@@ -1751,6 +1829,7 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
|
|||||||
container_of(cbo, struct nfsd4_copy, cp_cb_offload);
|
container_of(cbo, struct nfsd4_copy, cp_cb_offload);
|
||||||
|
|
||||||
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags);
|
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags);
|
||||||
|
nfsd4_put_client(cb->cb_clp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
|
static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
|
||||||
@@ -1870,10 +1949,14 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
|
|||||||
|
|
||||||
static void release_copy_files(struct nfsd4_copy *copy)
|
static void release_copy_files(struct nfsd4_copy *copy)
|
||||||
{
|
{
|
||||||
if (copy->nf_src)
|
if (copy->nf_src) {
|
||||||
nfsd_file_put(copy->nf_src);
|
nfsd_file_put(copy->nf_src);
|
||||||
if (copy->nf_dst)
|
copy->nf_src = NULL;
|
||||||
|
}
|
||||||
|
if (copy->nf_dst) {
|
||||||
nfsd_file_put(copy->nf_dst);
|
nfsd_file_put(copy->nf_dst);
|
||||||
|
copy->nf_dst = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_async_copy(struct nfsd4_copy *copy)
|
static void cleanup_async_copy(struct nfsd4_copy *copy)
|
||||||
@@ -1892,18 +1975,34 @@ static void cleanup_async_copy(struct nfsd4_copy *copy)
|
|||||||
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
|
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
|
||||||
{
|
{
|
||||||
struct nfsd4_cb_offload *cbo = ©->cp_cb_offload;
|
struct nfsd4_cb_offload *cbo = ©->cp_cb_offload;
|
||||||
|
struct nfs4_client *clp = copy->cp_clp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cp_clp is NULL when called via nfsd4_shutdown_copy() during
|
||||||
|
* client destruction. Skip the callback; the client is gone.
|
||||||
|
*/
|
||||||
|
if (!clp) {
|
||||||
|
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res));
|
memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res));
|
||||||
memcpy(&cbo->co_fh, ©->fh, sizeof(copy->fh));
|
memcpy(&cbo->co_fh, ©->fh, sizeof(copy->fh));
|
||||||
cbo->co_nfserr = copy->nfserr;
|
cbo->co_nfserr = copy->nfserr;
|
||||||
cbo->co_retries = 5;
|
cbo->co_retries = 5;
|
||||||
|
|
||||||
nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops,
|
/*
|
||||||
|
* Hold a reference on the client while the callback is in flight.
|
||||||
|
* Released in nfsd4_cb_offload_release().
|
||||||
|
*/
|
||||||
|
kref_get(&clp->cl_nfsdfs.cl_ref);
|
||||||
|
|
||||||
|
nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops,
|
||||||
NFSPROC4_CLNT_CB_OFFLOAD);
|
NFSPROC4_CLNT_CB_OFFLOAD);
|
||||||
nfsd41_cb_referring_call(&cbo->co_cb, &cbo->co_referring_sessionid,
|
nfsd41_cb_referring_call(&cbo->co_cb, &cbo->co_referring_sessionid,
|
||||||
cbo->co_referring_slotid,
|
cbo->co_referring_slotid,
|
||||||
cbo->co_referring_seqno);
|
cbo->co_referring_seqno);
|
||||||
trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid,
|
trace_nfsd_cb_offload(clp, &cbo->co_res.cb_stateid,
|
||||||
&cbo->co_fh, copy->cp_count, copy->nfserr);
|
&cbo->co_fh, copy->cp_count, copy->nfserr);
|
||||||
nfsd4_try_run_cb(&cbo->co_cb);
|
nfsd4_try_run_cb(&cbo->co_cb);
|
||||||
}
|
}
|
||||||
@@ -1918,6 +2017,7 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
|
|||||||
static int nfsd4_do_async_copy(void *data)
|
static int nfsd4_do_async_copy(void *data)
|
||||||
{
|
{
|
||||||
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
|
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
|
||||||
|
__be32 nfserr = nfs_ok;
|
||||||
|
|
||||||
trace_nfsd_copy_async(copy);
|
trace_nfsd_copy_async(copy);
|
||||||
if (nfsd4_ssc_is_inter(copy)) {
|
if (nfsd4_ssc_is_inter(copy)) {
|
||||||
@@ -1928,23 +2028,25 @@ static int nfsd4_do_async_copy(void *data)
|
|||||||
if (IS_ERR(filp)) {
|
if (IS_ERR(filp)) {
|
||||||
switch (PTR_ERR(filp)) {
|
switch (PTR_ERR(filp)) {
|
||||||
case -EBADF:
|
case -EBADF:
|
||||||
copy->nfserr = nfserr_wrong_type;
|
nfserr = nfserr_wrong_type;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
copy->nfserr = nfserr_offload_denied;
|
nfserr = nfserr_offload_denied;
|
||||||
}
|
}
|
||||||
/* ss_mnt will be unmounted by the laundromat */
|
/* ss_mnt will be unmounted by the laundromat */
|
||||||
goto do_callback;
|
goto do_callback;
|
||||||
}
|
}
|
||||||
copy->nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
|
nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
|
||||||
false);
|
false);
|
||||||
nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst);
|
nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst);
|
||||||
} else {
|
} else {
|
||||||
copy->nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
|
nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
|
||||||
copy->nf_dst->nf_file, false);
|
copy->nf_dst->nf_file, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
do_callback:
|
do_callback:
|
||||||
|
if (!test_bit(NFSD4_COPY_F_CB_ERROR, ©->cp_flags))
|
||||||
|
copy->nfserr = nfserr;
|
||||||
/* The kthread exits forthwith. Ensure that a subsequent
|
/* The kthread exits forthwith. Ensure that a subsequent
|
||||||
* OFFLOAD_CANCEL won't try to kill it again. */
|
* OFFLOAD_CANCEL won't try to kill it again. */
|
||||||
set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags);
|
set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags);
|
||||||
|
|||||||
@@ -2413,7 +2413,13 @@ static void __free_client(struct kref *k)
|
|||||||
kmem_cache_free(client_slab, clp);
|
kmem_cache_free(client_slab, clp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drop_client(struct nfs4_client *clp)
|
/**
|
||||||
|
* nfsd4_put_client - release a reference on an nfs4_client
|
||||||
|
* @clp: the client to be released
|
||||||
|
*
|
||||||
|
* When the last reference is released, the client is freed.
|
||||||
|
*/
|
||||||
|
void nfsd4_put_client(struct nfs4_client *clp)
|
||||||
{
|
{
|
||||||
kref_put(&clp->cl_nfsdfs.cl_ref, __free_client);
|
kref_put(&clp->cl_nfsdfs.cl_ref, __free_client);
|
||||||
}
|
}
|
||||||
@@ -2435,7 +2441,7 @@ free_client(struct nfs4_client *clp)
|
|||||||
clp->cl_nfsd_dentry = NULL;
|
clp->cl_nfsd_dentry = NULL;
|
||||||
wake_up_all(&expiry_wq);
|
wake_up_all(&expiry_wq);
|
||||||
}
|
}
|
||||||
drop_client(clp);
|
nfsd4_put_client(clp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* must be called under the client_lock */
|
/* must be called under the client_lock */
|
||||||
@@ -2833,7 +2839,7 @@ static int client_info_show(struct seq_file *m, void *v)
|
|||||||
spin_unlock(&clp->cl_lock);
|
spin_unlock(&clp->cl_lock);
|
||||||
seq_puts(m, "\n");
|
seq_puts(m, "\n");
|
||||||
|
|
||||||
drop_client(clp);
|
nfsd4_put_client(clp);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -3099,7 +3105,7 @@ static int client_states_open(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
ret = seq_open(file, &states_seq_ops);
|
ret = seq_open(file, &states_seq_ops);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
drop_client(clp);
|
nfsd4_put_client(clp);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
s = file->private_data;
|
s = file->private_data;
|
||||||
@@ -3113,7 +3119,7 @@ static int client_opens_release(struct inode *inode, struct file *file)
|
|||||||
struct nfs4_client *clp = m->private;
|
struct nfs4_client *clp = m->private;
|
||||||
|
|
||||||
/* XXX: alternatively, we could get/drop in seq start/stop */
|
/* XXX: alternatively, we could get/drop in seq start/stop */
|
||||||
drop_client(clp);
|
nfsd4_put_client(clp);
|
||||||
return seq_release(inode, file);
|
return seq_release(inode, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3169,7 +3175,7 @@ static ssize_t client_ctl_write(struct file *file, const char __user *buf,
|
|||||||
if (!clp)
|
if (!clp)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
force_expire_client(clp);
|
force_expire_client(clp);
|
||||||
drop_client(clp);
|
nfsd4_put_client(clp);
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3204,7 +3210,7 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
|
|||||||
{
|
{
|
||||||
struct nfs4_client *clp = cb->cb_clp;
|
struct nfs4_client *clp = cb->cb_clp;
|
||||||
|
|
||||||
drop_client(clp);
|
nfsd4_put_client(clp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
@@ -285,6 +285,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
|
|||||||
* 2. Is that directory a mount point, or
|
* 2. Is that directory a mount point, or
|
||||||
* 3. Is that directory the root of an exported file system?
|
* 3. Is that directory the root of an exported file system?
|
||||||
*/
|
*/
|
||||||
|
nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb);
|
||||||
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
|
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
|
||||||
mutex_lock(&nfsd_mutex);
|
mutex_lock(&nfsd_mutex);
|
||||||
nn = net_generic(netns(file), nfsd_net_id);
|
nn = net_generic(netns(file), nfsd_net_id);
|
||||||
|
|||||||
@@ -822,6 +822,7 @@ static inline void nfsd4_try_run_cb(struct nfsd4_callback *cb)
|
|||||||
|
|
||||||
extern void nfsd4_shutdown_callback(struct nfs4_client *);
|
extern void nfsd4_shutdown_callback(struct nfs4_client *);
|
||||||
extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
|
extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
|
||||||
|
void nfsd4_put_client(struct nfs4_client *clp);
|
||||||
void nfsd4_async_copy_reaper(struct nfsd_net *nn);
|
void nfsd4_async_copy_reaper(struct nfsd_net *nn);
|
||||||
bool nfsd4_has_active_async_copies(struct nfs4_client *clp);
|
bool nfsd4_has_active_async_copies(struct nfs4_client *clp);
|
||||||
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
|
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
|
||||||
@@ -842,10 +843,14 @@ struct nfsd_file *find_any_file(struct nfs4_file *f);
|
|||||||
|
|
||||||
#ifdef CONFIG_NFSD_V4
|
#ifdef CONFIG_NFSD_V4
|
||||||
void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb);
|
void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb);
|
||||||
|
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb);
|
||||||
#else
|
#else
|
||||||
static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
|
static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
|
||||||
|
{
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* grace period management */
|
/* grace period management */
|
||||||
|
|||||||
@@ -732,6 +732,7 @@ struct nfsd4_copy {
|
|||||||
#define NFSD4_COPY_F_COMMITTED (3)
|
#define NFSD4_COPY_F_COMMITTED (3)
|
||||||
#define NFSD4_COPY_F_COMPLETED (4)
|
#define NFSD4_COPY_F_COMPLETED (4)
|
||||||
#define NFSD4_COPY_F_OFFLOAD_DONE (5)
|
#define NFSD4_COPY_F_OFFLOAD_DONE (5)
|
||||||
|
#define NFSD4_COPY_F_CB_ERROR (6)
|
||||||
|
|
||||||
/* response */
|
/* response */
|
||||||
__be32 nfserr;
|
__be32 nfserr;
|
||||||
|
|||||||
Reference in New Issue
Block a user