diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-04-23 16:40:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-04-23 16:40:55 -0400 |
commit | 12a54b150fb5b6c2f3da932dc0e665355f8a5a48 (patch) | |
tree | fa5d6becf0392244d4a44cc6afdbc93e363689d1 | |
parent | d286e13d53f54b00bcd7443eedd067cd432cf547 (diff) | |
parent | f456458e4d25a8962d0946891617c76cc3ff5fb9 (diff) |
Merge tag 'nfsd-5.1-1' of git://linux-nfs.org/~bfields/linux
Pull nfsd bugfixes from Bruce Fields:
"Fix miscellaneous nfsd bugs, in NFSv4.1 callbacks, NFSv4.1
lock-notification callbacks, NFSv3 readdir encoding, and the
cache/upcall code"
* tag 'nfsd-5.1-1' of git://linux-nfs.org/~bfields/linux:
nfsd: wake blocked file lock waiters before sending callback
nfsd: wake waiters blocked on file_lock before deleting it
nfsd: Don't release the callback slot unless it was actually held
nfsd/nfsd3_proc_readdir: fix buffer count and page pointers
sunrpc: don't mark uninitialised items as VALID.
-rw-r--r-- | fs/nfsd/nfs3proc.c | 17 | ||||
-rw-r--r-- | fs/nfsd/nfs3xdr.c | 11 | ||||
-rw-r--r-- | fs/nfsd/nfs4callback.c | 8 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 12 | ||||
-rw-r--r-- | fs/nfsd/state.h | 1 | ||||
-rw-r--r-- | net/sunrpc/cache.c | 3 |
6 files changed, 45 insertions, 7 deletions
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 8f933e84cec1..9bc32af4e2da 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c | |||
@@ -442,7 +442,9 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp) | |||
442 | struct nfsd3_readdirargs *argp = rqstp->rq_argp; | 442 | struct nfsd3_readdirargs *argp = rqstp->rq_argp; |
443 | struct nfsd3_readdirres *resp = rqstp->rq_resp; | 443 | struct nfsd3_readdirres *resp = rqstp->rq_resp; |
444 | __be32 nfserr; | 444 | __be32 nfserr; |
445 | int count; | 445 | int count = 0; |
446 | struct page **p; | ||
447 | caddr_t page_addr = NULL; | ||
446 | 448 | ||
447 | dprintk("nfsd: READDIR(3) %s %d bytes at %d\n", | 449 | dprintk("nfsd: READDIR(3) %s %d bytes at %d\n", |
448 | SVCFH_fmt(&argp->fh), | 450 | SVCFH_fmt(&argp->fh), |
@@ -462,7 +464,18 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp) | |||
462 | nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, | 464 | nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, |
463 | &resp->common, nfs3svc_encode_entry); | 465 | &resp->common, nfs3svc_encode_entry); |
464 | memcpy(resp->verf, argp->verf, 8); | 466 | memcpy(resp->verf, argp->verf, 8); |
465 | resp->count = resp->buffer - argp->buffer; | 467 | count = 0; |
468 | for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) { | ||
469 | page_addr = page_address(*p); | ||
470 | |||
471 | if (((caddr_t)resp->buffer >= page_addr) && | ||
472 | ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) { | ||
473 | count += (caddr_t)resp->buffer - page_addr; | ||
474 | break; | ||
475 | } | ||
476 | count += PAGE_SIZE; | ||
477 | } | ||
478 | resp->count = count >> 2; | ||
466 | if (resp->offset) { | 479 | if (resp->offset) { |
467 | loff_t offset = argp->cookie; | 480 | loff_t offset = argp->cookie; |
468 | 481 | ||
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 93fea246f676..8d789124ed3c 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c | |||
@@ -573,6 +573,7 @@ int | |||
573 | nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) | 573 | nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) |
574 | { | 574 | { |
575 | struct nfsd3_readdirargs *args = rqstp->rq_argp; | 575 | struct nfsd3_readdirargs *args = rqstp->rq_argp; |
576 | int len; | ||
576 | u32 max_blocksize = svc_max_payload(rqstp); | 577 | u32 max_blocksize = svc_max_payload(rqstp); |
577 | 578 | ||
578 | p = decode_fh(p, &args->fh); | 579 | p = decode_fh(p, &args->fh); |
@@ -582,8 +583,14 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) | |||
582 | args->verf = p; p += 2; | 583 | args->verf = p; p += 2; |
583 | args->dircount = ~0; | 584 | args->dircount = ~0; |
584 | args->count = ntohl(*p++); | 585 | args->count = ntohl(*p++); |
585 | args->count = min_t(u32, args->count, max_blocksize); | 586 | len = args->count = min_t(u32, args->count, max_blocksize); |
586 | args->buffer = page_address(*(rqstp->rq_next_page++)); | 587 | |
588 | while (len > 0) { | ||
589 | struct page *p = *(rqstp->rq_next_page++); | ||
590 | if (!args->buffer) | ||
591 | args->buffer = page_address(p); | ||
592 | len -= PAGE_SIZE; | ||
593 | } | ||
587 | 594 | ||
588 | return xdr_argsize_check(rqstp, p); | 595 | return xdr_argsize_check(rqstp, p); |
589 | } | 596 | } |
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index d219159b98af..7caa3801ce72 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -1010,8 +1010,9 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) | |||
1010 | cb->cb_seq_status = 1; | 1010 | cb->cb_seq_status = 1; |
1011 | cb->cb_status = 0; | 1011 | cb->cb_status = 0; |
1012 | if (minorversion) { | 1012 | if (minorversion) { |
1013 | if (!nfsd41_cb_get_slot(clp, task)) | 1013 | if (!cb->cb_holds_slot && !nfsd41_cb_get_slot(clp, task)) |
1014 | return; | 1014 | return; |
1015 | cb->cb_holds_slot = true; | ||
1015 | } | 1016 | } |
1016 | rpc_call_start(task); | 1017 | rpc_call_start(task); |
1017 | } | 1018 | } |
@@ -1038,6 +1039,9 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback | |||
1038 | return true; | 1039 | return true; |
1039 | } | 1040 | } |
1040 | 1041 | ||
1042 | if (!cb->cb_holds_slot) | ||
1043 | goto need_restart; | ||
1044 | |||
1041 | switch (cb->cb_seq_status) { | 1045 | switch (cb->cb_seq_status) { |
1042 | case 0: | 1046 | case 0: |
1043 | /* | 1047 | /* |
@@ -1076,6 +1080,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback | |||
1076 | cb->cb_seq_status); | 1080 | cb->cb_seq_status); |
1077 | } | 1081 | } |
1078 | 1082 | ||
1083 | cb->cb_holds_slot = false; | ||
1079 | clear_bit(0, &clp->cl_cb_slot_busy); | 1084 | clear_bit(0, &clp->cl_cb_slot_busy); |
1080 | rpc_wake_up_next(&clp->cl_cb_waitq); | 1085 | rpc_wake_up_next(&clp->cl_cb_waitq); |
1081 | dprintk("%s: freed slot, new seqid=%d\n", __func__, | 1086 | dprintk("%s: freed slot, new seqid=%d\n", __func__, |
@@ -1283,6 +1288,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, | |||
1283 | cb->cb_seq_status = 1; | 1288 | cb->cb_seq_status = 1; |
1284 | cb->cb_status = 0; | 1289 | cb->cb_status = 0; |
1285 | cb->cb_need_restart = false; | 1290 | cb->cb_need_restart = false; |
1291 | cb->cb_holds_slot = false; | ||
1286 | } | 1292 | } |
1287 | 1293 | ||
1288 | void nfsd4_run_cb(struct nfsd4_callback *cb) | 1294 | void nfsd4_run_cb(struct nfsd4_callback *cb) |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6a45fb00c5fc..f056b1d3fecd 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -265,6 +265,7 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh, | |||
265 | static void | 265 | static void |
266 | free_blocked_lock(struct nfsd4_blocked_lock *nbl) | 266 | free_blocked_lock(struct nfsd4_blocked_lock *nbl) |
267 | { | 267 | { |
268 | locks_delete_block(&nbl->nbl_lock); | ||
268 | locks_release_private(&nbl->nbl_lock); | 269 | locks_release_private(&nbl->nbl_lock); |
269 | kfree(nbl); | 270 | kfree(nbl); |
270 | } | 271 | } |
@@ -293,11 +294,18 @@ remove_blocked_locks(struct nfs4_lockowner *lo) | |||
293 | nbl = list_first_entry(&reaplist, struct nfsd4_blocked_lock, | 294 | nbl = list_first_entry(&reaplist, struct nfsd4_blocked_lock, |
294 | nbl_lru); | 295 | nbl_lru); |
295 | list_del_init(&nbl->nbl_lru); | 296 | list_del_init(&nbl->nbl_lru); |
296 | locks_delete_block(&nbl->nbl_lock); | ||
297 | free_blocked_lock(nbl); | 297 | free_blocked_lock(nbl); |
298 | } | 298 | } |
299 | } | 299 | } |
300 | 300 | ||
301 | static void | ||
302 | nfsd4_cb_notify_lock_prepare(struct nfsd4_callback *cb) | ||
303 | { | ||
304 | struct nfsd4_blocked_lock *nbl = container_of(cb, | ||
305 | struct nfsd4_blocked_lock, nbl_cb); | ||
306 | locks_delete_block(&nbl->nbl_lock); | ||
307 | } | ||
308 | |||
301 | static int | 309 | static int |
302 | nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task) | 310 | nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task) |
303 | { | 311 | { |
@@ -325,6 +333,7 @@ nfsd4_cb_notify_lock_release(struct nfsd4_callback *cb) | |||
325 | } | 333 | } |
326 | 334 | ||
327 | static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = { | 335 | static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = { |
336 | .prepare = nfsd4_cb_notify_lock_prepare, | ||
328 | .done = nfsd4_cb_notify_lock_done, | 337 | .done = nfsd4_cb_notify_lock_done, |
329 | .release = nfsd4_cb_notify_lock_release, | 338 | .release = nfsd4_cb_notify_lock_release, |
330 | }; | 339 | }; |
@@ -4863,7 +4872,6 @@ nfs4_laundromat(struct nfsd_net *nn) | |||
4863 | nbl = list_first_entry(&reaplist, | 4872 | nbl = list_first_entry(&reaplist, |
4864 | struct nfsd4_blocked_lock, nbl_lru); | 4873 | struct nfsd4_blocked_lock, nbl_lru); |
4865 | list_del_init(&nbl->nbl_lru); | 4874 | list_del_init(&nbl->nbl_lru); |
4866 | locks_delete_block(&nbl->nbl_lock); | ||
4867 | free_blocked_lock(nbl); | 4875 | free_blocked_lock(nbl); |
4868 | } | 4876 | } |
4869 | out: | 4877 | out: |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 396c76755b03..9d6cb246c6c5 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -70,6 +70,7 @@ struct nfsd4_callback { | |||
70 | int cb_seq_status; | 70 | int cb_seq_status; |
71 | int cb_status; | 71 | int cb_status; |
72 | bool cb_need_restart; | 72 | bool cb_need_restart; |
73 | bool cb_holds_slot; | ||
73 | }; | 74 | }; |
74 | 75 | ||
75 | struct nfsd4_callback_ops { | 76 | struct nfsd4_callback_ops { |
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 12bb23b8e0c5..261131dfa1f1 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c | |||
@@ -54,6 +54,7 @@ static void cache_init(struct cache_head *h, struct cache_detail *detail) | |||
54 | h->last_refresh = now; | 54 | h->last_refresh = now; |
55 | } | 55 | } |
56 | 56 | ||
57 | static inline int cache_is_valid(struct cache_head *h); | ||
57 | static void cache_fresh_locked(struct cache_head *head, time_t expiry, | 58 | static void cache_fresh_locked(struct cache_head *head, time_t expiry, |
58 | struct cache_detail *detail); | 59 | struct cache_detail *detail); |
59 | static void cache_fresh_unlocked(struct cache_head *head, | 60 | static void cache_fresh_unlocked(struct cache_head *head, |
@@ -105,6 +106,8 @@ static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail, | |||
105 | if (cache_is_expired(detail, tmp)) { | 106 | if (cache_is_expired(detail, tmp)) { |
106 | hlist_del_init_rcu(&tmp->cache_list); | 107 | hlist_del_init_rcu(&tmp->cache_list); |
107 | detail->entries --; | 108 | detail->entries --; |
109 | if (cache_is_valid(tmp) == -EAGAIN) | ||
110 | set_bit(CACHE_NEGATIVE, &tmp->flags); | ||
108 | cache_fresh_locked(tmp, 0, detail); | 111 | cache_fresh_locked(tmp, 0, detail); |
109 | freeme = tmp; | 112 | freeme = tmp; |
110 | break; | 113 | break; |