summaryrefslogtreecommitdiff
path: root/drivers/block
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/ublk_drv.c47
1 files changed, 30 insertions, 17 deletions
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index c2c11f2a01e7..4ca6ec738c93 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -19,6 +19,7 @@
#include <linux/errno.h>
#include <linux/major.h>
#include <linux/wait.h>
+#include <linux/wait_bit.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/swap.h>
@@ -26,7 +27,6 @@
#include <linux/compat.h>
#include <linux/mutex.h>
#include <linux/writeback.h>
-#include <linux/completion.h>
#include <linux/highmem.h>
#include <linux/sysfs.h>
#include <linux/miscdevice.h>
@@ -327,7 +327,6 @@ struct ublk_device {
struct ublk_params params;
- struct completion completion;
u32 nr_queue_ready;
bool unprivileged_daemons;
struct mutex cancel_mutex;
@@ -3054,12 +3053,12 @@ static void ublk_mark_io_ready(struct ublk_device *ub, u16 q_id,
if (ublk_dev_ready(ub)) {
/*
* All queues ready - clear device-level canceling flag
- * and complete the recovery/initialization.
+ * and wake ublk_dev_ready() waiters.
*/
mutex_lock(&ub->cancel_mutex);
ub->canceling = false;
mutex_unlock(&ub->cancel_mutex);
- complete_all(&ub->completion);
+ wake_up_var(&ub->nr_queue_ready);
}
}
@@ -4273,7 +4272,6 @@ static int ublk_init_queues(struct ublk_device *ub)
goto fail;
}
- init_completion(&ub->completion);
return 0;
fail:
@@ -4417,6 +4415,26 @@ static bool ublk_validate_user_pid(struct ublk_device *ub, pid_t ublksrv_pid)
return ub->ublksrv_tgid == ublksrv_pid;
}
+/*
+ * Wait until all queues have fetched their I/O commands, and return with
+ * ub->mutex held and readiness guaranteed: then every queue's ->canceling
+ * is cleared. Ready may regress between wakeup and mutex_lock() (F_BATCH
+ * UNPREP, daemon death), so re-check it under the mutex and wait again.
+ */
+static int ublk_wait_dev_ready_and_lock(struct ublk_device *ub)
+{
+ while (true) {
+ if (wait_var_event_interruptible(&ub->nr_queue_ready,
+ ublk_dev_ready(ub)))
+ return -EINTR;
+
+ mutex_lock(&ub->mutex);
+ if (ublk_dev_ready(ub))
+ return 0;
+ mutex_unlock(&ub->mutex);
+ }
+}
+
static int ublk_ctrl_start_dev(struct ublk_device *ub,
const struct ublksrv_ctrl_cmd *header)
{
@@ -4499,15 +4517,10 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
};
}
- if (wait_for_completion_interruptible(&ub->completion) != 0)
+ if (ublk_wait_dev_ready_and_lock(ub))
return -EINTR;
- if (!ublk_validate_user_pid(ub, ublksrv_pid))
- return -EINVAL;
-
- mutex_lock(&ub->mutex);
- /* device may become not ready in case of F_BATCH */
- if (!ublk_dev_ready(ub)) {
+ if (!ublk_validate_user_pid(ub, ublksrv_pid)) {
ret = -EINVAL;
goto out_unlock;
}
@@ -5071,7 +5084,6 @@ static int ublk_ctrl_start_recovery(struct ublk_device *ub)
goto out_unlock;
}
pr_devel("%s: start recovery for dev id %d\n", __func__, ub->ub_number);
- init_completion(&ub->completion);
ret = 0;
out_unlock:
mutex_unlock(&ub->mutex);
@@ -5087,16 +5099,17 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub,
pr_devel("%s: Waiting for all FETCH_REQs, dev id %d...\n", __func__,
header->dev_id);
- if (wait_for_completion_interruptible(&ub->completion))
+ if (ublk_wait_dev_ready_and_lock(ub))
return -EINTR;
pr_devel("%s: All FETCH_REQs received, dev id %d\n", __func__,
header->dev_id);
- if (!ublk_validate_user_pid(ub, ublksrv_pid))
- return -EINVAL;
+ if (!ublk_validate_user_pid(ub, ublksrv_pid)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
- mutex_lock(&ub->mutex);
if (ublk_nosrv_should_stop_dev(ub))
goto out_unlock;