Commit 0f439c74 authored by Yan, Zheng's avatar Yan, Zheng Committed by Ilya Dryomov

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: default avatar"Yan, Zheng" <zyan@redhat.com>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent ee612d95
...@@ -902,6 +902,11 @@ int __ceph_caps_mds_wanted(struct ceph_inode_info *ci, bool check) ...@@ -902,6 +902,11 @@ int __ceph_caps_mds_wanted(struct ceph_inode_info *ci, bool check)
/* /*
* called under i_ceph_lock * called under i_ceph_lock
*/ */
static int __ceph_is_single_caps(struct ceph_inode_info *ci)
{
return rb_first(&ci->i_caps) == rb_last(&ci->i_caps);
}
static int __ceph_is_any_caps(struct ceph_inode_info *ci) static int __ceph_is_any_caps(struct ceph_inode_info *ci)
{ {
return !RB_EMPTY_ROOT(&ci->i_caps); return !RB_EMPTY_ROOT(&ci->i_caps);
...@@ -1715,21 +1720,24 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, ...@@ -1715,21 +1720,24 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
int mds = -1; /* keep track of how far we've gone through i_caps list int mds = -1; /* keep track of how far we've gone through i_caps list
to avoid an infinite loop on retry */ to avoid an infinite loop on retry */
struct rb_node *p; struct rb_node *p;
int delayed = 0, sent = 0, num; int delayed = 0, sent = 0;
bool is_delayed = flags & CHECK_CAPS_NODELAY; bool no_delay = flags & CHECK_CAPS_NODELAY;
bool queue_invalidate = false; bool queue_invalidate = false;
bool force_requeue = false;
bool tried_invalidate = false; bool tried_invalidate = false;
/* if we are unmounting, flush any unused caps immediately. */ /* if we are unmounting, flush any unused caps immediately. */
if (mdsc->stopping) if (mdsc->stopping)
is_delayed = true; no_delay = true;
spin_lock(&ci->i_ceph_lock); spin_lock(&ci->i_ceph_lock);
if (ci->i_ceph_flags & CEPH_I_FLUSH) if (ci->i_ceph_flags & CEPH_I_FLUSH)
flags |= CHECK_CAPS_FLUSH; flags |= CHECK_CAPS_FLUSH;
if (!(flags & CHECK_CAPS_AUTHONLY) ||
(ci->i_auth_cap && __ceph_is_single_caps(ci)))
__cap_delay_cancel(mdsc, ci);
goto retry_locked; goto retry_locked;
retry: retry:
spin_lock(&ci->i_ceph_lock); spin_lock(&ci->i_ceph_lock);
...@@ -1784,7 +1792,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, ...@@ -1784,7 +1792,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
* have cached pages, but don't want them, then try to invalidate. * have cached pages, but don't want them, then try to invalidate.
* If we fail, it's because pages are locked.... try again later. * If we fail, it's because pages are locked.... try again later.
*/ */
if ((!is_delayed || mdsc->stopping) && if ((!no_delay || mdsc->stopping) &&
!S_ISDIR(inode->i_mode) && /* ignore readdir cache */ !S_ISDIR(inode->i_mode) && /* ignore readdir cache */
!(ci->i_wb_ref || ci->i_wrbuffer_ref) && /* no dirty pages... */ !(ci->i_wb_ref || ci->i_wrbuffer_ref) && /* no dirty pages... */
inode->i_data.nrpages && /* have cached pages */ inode->i_data.nrpages && /* have cached pages */
...@@ -1801,10 +1809,8 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, ...@@ -1801,10 +1809,8 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
goto retry_locked; goto retry_locked;
} }
num = 0;
for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) {
cap = rb_entry(p, struct ceph_cap, ci_node); cap = rb_entry(p, struct ceph_cap, ci_node);
num++;
/* avoid looping forever */ /* avoid looping forever */
if (mds >= cap->mds || if (mds >= cap->mds ||
...@@ -1867,7 +1873,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, ...@@ -1867,7 +1873,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
cap->mds_wanted == want) cap->mds_wanted == want)
continue; /* nope, all good */ continue; /* nope, all good */
if (is_delayed) if (no_delay)
goto ack; goto ack;
/* delay? */ /* delay? */
...@@ -1958,15 +1964,8 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, ...@@ -1958,15 +1964,8 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
goto retry; /* retake i_ceph_lock and restart our cap scan. */ goto retry; /* retake i_ceph_lock and restart our cap scan. */
} }
/* /* Reschedule delayed caps release if we delayed anything */
* Reschedule delayed caps release if we delayed anything, if (delayed)
* otherwise cancel.
*/
if (delayed && is_delayed)
force_requeue = true; /* __send_cap delayed release; requeue */
if (!delayed && !is_delayed)
__cap_delay_cancel(mdsc, ci);
else if (!is_delayed || force_requeue)
__cap_delay_requeue(mdsc, ci); __cap_delay_requeue(mdsc, ci);
spin_unlock(&ci->i_ceph_lock); spin_unlock(&ci->i_ceph_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment