diff options
author | Yan, Zheng <zyan@redhat.com> | 2018-01-08 01:44:10 -0500 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2018-01-29 12:36:11 -0500 |
commit | 0f439c746c8cb370ce3ae1668182b18a5cb12b14 (patch) | |
tree | afea56c8ccc802f522dccf515e622329743a9610 | |
parent | ee612d954fe96612afaa966d8a67b736ba0c571e (diff) |
ceph: fix race of queuing delayed caps
When called with CHECK_CAPS_AUTHONLY flag, ceph_check_caps() only
processes auth caps. In that case, it's unsafe to remove inode
from mdsc->cap_delay_list, because there can be delayed non-auth
caps.
Besides, ceph_check_caps() may lock/unlock i_ceph_lock several
times, when multiple threads call ceph_check_caps() at the same
time. It's possible that one thread calls __cap_delay_requeue(),
another thread calls __cap_delay_cancel(). __cap_delay_cancel()
should be called at very beginning of ceph_check_caps(), so that
it does not race with __cap_delay_requeue().
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
-rw-r--r-- | fs/ceph/caps.c | 33 |
1 files changed, 16 insertions, 17 deletions
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 36f8a4bdf8c5..1726ddcf5e34 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c | |||
@@ -902,6 +902,11 @@ int __ceph_caps_mds_wanted(struct ceph_inode_info *ci, bool check) | |||
902 | /* | 902 | /* |
903 | * called under i_ceph_lock | 903 | * called under i_ceph_lock |
904 | */ | 904 | */ |
905 | static int __ceph_is_single_caps(struct ceph_inode_info *ci) | ||
906 | { | ||
907 | return rb_first(&ci->i_caps) == rb_last(&ci->i_caps); | ||
908 | } | ||
909 | |||
905 | static int __ceph_is_any_caps(struct ceph_inode_info *ci) | 910 | static int __ceph_is_any_caps(struct ceph_inode_info *ci) |
906 | { | 911 | { |
907 | return !RB_EMPTY_ROOT(&ci->i_caps); | 912 | return !RB_EMPTY_ROOT(&ci->i_caps); |
@@ -1715,21 +1720,24 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, | |||
1715 | int mds = -1; /* keep track of how far we've gone through i_caps list | 1720 | int mds = -1; /* keep track of how far we've gone through i_caps list |
1716 | to avoid an infinite loop on retry */ | 1721 | to avoid an infinite loop on retry */ |
1717 | struct rb_node *p; | 1722 | struct rb_node *p; |
1718 | int delayed = 0, sent = 0, num; | 1723 | int delayed = 0, sent = 0; |
1719 | bool is_delayed = flags & CHECK_CAPS_NODELAY; | 1724 | bool no_delay = flags & CHECK_CAPS_NODELAY; |
1720 | bool queue_invalidate = false; | 1725 | bool queue_invalidate = false; |
1721 | bool force_requeue = false; | ||
1722 | bool tried_invalidate = false; | 1726 | bool tried_invalidate = false; |
1723 | 1727 | ||
1724 | /* if we are unmounting, flush any unused caps immediately. */ | 1728 | /* if we are unmounting, flush any unused caps immediately. */ |
1725 | if (mdsc->stopping) | 1729 | if (mdsc->stopping) |
1726 | is_delayed = true; | 1730 | no_delay = true; |
1727 | 1731 | ||
1728 | spin_lock(&ci->i_ceph_lock); | 1732 | spin_lock(&ci->i_ceph_lock); |
1729 | 1733 | ||
1730 | if (ci->i_ceph_flags & CEPH_I_FLUSH) | 1734 | if (ci->i_ceph_flags & CEPH_I_FLUSH) |
1731 | flags |= CHECK_CAPS_FLUSH; | 1735 | flags |= CHECK_CAPS_FLUSH; |
1732 | 1736 | ||
1737 | if (!(flags & CHECK_CAPS_AUTHONLY) || | ||
1738 | (ci->i_auth_cap && __ceph_is_single_caps(ci))) | ||
1739 | __cap_delay_cancel(mdsc, ci); | ||
1740 | |||
1733 | goto retry_locked; | 1741 | goto retry_locked; |
1734 | retry: | 1742 | retry: |
1735 | spin_lock(&ci->i_ceph_lock); | 1743 | spin_lock(&ci->i_ceph_lock); |
@@ -1784,7 +1792,7 @@ retry_locked: | |||
1784 | * have cached pages, but don't want them, then try to invalidate. | 1792 | * have cached pages, but don't want them, then try to invalidate. |
1785 | * If we fail, it's because pages are locked.... try again later. | 1793 | * If we fail, it's because pages are locked.... try again later. |
1786 | */ | 1794 | */ |
1787 | if ((!is_delayed || mdsc->stopping) && | 1795 | if ((!no_delay || mdsc->stopping) && |
1788 | !S_ISDIR(inode->i_mode) && /* ignore readdir cache */ | 1796 | !S_ISDIR(inode->i_mode) && /* ignore readdir cache */ |
1789 | !(ci->i_wb_ref || ci->i_wrbuffer_ref) && /* no dirty pages... */ | 1797 | !(ci->i_wb_ref || ci->i_wrbuffer_ref) && /* no dirty pages... */ |
1790 | inode->i_data.nrpages && /* have cached pages */ | 1798 | inode->i_data.nrpages && /* have cached pages */ |
@@ -1801,10 +1809,8 @@ retry_locked: | |||
1801 | goto retry_locked; | 1809 | goto retry_locked; |
1802 | } | 1810 | } |
1803 | 1811 | ||
1804 | num = 0; | ||
1805 | for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { | 1812 | for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { |
1806 | cap = rb_entry(p, struct ceph_cap, ci_node); | 1813 | cap = rb_entry(p, struct ceph_cap, ci_node); |
1807 | num++; | ||
1808 | 1814 | ||
1809 | /* avoid looping forever */ | 1815 | /* avoid looping forever */ |
1810 | if (mds >= cap->mds || | 1816 | if (mds >= cap->mds || |
@@ -1867,7 +1873,7 @@ retry_locked: | |||
1867 | cap->mds_wanted == want) | 1873 | cap->mds_wanted == want) |
1868 | continue; /* nope, all good */ | 1874 | continue; /* nope, all good */ |
1869 | 1875 | ||
1870 | if (is_delayed) | 1876 | if (no_delay) |
1871 | goto ack; | 1877 | goto ack; |
1872 | 1878 | ||
1873 | /* delay? */ | 1879 | /* delay? */ |
@@ -1958,15 +1964,8 @@ ack: | |||
1958 | goto retry; /* retake i_ceph_lock and restart our cap scan. */ | 1964 | goto retry; /* retake i_ceph_lock and restart our cap scan. */ |
1959 | } | 1965 | } |
1960 | 1966 | ||
1961 | /* | 1967 | /* Reschedule delayed caps release if we delayed anything */ |
1962 | * Reschedule delayed caps release if we delayed anything, | 1968 | if (delayed) |
1963 | * otherwise cancel. | ||
1964 | */ | ||
1965 | if (delayed && is_delayed) | ||
1966 | force_requeue = true; /* __send_cap delayed release; requeue */ | ||
1967 | if (!delayed && !is_delayed) | ||
1968 | __cap_delay_cancel(mdsc, ci); | ||
1969 | else if (!is_delayed || force_requeue) | ||
1970 | __cap_delay_requeue(mdsc, ci); | 1969 | __cap_delay_requeue(mdsc, ci); |
1971 | 1970 | ||
1972 | spin_unlock(&ci->i_ceph_lock); | 1971 | spin_unlock(&ci->i_ceph_lock); |