Commit 9fc75b7
committed
rbd: eliminate a race in lock_dwork draining on unmap
Given how rbd_lock_add_request() and rbd_img_exclusive_lock() are
written, lock_dwork may be (re)queued more than it's actually needed:
for example in case a new I/O request comes in while we are in the
middle of rbd_acquire_lock() on behalf of another I/O request. This is
expected and with rbd_release_lock() preemptively canceling lock_dwork
is benign under normal operation.
A more problematic example is maybe_kick_acquire():
if (have_requests || delayed_work_pending(&rbd_dev->lock_dwork)) {
dout("%s rbd_dev %p kicking lock_dwork\n", __func__, rbd_dev);
mod_delayed_work(rbd_dev->task_wq, &rbd_dev->lock_dwork, 0);
}
It's not unrealistic for lock_dwork to get canceled right after
delayed_work_pending() returns true and for mod_delayed_work() to
requeue it right there anyway. This is a classic TOCTOU race.
When it comes to unmapping the image, there is an implicit assumption
of no self-initiated exclusive lock activity past the point of return
from rbd_dev_image_unlock() which unlocks the lock if it happens to be
held. This unlock is assumed to be final and lock_dwork (as well as
all other exclusive lock tasks, really) isn't expected to get queued
again. However, lock_dwork is canceled only in cancel_tasks_sync()
(i.e. later in the unmap sequence) and on top of that the cancellation
can get in effect nullified by maybe_kick_acquire(). This may result
in rbd_acquire_lock() executing after rbd_dev_device_release() and
rbd_dev_image_release() run and free and/or reset a bunch of things.
One of the possible failure modes then is a violated
rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
in rbd_dev_header_info() which is called via rbd_dev_refresh() from
rbd_post_acquire_action().
Redo exclusive lock task draining to provide saner semantics and try
to meet the assumptions around rbd_dev_image_unlock().
Cc: stable@vger.kernel.org
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>1 parent 5200f5f commit 9fc75b7
1 file changed
Lines changed: 8 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4565 | 4565 | | |
4566 | 4566 | | |
4567 | 4567 | | |
4568 | | - | |
4569 | | - | |
4570 | | - | |
4571 | | - | |
4572 | | - | |
4573 | | - | |
4574 | | - | |
4575 | | - | |
4576 | | - | |
4577 | | - | |
4578 | 4568 | | |
4579 | 4569 | | |
4580 | 4570 | | |
4581 | 4571 | | |
4582 | 4572 | | |
4583 | 4573 | | |
4584 | | - | |
4585 | | - | |
4586 | 4574 | | |
4587 | 4575 | | |
4588 | 4576 | | |
| |||
6548 | 6536 | | |
6549 | 6537 | | |
6550 | 6538 | | |
| 6539 | + | |
| 6540 | + | |
| 6541 | + | |
| 6542 | + | |
| 6543 | + | |
6551 | 6544 | | |
6552 | 6545 | | |
6553 | 6546 | | |
6554 | 6547 | | |
| 6548 | + | |
| 6549 | + | |
| 6550 | + | |
6555 | 6551 | | |
6556 | 6552 | | |
6557 | 6553 | | |
| |||
0 commit comments