Skip to content

Commit aadc3bb

Browse files
committed
NFSD: Limit the number of concurrent async COPY operations
Nothing appears to limit the number of concurrent async COPY operations that clients can start. In addition, AFAICT each async COPY can copy an unlimited number of 4MB chunks, so can run for a long time. Thus IMO async COPY can become a DoS vector. Add a restriction mechanism that bounds the number of concurrent background COPY operations. Start simple and try to be fair -- this patch implements a per-namespace limit. An async COPY request that occurs while this limit is exceeded gets NFS4ERR_DELAY. The requesting client can choose to send the request again after a delay or fall back to a traditional read/write style copy. If there is need to make the mechanism more sophisticated, we can visit that in future patches. Cc: [email protected] Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 9ed666e commit aadc3bb

File tree

4 files changed

+12
-2
lines changed

4 files changed

+12
-2
lines changed

fs/nfsd/netns.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ struct nfsd_net {
148148
u32 s2s_cp_cl_id;
149149
struct idr s2s_cp_stateids;
150150
spinlock_t s2s_cp_lock;
151+
atomic_t pending_async_copies;
151152

152153
/*
153154
* Version information

fs/nfsd/nfs4proc.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,7 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
12801280
{
12811281
if (!refcount_dec_and_test(&copy->refcount))
12821282
return;
1283+
atomic_dec(&copy->cp_nn->pending_async_copies);
12831284
kfree(copy->cp_src);
12841285
kfree(copy);
12851286
}
@@ -1835,10 +1836,16 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
18351836
memcpy(&copy->fh, &cstate->current_fh.fh_handle,
18361837
sizeof(struct knfsd_fh));
18371838
if (nfsd4_copy_is_async(copy)) {
1838-
status = nfserrno(-ENOMEM);
18391839
async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
18401840
if (!async_copy)
18411841
goto out_err;
1842+
async_copy->cp_nn = nn;
1843+
/* Arbitrary cap on number of pending async copy operations */
1844+
if (atomic_inc_return(&nn->pending_async_copies) >
1845+
(int)rqstp->rq_pool->sp_nrthreads) {
1846+
atomic_dec(&nn->pending_async_copies);
1847+
goto out_err;
1848+
}
18421849
INIT_LIST_HEAD(&async_copy->copies);
18431850
refcount_set(&async_copy->refcount, 1);
18441851
async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL);
@@ -1878,7 +1885,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
18781885
}
18791886
if (async_copy)
18801887
cleanup_async_copy(async_copy);
1881-
status = nfserrno(-ENOMEM);
1888+
status = nfserr_jukebox;
18821889
goto out;
18831890
}
18841891

fs/nfsd/nfs4state.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8554,6 +8554,7 @@ static int nfs4_state_create_net(struct net *net)
85548554
spin_lock_init(&nn->client_lock);
85558555
spin_lock_init(&nn->s2s_cp_lock);
85568556
idr_init(&nn->s2s_cp_stateids);
8557+
atomic_set(&nn->pending_async_copies, 0);
85578558

85588559
spin_lock_init(&nn->blocked_locks_lock);
85598560
INIT_LIST_HEAD(&nn->blocked_locks_lru);

fs/nfsd/xdr4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ struct nfsd4_copy {
713713
struct nfsd4_ssc_umount_item *ss_nsui;
714714
struct nfs_fh c_fh;
715715
nfs4_stateid stateid;
716+
struct nfsd_net *cp_nn;
716717
};
717718

718719
static inline void nfsd4_copy_set_sync(struct nfsd4_copy *copy, bool sync)

0 commit comments

Comments
 (0)