Skip to content

Commit 3daab31

Browse files
committed
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>
1 parent d8316b8 commit 3daab31

5 files changed

Lines changed: 133 additions & 18 deletions

File tree

fs/nfsd/nfs4proc.c

Lines changed: 113 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,14 +1427,26 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
14271427
kfree(copy);
14281428
}
14291429

1430+
static void release_copy_files(struct nfsd4_copy *copy);
1431+
14301432
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
14311433
{
14321434
trace_nfsd_copy_async_cancel(copy);
14331435
if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) {
14341436
kthread_stop(copy->copy_task);
1435-
copy->nfserr = nfs_ok;
1437+
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
1438+
copy->nfserr = nfs_ok;
14361439
set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
14371440
}
1441+
1442+
/*
1443+
* The copy was removed from async_copies before this function
1444+
* was called, so the reaper cannot clean it up. Release files
1445+
* here regardless of who won the STOPPED race. If the thread
1446+
* set STOPPED, it has finished using the files. If STOPPED
1447+
* was set here, kthread_stop() waited for the thread to exit.
1448+
*/
1449+
release_copy_files(copy);
14381450
nfs4_put_copy(copy);
14391451
}
14401452

@@ -1462,6 +1474,72 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp)
14621474
while ((copy = nfsd4_unhash_copy(clp)) != NULL)
14631475
nfsd4_stop_copy(copy);
14641476
}
1477+
1478+
static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy,
1479+
const struct super_block *sb)
1480+
{
1481+
if (copy->nf_src &&
1482+
file_inode(copy->nf_src->nf_file)->i_sb == sb)
1483+
return true;
1484+
if (copy->nf_dst &&
1485+
file_inode(copy->nf_dst->nf_file)->i_sb == sb)
1486+
return true;
1487+
return false;
1488+
}
1489+
1490+
/**
1491+
* nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb
1492+
* @net: net namespace containing the copy operations
1493+
* @sb: targeted superblock
1494+
*/
1495+
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
1496+
{
1497+
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1498+
struct nfsd4_copy *copy, *tmp;
1499+
struct nfs4_client *clp;
1500+
unsigned int idhashval;
1501+
LIST_HEAD(to_cancel);
1502+
1503+
spin_lock(&nn->client_lock);
1504+
for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
1505+
struct list_head *head = &nn->conf_id_hashtbl[idhashval];
1506+
1507+
list_for_each_entry(clp, head, cl_idhash) {
1508+
spin_lock(&clp->async_lock);
1509+
list_for_each_entry_safe(copy, tmp,
1510+
&clp->async_copies, copies) {
1511+
if (nfsd4_copy_on_sb(copy, sb)) {
1512+
refcount_inc(&copy->refcount);
1513+
/*
1514+
* Hold a reference on the client while
1515+
* nfsd4_stop_copy() runs. Unlike
1516+
* nfsd4_unhash_copy(), cp_clp is not
1517+
* NULLed here because nfsd4_send_cb_offload()
1518+
* needs a valid client to send CB_OFFLOAD.
1519+
* That function takes its own reference to
1520+
* survive callback flight.
1521+
*/
1522+
kref_get(&clp->cl_nfsdfs.cl_ref);
1523+
copy->nfserr = nfserr_admin_revoked;
1524+
set_bit(NFSD4_COPY_F_CB_ERROR,
1525+
&copy->cp_flags);
1526+
list_move(&copy->copies, &to_cancel);
1527+
}
1528+
}
1529+
spin_unlock(&clp->async_lock);
1530+
}
1531+
}
1532+
spin_unlock(&nn->client_lock);
1533+
1534+
list_for_each_entry_safe(copy, tmp, &to_cancel, copies) {
1535+
struct nfs4_client *clp = copy->cp_clp;
1536+
1537+
list_del_init(&copy->copies);
1538+
nfsd4_stop_copy(copy);
1539+
nfsd4_put_client(clp);
1540+
}
1541+
}
1542+
14651543
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
14661544

