diff options
author | Sage Weil <sage@newdream.net> | 2010-03-16 16:39:28 -0400 |
---|---|---|
committer | Sage Weil <sage@newdream.net> | 2010-03-23 10:46:53 -0400 |
commit | cdc2ce056a3620139056b60ad7f6d355ad13f445 (patch) | |
tree | 1aed7f66612d13beeeec6e2c191f19eff44d1054 | |
parent | 4ea0043a29c82ca52ca54728d837314563bec574 (diff) |
ceph: fix session locking in handle_caps, ceph_check_caps
Passing a session pointer to ceph_check_caps() used to mean it would leave
the session mutex locked. That wasn't always possible if it wasn't passed
CHECK_CAPS_AUTHONLY. If could unlock the passed session and lock a
differet session mutex, which was clearly wrong, and also emitted a
warning when it a racing CPU retook it and we did an unlock from the wrong
context.
This was only a problem when there was more than one MDS.
First, make ceph_check_caps unconditionally drop the session mutex, so that
it is free to lock other sessions as needed. Then adjust the one caller
that passes in a session (handle_cap_grant) accordingly.
Signed-off-by: Sage Weil <sage@newdream.net>
-rw-r--r-- | fs/ceph/caps.c | 14 |
1 files changed, 9 insertions, 5 deletions
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 726c8d445995..782848632e81 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c | |||
@@ -1407,6 +1407,7 @@ static int try_nonblocking_invalidate(struct inode *inode) | |||
1407 | */ | 1407 | */ |
1408 | void ceph_check_caps(struct ceph_inode_info *ci, int flags, | 1408 | void ceph_check_caps(struct ceph_inode_info *ci, int flags, |
1409 | struct ceph_mds_session *session) | 1409 | struct ceph_mds_session *session) |
1410 | __releases(session->s_mutex) | ||
1410 | { | 1411 | { |
1411 | struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode); | 1412 | struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode); |
1412 | struct ceph_mds_client *mdsc = &client->mdsc; | 1413 | struct ceph_mds_client *mdsc = &client->mdsc; |
@@ -1414,7 +1415,6 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, | |||
1414 | struct ceph_cap *cap; | 1415 | struct ceph_cap *cap; |
1415 | int file_wanted, used; | 1416 | int file_wanted, used; |
1416 | int took_snap_rwsem = 0; /* true if mdsc->snap_rwsem held */ | 1417 | int took_snap_rwsem = 0; /* true if mdsc->snap_rwsem held */ |
1417 | int drop_session_lock = session ? 0 : 1; | ||
1418 | int issued, implemented, want, retain, revoking, flushing = 0; | 1418 | int issued, implemented, want, retain, revoking, flushing = 0; |
1419 | int mds = -1; /* keep track of how far we've gone through i_caps list | 1419 | int mds = -1; /* keep track of how far we've gone through i_caps list |
1420 | to avoid an infinite loop on retry */ | 1420 | to avoid an infinite loop on retry */ |
@@ -1639,7 +1639,7 @@ ack: | |||
1639 | if (queue_invalidate) | 1639 | if (queue_invalidate) |
1640 | ceph_queue_invalidate(inode); | 1640 | ceph_queue_invalidate(inode); |
1641 | 1641 | ||
1642 | if (session && drop_session_lock) | 1642 | if (session) |
1643 | mutex_unlock(&session->s_mutex); | 1643 | mutex_unlock(&session->s_mutex); |
1644 | if (took_snap_rwsem) | 1644 | if (took_snap_rwsem) |
1645 | up_read(&mdsc->snap_rwsem); | 1645 | up_read(&mdsc->snap_rwsem); |
@@ -2688,14 +2688,17 @@ void ceph_handle_caps(struct ceph_mds_session *session, | |||
2688 | case CEPH_CAP_OP_REVOKE: | 2688 | case CEPH_CAP_OP_REVOKE: |
2689 | case CEPH_CAP_OP_GRANT: | 2689 | case CEPH_CAP_OP_GRANT: |
2690 | r = handle_cap_grant(inode, h, session, cap, msg->middle); | 2690 | r = handle_cap_grant(inode, h, session, cap, msg->middle); |
2691 | if (r == 1) | 2691 | if (r == 1) { |
2692 | ceph_check_caps(ceph_inode(inode), | 2692 | ceph_check_caps(ceph_inode(inode), |
2693 | CHECK_CAPS_NODELAY|CHECK_CAPS_AUTHONLY, | 2693 | CHECK_CAPS_NODELAY|CHECK_CAPS_AUTHONLY, |
2694 | session); | 2694 | session); |
2695 | else if (r == 2) | 2695 | session = NULL; |
2696 | } else if (r == 2) { | ||
2696 | ceph_check_caps(ceph_inode(inode), | 2697 | ceph_check_caps(ceph_inode(inode), |
2697 | CHECK_CAPS_NODELAY, | 2698 | CHECK_CAPS_NODELAY, |
2698 | session); | 2699 | session); |
2700 | session = NULL; | ||
2701 | } | ||
2699 | break; | 2702 | break; |
2700 | 2703 | ||
2701 | case CEPH_CAP_OP_FLUSH_ACK: | 2704 | case CEPH_CAP_OP_FLUSH_ACK: |
@@ -2713,7 +2716,8 @@ void ceph_handle_caps(struct ceph_mds_session *session, | |||
2713 | } | 2716 | } |
2714 | 2717 | ||
2715 | done: | 2718 | done: |
2716 | mutex_unlock(&session->s_mutex); | 2719 | if (session) |
2720 | mutex_unlock(&session->s_mutex); | ||
2717 | 2721 | ||
2718 | if (check_caps) | 2722 | if (check_caps) |
2719 | ceph_check_caps(ceph_inode(inode), CHECK_CAPS_NODELAY, NULL); | 2723 | ceph_check_caps(ceph_inode(inode), CHECK_CAPS_NODELAY, NULL); |