diff options
Diffstat (limited to 'fs/lockd/clntproc.c')
-rw-r--r-- | fs/lockd/clntproc.c | 184 |
1 files changed, 117 insertions, 67 deletions
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index b6b74a60e1eb..40b16f23e49a 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c | |||
@@ -155,8 +155,6 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req) | |||
155 | int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) | 155 | int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) |
156 | { | 156 | { |
157 | struct nlm_rqst *call; | 157 | struct nlm_rqst *call; |
158 | sigset_t oldset; | ||
159 | unsigned long flags; | ||
160 | int status; | 158 | int status; |
161 | 159 | ||
162 | nlm_get_host(host); | 160 | nlm_get_host(host); |
@@ -168,22 +166,6 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) | |||
168 | /* Set up the argument struct */ | 166 | /* Set up the argument struct */ |
169 | nlmclnt_setlockargs(call, fl); | 167 | nlmclnt_setlockargs(call, fl); |
170 | 168 | ||
171 | /* Keep the old signal mask */ | ||
172 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
173 | oldset = current->blocked; | ||
174 | |||
175 | /* If we're cleaning up locks because the process is exiting, | ||
176 | * perform the RPC call asynchronously. */ | ||
177 | if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) | ||
178 | && fl->fl_type == F_UNLCK | ||
179 | && (current->flags & PF_EXITING)) { | ||
180 | sigfillset(¤t->blocked); /* Mask all signals */ | ||
181 | recalc_sigpending(); | ||
182 | |||
183 | call->a_flags = RPC_TASK_ASYNC; | ||
184 | } | ||
185 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
186 | |||
187 | if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { | 169 | if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { |
188 | if (fl->fl_type != F_UNLCK) { | 170 | if (fl->fl_type != F_UNLCK) { |
189 | call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; | 171 | call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; |
@@ -198,11 +180,6 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl) | |||
198 | fl->fl_ops->fl_release_private(fl); | 180 | fl->fl_ops->fl_release_private(fl); |
199 | fl->fl_ops = NULL; | 181 | fl->fl_ops = NULL; |
200 | 182 | ||
201 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
202 | current->blocked = oldset; | ||
203 | recalc_sigpending(); | ||
204 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
205 | |||
206 | dprintk("lockd: clnt proc returns %d\n", status); | 183 | dprintk("lockd: clnt proc returns %d\n", status); |
207 | return status; | 184 | return status; |
208 | } | 185 | } |
@@ -221,6 +198,7 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host) | |||
221 | for(;;) { | 198 | for(;;) { |
222 | call = kzalloc(sizeof(*call), GFP_KERNEL); | 199 | call = kzalloc(sizeof(*call), GFP_KERNEL); |
223 | if (call != NULL) { | 200 | if (call != NULL) { |
201 | atomic_set(&call->a_count, 1); | ||
224 | locks_init_lock(&call->a_args.lock.fl); | 202 | locks_init_lock(&call->a_args.lock.fl); |
225 | locks_init_lock(&call->a_res.lock.fl); | 203 | locks_init_lock(&call->a_res.lock.fl); |
226 | call->a_host = host; | 204 | call->a_host = host; |
@@ -237,6 +215,8 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host) | |||
237 | 215 | ||
238 | void nlm_release_call(struct nlm_rqst *call) | 216 | void nlm_release_call(struct nlm_rqst *call) |
239 | { | 217 | { |
218 | if (!atomic_dec_and_test(&call->a_count)) | ||
219 | return; | ||
240 | nlm_release_host(call->a_host); | 220 | nlm_release_host(call->a_host); |
241 | nlmclnt_release_lockargs(call); | 221 | nlmclnt_release_lockargs(call); |
242 | kfree(call); | 222 | kfree(call); |
@@ -267,7 +247,7 @@ static int nlm_wait_on_grace(wait_queue_head_t *queue) | |||
267 | * Generic NLM call | 247 | * Generic NLM call |
268 | */ | 248 | */ |
269 | static int | 249 | static int |
270 | nlmclnt_call(struct nlm_rqst *req, u32 proc) | 250 | nlmclnt_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc) |
271 | { | 251 | { |
272 | struct nlm_host *host = req->a_host; | 252 | struct nlm_host *host = req->a_host; |
273 | struct rpc_clnt *clnt; | 253 | struct rpc_clnt *clnt; |
@@ -276,6 +256,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) | |||
276 | struct rpc_message msg = { | 256 | struct rpc_message msg = { |
277 | .rpc_argp = argp, | 257 | .rpc_argp = argp, |
278 | .rpc_resp = resp, | 258 | .rpc_resp = resp, |
259 | .rpc_cred = cred, | ||
279 | }; | 260 | }; |
280 | int status; | 261 | int status; |
281 | 262 | ||
@@ -343,10 +324,16 @@ in_grace_period: | |||
343 | /* | 324 | /* |
344 | * Generic NLM call, async version. | 325 | * Generic NLM call, async version. |
345 | */ | 326 | */ |
346 | static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) | 327 | static struct rpc_task *__nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) |
347 | { | 328 | { |
348 | struct nlm_host *host = req->a_host; | 329 | struct nlm_host *host = req->a_host; |
349 | struct rpc_clnt *clnt; | 330 | struct rpc_clnt *clnt; |
331 | struct rpc_task_setup task_setup_data = { | ||
332 | .rpc_message = msg, | ||
333 | .callback_ops = tk_ops, | ||
334 | .callback_data = req, | ||
335 | .flags = RPC_TASK_ASYNC, | ||
336 | }; | ||
350 | 337 | ||
351 | dprintk("lockd: call procedure %d on %s (async)\n", | 338 | dprintk("lockd: call procedure %d on %s (async)\n", |
352 | (int)proc, host->h_name); | 339 | (int)proc, host->h_name); |
@@ -356,21 +343,36 @@ static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message * | |||
356 | if (clnt == NULL) | 343 | if (clnt == NULL) |
357 | goto out_err; | 344 | goto out_err; |
358 | msg->rpc_proc = &clnt->cl_procinfo[proc]; | 345 | msg->rpc_proc = &clnt->cl_procinfo[proc]; |
346 | task_setup_data.rpc_client = clnt; | ||
359 | 347 | ||
360 | /* bootstrap and kick off the async RPC call */ | 348 | /* bootstrap and kick off the async RPC call */ |
361 | return rpc_call_async(clnt, msg, RPC_TASK_ASYNC, tk_ops, req); | 349 | return rpc_run_task(&task_setup_data); |
362 | out_err: | 350 | out_err: |
363 | tk_ops->rpc_release(req); | 351 | tk_ops->rpc_release(req); |
364 | return -ENOLCK; | 352 | return ERR_PTR(-ENOLCK); |
365 | } | 353 | } |
366 | 354 | ||
355 | static int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops) | ||
356 | { | ||
357 | struct rpc_task *task; | ||
358 | |||
359 | task = __nlm_async_call(req, proc, msg, tk_ops); | ||
360 | if (IS_ERR(task)) | ||
361 | return PTR_ERR(task); | ||
362 | rpc_put_task(task); | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * NLM asynchronous call. | ||
368 | */ | ||
367 | int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) | 369 | int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) |
368 | { | 370 | { |
369 | struct rpc_message msg = { | 371 | struct rpc_message msg = { |
370 | .rpc_argp = &req->a_args, | 372 | .rpc_argp = &req->a_args, |
371 | .rpc_resp = &req->a_res, | 373 | .rpc_resp = &req->a_res, |
372 | }; | 374 | }; |
373 | return __nlm_async_call(req, proc, &msg, tk_ops); | 375 | return nlm_do_async_call(req, proc, &msg, tk_ops); |
374 | } | 376 | } |
375 | 377 | ||
376 | int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) | 378 | int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) |
@@ -378,7 +380,33 @@ int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *t | |||
378 | struct rpc_message msg = { | 380 | struct rpc_message msg = { |
379 | .rpc_argp = &req->a_res, | 381 | .rpc_argp = &req->a_res, |
380 | }; | 382 | }; |
381 | return __nlm_async_call(req, proc, &msg, tk_ops); | 383 | return nlm_do_async_call(req, proc, &msg, tk_ops); |
384 | } | ||
385 | |||
386 | /* | ||
387 | * NLM client asynchronous call. | ||
388 | * | ||
389 | * Note that although the calls are asynchronous, and are therefore | ||
390 | * guaranteed to complete, we still always attempt to wait for | ||
391 | * completion in order to be able to correctly track the lock | ||
392 | * state. | ||
393 | */ | ||
394 | static int nlmclnt_async_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) | ||
395 | { | ||
396 | struct rpc_message msg = { | ||
397 | .rpc_argp = &req->a_args, | ||
398 | .rpc_resp = &req->a_res, | ||
399 | .rpc_cred = cred, | ||
400 | }; | ||
401 | struct rpc_task *task; | ||
402 | int err; | ||
403 | |||
404 | task = __nlm_async_call(req, proc, &msg, tk_ops); | ||
405 | if (IS_ERR(task)) | ||
406 | return PTR_ERR(task); | ||
407 | err = rpc_wait_for_completion_task(task); | ||
408 | rpc_put_task(task); | ||
409 | return err; | ||
382 | } | 410 | } |
383 | 411 | ||
384 | /* | 412 | /* |
@@ -389,7 +417,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) | |||
389 | { | 417 | { |
390 | int status; | 418 | int status; |
391 | 419 | ||
392 | status = nlmclnt_call(req, NLMPROC_TEST); | 420 | status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST); |
393 | if (status < 0) | 421 | if (status < 0) |
394 | goto out; | 422 | goto out; |
395 | 423 | ||
@@ -480,10 +508,12 @@ static int do_vfs_lock(struct file_lock *fl) | |||
480 | static int | 508 | static int |
481 | nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) | 509 | nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) |
482 | { | 510 | { |
511 | struct rpc_cred *cred = nfs_file_cred(fl->fl_file); | ||
483 | struct nlm_host *host = req->a_host; | 512 | struct nlm_host *host = req->a_host; |
484 | struct nlm_res *resp = &req->a_res; | 513 | struct nlm_res *resp = &req->a_res; |
485 | struct nlm_wait *block = NULL; | 514 | struct nlm_wait *block = NULL; |
486 | unsigned char fl_flags = fl->fl_flags; | 515 | unsigned char fl_flags = fl->fl_flags; |
516 | unsigned char fl_type; | ||
487 | int status = -ENOLCK; | 517 | int status = -ENOLCK; |
488 | 518 | ||
489 | if (nsm_monitor(host) < 0) { | 519 | if (nsm_monitor(host) < 0) { |
@@ -493,18 +523,22 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) | |||
493 | } | 523 | } |
494 | fl->fl_flags |= FL_ACCESS; | 524 | fl->fl_flags |= FL_ACCESS; |
495 | status = do_vfs_lock(fl); | 525 | status = do_vfs_lock(fl); |
526 | fl->fl_flags = fl_flags; | ||
496 | if (status < 0) | 527 | if (status < 0) |
497 | goto out; | 528 | goto out; |
498 | 529 | ||
499 | block = nlmclnt_prepare_block(host, fl); | 530 | block = nlmclnt_prepare_block(host, fl); |
500 | again: | 531 | again: |
532 | /* | ||
533 | * Initialise resp->status to a valid non-zero value, | ||
534 | * since 0 == nlm_lck_granted | ||
535 | */ | ||
536 | resp->status = nlm_lck_blocked; | ||
501 | for(;;) { | 537 | for(;;) { |
502 | /* Reboot protection */ | 538 | /* Reboot protection */ |
503 | fl->fl_u.nfs_fl.state = host->h_state; | 539 | fl->fl_u.nfs_fl.state = host->h_state; |
504 | status = nlmclnt_call(req, NLMPROC_LOCK); | 540 | status = nlmclnt_call(cred, req, NLMPROC_LOCK); |
505 | if (status < 0) | 541 | if (status < 0) |
506 | goto out_unblock; | ||
507 | if (!req->a_args.block) | ||
508 | break; | 542 | break; |
509 | /* Did a reclaimer thread notify us of a server reboot? */ | 543 | /* Did a reclaimer thread notify us of a server reboot? */ |
510 | if (resp->status == nlm_lck_denied_grace_period) | 544 | if (resp->status == nlm_lck_denied_grace_period) |
@@ -513,15 +547,22 @@ again: | |||
513 | break; | 547 | break; |
514 | /* Wait on an NLM blocking lock */ | 548 | /* Wait on an NLM blocking lock */ |
515 | status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT); | 549 | status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT); |
516 | /* if we were interrupted. Send a CANCEL request to the server | ||
517 | * and exit | ||
518 | */ | ||
519 | if (status < 0) | 550 | if (status < 0) |
520 | goto out_unblock; | 551 | break; |
521 | if (resp->status != nlm_lck_blocked) | 552 | if (resp->status != nlm_lck_blocked) |
522 | break; | 553 | break; |
523 | } | 554 | } |
524 | 555 | ||
556 | /* if we were interrupted while blocking, then cancel the lock request | ||
557 | * and exit | ||
558 | */ | ||
559 | if (resp->status == nlm_lck_blocked) { | ||
560 | if (!req->a_args.block) | ||
561 | goto out_unlock; | ||
562 | if (nlmclnt_cancel(host, req->a_args.block, fl) == 0) | ||
563 | goto out_unblock; | ||
564 | } | ||
565 | |||
525 | if (resp->status == nlm_granted) { | 566 | if (resp->status == nlm_granted) { |
526 | down_read(&host->h_rwsem); | 567 | down_read(&host->h_rwsem); |
527 | /* Check whether or not the server has rebooted */ | 568 | /* Check whether or not the server has rebooted */ |
@@ -530,20 +571,34 @@ again: | |||
530 | goto again; | 571 | goto again; |
531 | } | 572 | } |
532 | /* Ensure the resulting lock will get added to granted list */ | 573 | /* Ensure the resulting lock will get added to granted list */ |
533 | fl->fl_flags = fl_flags | FL_SLEEP; | 574 | fl->fl_flags |= FL_SLEEP; |
534 | if (do_vfs_lock(fl) < 0) | 575 | if (do_vfs_lock(fl) < 0) |
535 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); | 576 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); |
536 | up_read(&host->h_rwsem); | 577 | up_read(&host->h_rwsem); |
578 | fl->fl_flags = fl_flags; | ||
579 | status = 0; | ||
537 | } | 580 | } |
581 | if (status < 0) | ||
582 | goto out_unlock; | ||
538 | status = nlm_stat_to_errno(resp->status); | 583 | status = nlm_stat_to_errno(resp->status); |
539 | out_unblock: | 584 | out_unblock: |
540 | nlmclnt_finish_block(block); | 585 | nlmclnt_finish_block(block); |
541 | /* Cancel the blocked request if it is still pending */ | ||
542 | if (resp->status == nlm_lck_blocked) | ||
543 | nlmclnt_cancel(host, req->a_args.block, fl); | ||
544 | out: | 586 | out: |
545 | nlm_release_call(req); | 587 | nlm_release_call(req); |
588 | return status; | ||
589 | out_unlock: | ||
590 | /* Fatal error: ensure that we remove the lock altogether */ | ||
591 | dprintk("lockd: lock attempt ended in fatal error.\n" | ||
592 | " Attempting to unlock.\n"); | ||
593 | nlmclnt_finish_block(block); | ||
594 | fl_type = fl->fl_type; | ||
595 | fl->fl_type = F_UNLCK; | ||
596 | down_read(&host->h_rwsem); | ||
597 | do_vfs_lock(fl); | ||
598 | up_read(&host->h_rwsem); | ||
599 | fl->fl_type = fl_type; | ||
546 | fl->fl_flags = fl_flags; | 600 | fl->fl_flags = fl_flags; |
601 | nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); | ||
547 | return status; | 602 | return status; |
548 | } | 603 | } |
549 | 604 | ||
@@ -567,8 +622,8 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl) | |||
567 | nlmclnt_setlockargs(req, fl); | 622 | nlmclnt_setlockargs(req, fl); |
568 | req->a_args.reclaim = 1; | 623 | req->a_args.reclaim = 1; |
569 | 624 | ||
570 | if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0 | 625 | status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK); |
571 | && req->a_res.status == nlm_granted) | 626 | if (status >= 0 && req->a_res.status == nlm_granted) |
572 | return 0; | 627 | return 0; |
573 | 628 | ||
574 | printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " | 629 | printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " |
@@ -598,7 +653,8 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) | |||
598 | { | 653 | { |
599 | struct nlm_host *host = req->a_host; | 654 | struct nlm_host *host = req->a_host; |
600 | struct nlm_res *resp = &req->a_res; | 655 | struct nlm_res *resp = &req->a_res; |
601 | int status = 0; | 656 | int status; |
657 | unsigned char fl_flags = fl->fl_flags; | ||
602 | 658 | ||
603 | /* | 659 | /* |
604 | * Note: the server is supposed to either grant us the unlock | 660 | * Note: the server is supposed to either grant us the unlock |
@@ -607,16 +663,17 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) | |||
607 | */ | 663 | */ |
608 | fl->fl_flags |= FL_EXISTS; | 664 | fl->fl_flags |= FL_EXISTS; |
609 | down_read(&host->h_rwsem); | 665 | down_read(&host->h_rwsem); |
610 | if (do_vfs_lock(fl) == -ENOENT) { | 666 | status = do_vfs_lock(fl); |
611 | up_read(&host->h_rwsem); | 667 | up_read(&host->h_rwsem); |
668 | fl->fl_flags = fl_flags; | ||
669 | if (status == -ENOENT) { | ||
670 | status = 0; | ||
612 | goto out; | 671 | goto out; |
613 | } | 672 | } |
614 | up_read(&host->h_rwsem); | ||
615 | |||
616 | if (req->a_flags & RPC_TASK_ASYNC) | ||
617 | return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); | ||
618 | 673 | ||
619 | status = nlmclnt_call(req, NLMPROC_UNLOCK); | 674 | atomic_inc(&req->a_count); |
675 | status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, | ||
676 | NLMPROC_UNLOCK, &nlmclnt_unlock_ops); | ||
620 | if (status < 0) | 677 | if (status < 0) |
621 | goto out; | 678 | goto out; |
622 | 679 | ||
@@ -671,16 +728,10 @@ static const struct rpc_call_ops nlmclnt_unlock_ops = { | |||
671 | static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl) | 728 | static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl) |
672 | { | 729 | { |
673 | struct nlm_rqst *req; | 730 | struct nlm_rqst *req; |
674 | unsigned long flags; | 731 | int status; |
675 | sigset_t oldset; | ||
676 | int status; | ||
677 | 732 | ||
678 | /* Block all signals while setting up call */ | 733 | dprintk("lockd: blocking lock attempt was interrupted by a signal.\n" |
679 | spin_lock_irqsave(¤t->sighand->siglock, flags); | 734 | " Attempting to cancel lock.\n"); |
680 | oldset = current->blocked; | ||
681 | sigfillset(¤t->blocked); | ||
682 | recalc_sigpending(); | ||
683 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
684 | 735 | ||
685 | req = nlm_alloc_call(nlm_get_host(host)); | 736 | req = nlm_alloc_call(nlm_get_host(host)); |
686 | if (!req) | 737 | if (!req) |
@@ -690,13 +741,12 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl | |||
690 | nlmclnt_setlockargs(req, fl); | 741 | nlmclnt_setlockargs(req, fl); |
691 | req->a_args.block = block; | 742 | req->a_args.block = block; |
692 | 743 | ||
693 | status = nlm_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops); | 744 | atomic_inc(&req->a_count); |
694 | 745 | status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req, | |
695 | spin_lock_irqsave(¤t->sighand->siglock, flags); | 746 | NLMPROC_CANCEL, &nlmclnt_cancel_ops); |
696 | current->blocked = oldset; | 747 | if (status == 0 && req->a_res.status == nlm_lck_denied) |
697 | recalc_sigpending(); | 748 | status = -ENOLCK; |
698 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | 749 | nlm_release_call(req); |
699 | |||
700 | return status; | 750 | return status; |
701 | } | 751 | } |
702 | 752 | ||