14671545
extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
@@ -1751,6 +1829,7 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
17511829
container_of(cbo, struct nfsd4_copy, cp_cb_offload);
17521830

17531831
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
1832+
nfsd4_put_client(cb->cb_clp);
17541833
}
17551834

17561835
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)
18701949

18711950
static void release_copy_files(struct nfsd4_copy *copy)
18721951
{
1873-
if (copy->nf_src)
1952+
if (copy->nf_src) {
18741953
nfsd_file_put(copy->nf_src);
1875-
if (copy->nf_dst)
1954+
copy->nf_src = NULL;
1955+
}
1956+
if (copy->nf_dst) {
18761957
nfsd_file_put(copy->nf_dst);
1958+
copy->nf_dst = NULL;
1959+
}
18771960
}
18781961

18791962
static void cleanup_async_copy(struct nfsd4_copy *copy)
@@ -1892,18 +1975,34 @@ static void cleanup_async_copy(struct nfsd4_copy *copy)
18921975
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
18931976
{
18941977
struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload;
1978+
struct nfs4_client *clp = copy->cp_clp;
1979+
1980+
/*
1981+
* cp_clp is NULL when called via nfsd4_shutdown_copy() during
1982+
* client destruction. Skip the callback; the client is gone.
1983+
*/
1984+
if (!clp) {
1985+
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
1986+
return;
1987+
}
18951988

18961989
memcpy(&cbo->co_res, &copy->cp_res, sizeof(copy->cp_res));
18971990
memcpy(&cbo->co_fh, &copy->fh, sizeof(copy->fh));
18981991
cbo->co_nfserr = copy->nfserr;
18991992
cbo->co_retries = 5;
19001993

1901-
nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops,
1994+
/*
1995+
* Hold a reference on the client while the callback is in flight.
1996+
* Released in nfsd4_cb_offload_release().
1997+
*/
1998+
kref_get(&clp->cl_nfsdfs.cl_ref);
1999+
2000+
nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops,
19022001
NFSPROC4_CLNT_CB_OFFLOAD);
19032002
nfsd41_cb_referring_call(&cbo->co_cb, &cbo->co_referring_sessionid,
19042003
cbo->co_referring_slotid,
19052004
cbo->co_referring_seqno);
1906-
trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid,
2005+
trace_nfsd_cb_offload(clp, &cbo->co_res.cb_stateid,
19072006
&cbo->co_fh, copy->cp_count, copy->nfserr);
19082007
nfsd4_try_run_cb(&cbo->co_cb);
19092008
}
@@ -1918,6 +2017,7 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
19182017
static int nfsd4_do_async_copy(void *data)
19192018
{
19202019
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
2020+
__be32 nfserr = nfs_ok;
19212021

19222022
trace_nfsd_copy_async(copy);
19232023
if (nfsd4_ssc_is_inter(copy)) {
@@ -1928,23 +2028,25 @@ static int nfsd4_do_async_copy(void *data)
19282028
if (IS_ERR(filp)) {
19292029
switch (PTR_ERR(filp)) {
19302030
case -EBADF:
1931-
copy->nfserr = nfserr_wrong_type;
2031+
nfserr = nfserr_wrong_type;
19322032
break;
19332033
default:
1934-
copy->nfserr = nfserr_offload_denied;
2034+
nfserr = nfserr_offload_denied;
19352035
}
19362036
/* ss_mnt will be unmounted by the laundromat */
19372037
goto do_callback;
19382038
}
1939-
copy->nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
1940-
false);
2039+
nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
2040+
false);
19412041
nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst);
19422042
} else {
1943-
copy->nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
1944-
copy->nf_dst->nf_file, false);
2043+
nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
2044+
copy->nf_dst->nf_file, false);
19452045
}
19462046

19472047
do_callback:
2048+
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
2049+
copy->nfserr = nfserr;
19482050
/* The kthread exits forthwith. Ensure that a subsequent
19492051
* OFFLOAD_CANCEL won't try to kill it again. */
19502052
set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags);

fs/nfsd/nfs4state.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,7 +2413,13 @@ static void __free_client(struct kref *k)
24132413
kmem_cache_free(client_slab, clp);
24142414
}
24152415

