diff options
Diffstat (limited to 'fs/afs/flock.c')
-rw-r--r-- | fs/afs/flock.c | 49 |
1 files changed, 42 insertions, 7 deletions
diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 325bf731d8dd..ef313f4c1d11 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c | |||
@@ -409,7 +409,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode) | |||
409 | * whether we think that we have a locking permit. | 409 | * whether we think that we have a locking permit. |
410 | */ | 410 | */ |
411 | static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, | 411 | static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, |
412 | afs_lock_type_t type, bool can_sleep) | 412 | enum afs_flock_mode mode, afs_lock_type_t type) |
413 | { | 413 | { |
414 | afs_access_t access; | 414 | afs_access_t access; |
415 | int ret; | 415 | int ret; |
@@ -437,13 +437,9 @@ static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, | |||
437 | if (type == AFS_LOCK_READ) { | 437 | if (type == AFS_LOCK_READ) { |
438 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK))) | 438 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK))) |
439 | return -EACCES; | 439 | return -EACCES; |
440 | if (vnode->status.lock_count == -1 && !can_sleep) | ||
441 | return -EAGAIN; /* Write locked */ | ||
442 | } else { | 440 | } else { |
443 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE))) | 441 | if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE))) |
444 | return -EACCES; | 442 | return -EACCES; |
445 | if (vnode->status.lock_count != 0 && !can_sleep) | ||
446 | return -EAGAIN; /* Locked */ | ||
447 | } | 443 | } |
448 | 444 | ||
449 | return 0; | 445 | return 0; |
@@ -456,24 +452,48 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) | |||
456 | { | 452 | { |
457 | struct inode *inode = locks_inode(file); | 453 | struct inode *inode = locks_inode(file); |
458 | struct afs_vnode *vnode = AFS_FS_I(inode); | 454 | struct afs_vnode *vnode = AFS_FS_I(inode); |
455 | enum afs_flock_mode mode = AFS_FS_S(inode->i_sb)->flock_mode; | ||
459 | afs_lock_type_t type; | 456 | afs_lock_type_t type; |
460 | struct key *key = afs_file_key(file); | 457 | struct key *key = afs_file_key(file); |
458 | bool partial, no_server_lock = false; | ||
461 | int ret; | 459 | int ret; |
462 | 460 | ||
463 | _enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); | 461 | if (mode == afs_flock_mode_unset) |
462 | mode = afs_flock_mode_openafs; | ||
463 | |||
464 | _enter("{%llx:%llu},%llu-%llu,%u,%u", | ||
465 | vnode->fid.vid, vnode->fid.vnode, | ||
466 | fl->fl_start, fl->fl_end, fl->fl_type, mode); | ||
464 | 467 | ||
465 | fl->fl_ops = &afs_lock_ops; | 468 | fl->fl_ops = &afs_lock_ops; |
466 | INIT_LIST_HEAD(&fl->fl_u.afs.link); | 469 | INIT_LIST_HEAD(&fl->fl_u.afs.link); |
467 | fl->fl_u.afs.state = AFS_LOCK_PENDING; | 470 | fl->fl_u.afs.state = AFS_LOCK_PENDING; |
468 | 471 | ||
472 | partial = (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX); | ||
469 | type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; | 473 | type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; |
474 | if (mode == afs_flock_mode_write && partial) | ||
475 | type = AFS_LOCK_WRITE; | ||
470 | 476 | ||
471 | ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP); | 477 | ret = afs_do_setlk_check(vnode, key, mode, type); |
472 | if (ret < 0) | 478 | if (ret < 0) |
473 | return ret; | 479 | return ret; |
474 | 480 | ||
475 | trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock); | 481 | trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock); |
476 | 482 | ||
483 | /* AFS3 protocol only supports full-file locks and doesn't provide any | ||
484 | * method of upgrade/downgrade, so we need to emulate for partial-file | ||
485 | * locks. | ||
486 | * | ||
487 | * The OpenAFS client only gets a server lock for a full-file lock and | ||
488 | * keeps partial-file locks local. Allow this behaviour to be emulated | ||
489 | * (as the default). | ||
490 | */ | ||
491 | if (mode == afs_flock_mode_local || | ||
492 | (partial && mode == afs_flock_mode_openafs)) { | ||
493 | no_server_lock = true; | ||
494 | goto skip_server_lock; | ||
495 | } | ||
496 | |||
477 | spin_lock(&vnode->lock); | 497 | spin_lock(&vnode->lock); |
478 | list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); | 498 | list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); |
479 | 499 | ||
@@ -502,6 +522,18 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) | |||
502 | } | 522 | } |
503 | } | 523 | } |
504 | 524 | ||
525 | if (vnode->lock_state == AFS_VNODE_LOCK_NONE && | ||
526 | !(fl->fl_flags & FL_SLEEP)) { | ||
527 | ret = -EAGAIN; | ||
528 | if (type == AFS_LOCK_READ) { | ||
529 | if (vnode->status.lock_count == -1) | ||
530 | goto lock_is_contended; /* Write locked */ | ||
531 | } else { | ||
532 | if (vnode->status.lock_count != 0) | ||
533 | goto lock_is_contended; /* Locked */ | ||
534 | } | ||
535 | } | ||
536 | |||
505 | if (vnode->lock_state != AFS_VNODE_LOCK_NONE) | 537 | if (vnode->lock_state != AFS_VNODE_LOCK_NONE) |
506 | goto need_to_wait; | 538 | goto need_to_wait; |
507 | 539 | ||
@@ -571,6 +603,7 @@ vnode_is_locked: | |||
571 | /* the lock has been granted by the server... */ | 603 | /* the lock has been granted by the server... */ |
572 | ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); | 604 | ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); |
573 | 605 | ||
606 | skip_server_lock: | ||
574 | /* ... but the VFS still needs to distribute access on this client. */ | 607 | /* ... but the VFS still needs to distribute access on this client. */ |
575 | trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0); | 608 | trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0); |
576 | ret = locks_lock_file_wait(file, fl); | 609 | ret = locks_lock_file_wait(file, fl); |
@@ -649,6 +682,8 @@ vfs_rejected_lock: | |||
649 | * deal with. | 682 | * deal with. |
650 | */ | 683 | */ |
651 | _debug("vfs refused %d", ret); | 684 | _debug("vfs refused %d", ret); |
685 | if (no_server_lock) | ||
686 | goto error; | ||
652 | spin_lock(&vnode->lock); | 687 | spin_lock(&vnode->lock); |
653 | list_del_init(&fl->fl_u.afs.link); | 688 | list_del_init(&fl->fl_u.afs.link); |
654 | afs_defer_unlock(vnode); | 689 | afs_defer_unlock(vnode); |