]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
osd/PrimaryLogPG: avoid infinite loop when flush collides with write lock 21653/head
authorSage Weil <sage@redhat.com>
Wed, 25 Apr 2018 20:32:38 +0000 (15:32 -0500)
committerSage Weil <sage@redhat.com>
Wed, 25 Apr 2018 20:34:21 +0000 (15:34 -0500)
We try to take a write lock with fop->op.  If we fail, fop->op is put
on the lock's waiting list.  Requeuing it again will simply kick off
processing of another instance of the same op, which will again fail
to take the lock, leading to an infinite loop that can't terminate
because requeue_op is doing a push_front and preventing other PG
messages that might release the lock.

Do the same write lock attempt on any dup_ops so that they too will
end up on the wait list.

It looks like this broke waaay back in commit d700d99f76e0a29bfb419bc85d19ef1950b62a9a,
a 2014 refactor of the OpContext behavior.

Fixes: https://tracker.ceph.com/issues/23664
Signed-off-by: Sage Weil <sage@redhat.com>
src/osd/PrimaryLogPG.cc

index a5c260740f8e609d2887661c8991b4d8943470e7..3b18934b4eb1af220099fb0dcd27684cc2d575fc 100644 (file)
@@ -10009,10 +10009,18 @@ int PrimaryLogPG::try_flush_mark_clean(FlushOpRef fop)
        fop->op)) {
     dout(20) << __func__ << " took write lock" << dendl;
   } else if (fop->op) {
-    dout(10) << __func__ << " waiting on write lock" << dendl;
+    dout(10) << __func__ << " waiting on write lock " << fop->op << " "
+            << fop->dup_ops << dendl;
     close_op_ctx(ctx.release());
-    requeue_op(fop->op);
-    requeue_ops(fop->dup_ops);
+    // fop->op is now waiting on the lock; get fop->dup_ops to wait too.
+    for (auto op : fop->dup_ops) {
+      bool locked = ctx->lock_manager.get_lock_type(
+       ObjectContext::RWState::RWWRITE,
+       oid,
+       obc,
+       op);
+      assert(!locked);
+    }
     return -EAGAIN;    // will retry
   } else {
     dout(10) << __func__ << " failed write lock, no op; failing" << dendl;