2416-
static void drop_client(struct nfs4_client *clp)
2416+
/**
2417+
* nfsd4_put_client - release a reference on an nfs4_client
2418+
* @clp: the client to be released
2419+
*
2420+
* When the last reference is released, the client is freed.
2421+
*/
2422+
void nfsd4_put_client(struct nfs4_client *clp)
24172423
{
24182424
kref_put(&clp->cl_nfsdfs.cl_ref, __free_client);
24192425
}
@@ -2435,7 +2441,7 @@ free_client(struct nfs4_client *clp)
24352441
clp->cl_nfsd_dentry = NULL;
24362442
wake_up_all(&expiry_wq);
24372443
}
2438-
drop_client(clp);
2444+
nfsd4_put_client(clp);
24392445
}
24402446

24412447
/* must be called under the client_lock */
@@ -2833,7 +2839,7 @@ static int client_info_show(struct seq_file *m, void *v)
28332839
spin_unlock(&clp->cl_lock);
28342840
seq_puts(m, "\n");
28352841

2836-
drop_client(clp);
2842+
nfsd4_put_client(clp);
28372843

28382844
return 0;
28392845
}
@@ -3099,7 +3105,7 @@ static int client_states_open(struct inode *inode, struct file *file)
30993105

31003106
ret = seq_open(file, &states_seq_ops);
31013107
if (ret) {
3102-
drop_client(clp);
3108+
nfsd4_put_client(clp);
31033109
return ret;
31043110
}
31053111
s = file->private_data;
@@ -3113,7 +3119,7 @@ static int client_opens_release(struct inode *inode, struct file *file)
31133119
struct nfs4_client *clp = m->private;
31143120

31153121
/* XXX: alternatively, we could get/drop in seq start/stop */
3116-
drop_client(clp);
3122+
nfsd4_put_client(clp);
31173123
return seq_release(inode, file);
31183124
}
31193125

@@ -3169,7 +3175,7 @@ static ssize_t client_ctl_write(struct file *file, const char __user *buf,
31693175
if (!clp)
31703176
return -ENXIO;
31713177
force_expire_client(clp);
3172-
drop_client(clp);
3178+
nfsd4_put_client(clp);
31733179
return 7;
31743180
}
31753181

@@ -3204,7 +3210,7 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
32043210
{
32053211
struct nfs4_client *clp = cb->cb_clp;
32063212

3207-
drop_client(clp);
3213+
nfsd4_put_client(clp);
32083214
}
32093215

32103216
static int

fs/nfsd/nfsctl.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
285285
* 2. Is that directory a mount point, or
286286
* 3. Is that directory the root of an exported file system?
287287
*/
288+
nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb);
288289
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
289290
mutex_lock(&nfsd_mutex);
290291
nn = net_generic(netns(file), nfsd_net_id);

fs/nfsd/state.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ static inline void nfsd4_try_run_cb(struct nfsd4_callback *cb)
822822

823823
extern void nfsd4_shutdown_callback(struct nfs4_client *);
824824
extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
825+
void nfsd4_put_client(struct nfs4_client *clp);
825826
void nfsd4_async_copy_reaper(struct nfsd_net *nn);
826827
bool nfsd4_has_active_async_copies(struct nfs4_client *clp);
827828
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);
842843

843844
#ifdef CONFIG_NFSD_V4
844845
void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb);
846+
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb);
845847
#else
846848
static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
847849
{
848850
}
851+
static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
852+
{
853+
}
849854
#endif
850855

851856
/* grace period management */

fs/nfsd/xdr4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,7 @@ struct nfsd4_copy {
732732
#define NFSD4_COPY_F_COMMITTED (3)
733733
#define NFSD4_COPY_F_COMPLETED (4)
734734
#define NFSD4_COPY_F_OFFLOAD_DONE (5)
735+
#define NFSD4_COPY_F_CB_ERROR (6)
735736

736737
/* response */
737738
__be32 nfserr;

0 commit comments

Comments
 (0)