diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 7346acda9d767c2ef3c4bb505b88a85372ffa66c..04360a51a16f93edc03b87411c79c18f5fcb2f57 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 4344b5d983184aa4519b2690bc3ef82368b228e3..7bca44450d27ba893e10b09a41a7e9102aff3101 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1111,6 +1111,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); } @@ -1534,10 +1535,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); @@ -1562,7 +1569,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; if (!copy->cp_intra) nfsd4_interssc_disconnect(copy->ss_mnt); goto out; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 665d0eaeb8dbfccd0bb6fb82c8cc206425c881d2..36d1ee0d78468f531048f18a2939fc7d8a1b35ec 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -7327,6 +7327,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;