diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 02d3d2f0e616886e8bf397c767b5ef70b4c91a0f..8500acac09e0281b9e1b7d1ae0758542c1cb97ff 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -128,6 +128,7 @@ struct nfsd_net { u32 s2s_cp_cl_id; struct idr s2s_cp_stateids; spinlock_t s2s_cp_lock; + atomic_t pending_async_copies; /* * Version information diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 41dc517c934feb7ce57d210c6d4f0fe224d8e00c..cef719ce5edf80197a027e19eab7762899b0c902 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1113,6 +1113,7 @@ void nfs4_put_copy(struct nfsd4_copy *copy) { if (!refcount_dec_and_test(©->refcount)) return; + atomic_dec(©->cp_nn->pending_async_copies); kfree(copy); } @@ -1524,10 +1525,16 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!copy->cp_synchronous) { struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); - status = nfserrno(-ENOMEM); async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); if (!async_copy) goto out_err; + async_copy->cp_nn = nn; + /* Arbitrary cap on number of pending async copy operations */ + if (atomic_inc_return(&nn->pending_async_copies) > + (int)rqstp->rq_pool->sp_nrthreads) { + atomic_dec(&nn->pending_async_copies); + goto out_err; + } if (!nfs4_init_copy_state(nn, copy)) goto out_err; refcount_set(&async_copy->refcount, 1); @@ -1552,7 +1559,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, out_err: if (async_copy) cleanup_async_copy(async_copy); - status = nfserrno(-ENOMEM); + status = nfserr_jukebox; /* * source's vfsmount of inter-copy will be unmounted * by the laundromat diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d402ca0b535f0787f5aa3bdda1608c875db0bfd0..19b277d998fa4e7f209a543b29987cbb6a031139 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -7340,6 +7340,7 @@ static int nfs4_state_create_net(struct net *net) spin_lock_init(&nn->client_lock); spin_lock_init(&nn->s2s_cp_lock); idr_init(&nn->s2s_cp_stateids); + atomic_set(&nn->pending_async_copies, 0); spin_lock_init(&nn->blocked_locks_lock); INIT_LIST_HEAD(&nn->blocked_locks_lru); diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 679d40af1bbb1c252e11ae1171821fbdd9e507f2..1ced060704a3d3f2574ec0a14e24345a31ee5646 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -579,6 +579,7 @@ struct nfsd4_copy { struct vfsmount *ss_mnt; struct nfs_fh c_fh; nfs4_stateid stateid; + struct nfsd_net *cp_nn; }; extern bool inter_copy_offload_enable;