aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ceph/mds_client.c
diff options
context:
space:
mode:
authorSage Weil <sage@newdream.net>2010-02-16 14:39:45 -0500
committerSage Weil <sage@newdream.net>2010-02-17 13:02:47 -0500
commit7c1332b8cb5b27656cf6ab1f5fe808a8eb8bb2c0 (patch)
treef990ab6b339a88896f41a6b3541d0676684c935d /fs/ceph/mds_client.c
parent85ccce43a3fc15a40ded6ae1603e3f68a17f4d24 (diff)
ceph: fix iterate_caps removal race
We need to be able to iterate over all caps on a session with a possibly slow callback on each cap. To allow this, we used to prevent cap reordering while we were iterating. However, we were not safe from races with removal: removing the 'next' cap would make the next pointer from list_for_each_entry_safe be invalid, and cause a lock up or similar badness. Instead, we keep an iterator pointer in the session pointing to the current cap. As before, we avoid reordering. For removal, if the cap isn't the current cap we are iterating over, we are fine. If it is, we clear cap->ci (to mark the cap as pending removal) but leave it in the session list. In iterate_caps, we can safely finish removal and get the next cap pointer. While we're at it, clean up put_cap to not take a cap reservation context, as it was never used. Signed-off-by: Sage Weil <sage@newdream.net>
Diffstat (limited to 'fs/ceph/mds_client.c')
-rw-r--r--fs/ceph/mds_client.c51
1 files changed, 42 insertions, 9 deletions
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 02834cecc3a..124c0c17a14 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -344,7 +344,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
344 INIT_LIST_HEAD(&s->s_waiting); 344 INIT_LIST_HEAD(&s->s_waiting);
345 INIT_LIST_HEAD(&s->s_unsafe); 345 INIT_LIST_HEAD(&s->s_unsafe);
346 s->s_num_cap_releases = 0; 346 s->s_num_cap_releases = 0;
347 s->s_iterating_caps = false; 347 s->s_cap_iterator = NULL;
348 INIT_LIST_HEAD(&s->s_cap_releases); 348 INIT_LIST_HEAD(&s->s_cap_releases);
349 INIT_LIST_HEAD(&s->s_cap_releases_done); 349 INIT_LIST_HEAD(&s->s_cap_releases_done);
350 INIT_LIST_HEAD(&s->s_cap_flushing); 350 INIT_LIST_HEAD(&s->s_cap_flushing);
@@ -729,28 +729,61 @@ static int iterate_session_caps(struct ceph_mds_session *session,
729 int (*cb)(struct inode *, struct ceph_cap *, 729 int (*cb)(struct inode *, struct ceph_cap *,
730 void *), void *arg) 730 void *), void *arg)
731{ 731{
732 struct ceph_cap *cap, *ncap; 732 struct list_head *p;
733 struct inode *inode; 733 struct ceph_cap *cap;
734 struct inode *inode, *last_inode = NULL;
735 struct ceph_cap *old_cap = NULL;
734 int ret; 736 int ret;
735 737
736 dout("iterate_session_caps %p mds%d\n", session, session->s_mds); 738 dout("iterate_session_caps %p mds%d\n", session, session->s_mds);
737 spin_lock(&session->s_cap_lock); 739 spin_lock(&session->s_cap_lock);
738 session->s_iterating_caps = true; 740 p = session->s_caps.next;
739 list_for_each_entry_safe(cap, ncap, &session->s_caps, session_caps) { 741 while (p != &session->s_caps) {
742 cap = list_entry(p, struct ceph_cap, session_caps);
740 inode = igrab(&cap->ci->vfs_inode); 743 inode = igrab(&cap->ci->vfs_inode);
741 if (!inode) 744 if (!inode) {
745 p = p->next;
742 continue; 746 continue;
747 }
748 session->s_cap_iterator = cap;
743 spin_unlock(&session->s_cap_lock); 749 spin_unlock(&session->s_cap_lock);
750
751 if (last_inode) {
752 iput(last_inode);
753 last_inode = NULL;
754 }
755 if (old_cap) {
756 ceph_put_cap(old_cap);
757 old_cap = NULL;
758 }
759
744 ret = cb(inode, cap, arg); 760 ret = cb(inode, cap, arg);
745 iput(inode); 761 last_inode = inode;
762
746 spin_lock(&session->s_cap_lock); 763 spin_lock(&session->s_cap_lock);
764 p = p->next;
765 if (cap->ci == NULL) {
766 dout("iterate_session_caps finishing cap %p removal\n",
767 cap);
768 BUG_ON(cap->session != session);
769 list_del_init(&cap->session_caps);
770 session->s_nr_caps--;
771 cap->session = NULL;
772 old_cap = cap; /* put_cap it w/o locks held */
773 }
747 if (ret < 0) 774 if (ret < 0)
748 goto out; 775 goto out;
749 } 776 }
750 ret = 0; 777 ret = 0;
751out: 778out:
752 session->s_iterating_caps = false; 779 session->s_cap_iterator = NULL;
753 spin_unlock(&session->s_cap_lock); 780 spin_unlock(&session->s_cap_lock);
781
782 if (last_inode)
783 iput(last_inode);
784 if (old_cap)
785 ceph_put_cap(old_cap);
786
754 return ret; 787 return ret;
755} 788}
756 789
@@ -942,7 +975,7 @@ static int trim_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg)
942 session->s_trim_caps--; 975 session->s_trim_caps--;
943 if (oissued) { 976 if (oissued) {
944 /* we aren't the only cap.. just remove us */ 977 /* we aren't the only cap.. just remove us */
945 __ceph_remove_cap(cap, NULL); 978 __ceph_remove_cap(cap);
946 } else { 979 } else {
947 /* try to drop referring dentries */ 980 /* try to drop referring dentries */
948 spin_unlock(&inode->i_lock); 981 spin_unlock(&inode->i_lock);