|
51 | 51 | /* private ioctl command mirror */
|
52 | 52 | #define UBLK_CMD_DEL_DEV_ASYNC _IOC_NR(UBLK_U_CMD_DEL_DEV_ASYNC)
|
53 | 53 | #define UBLK_CMD_UPDATE_SIZE _IOC_NR(UBLK_U_CMD_UPDATE_SIZE)
|
| 54 | +#define UBLK_CMD_QUIESCE_DEV _IOC_NR(UBLK_U_CMD_QUIESCE_DEV) |
54 | 55 |
|
55 | 56 | #define UBLK_IO_REGISTER_IO_BUF _IOC_NR(UBLK_U_IO_REGISTER_IO_BUF)
|
56 | 57 | #define UBLK_IO_UNREGISTER_IO_BUF _IOC_NR(UBLK_U_IO_UNREGISTER_IO_BUF)
|
|
67 | 68 | | UBLK_F_ZONED \
|
68 | 69 | | UBLK_F_USER_RECOVERY_FAIL_IO \
|
69 | 70 | | UBLK_F_UPDATE_SIZE \
|
70 |
| - | UBLK_F_AUTO_BUF_REG) |
| 71 | + | UBLK_F_AUTO_BUF_REG \ |
| 72 | + | UBLK_F_QUIESCE) |
71 | 73 |
|
72 | 74 | #define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \
|
73 | 75 | | UBLK_F_USER_RECOVERY_REISSUE \
|
@@ -2841,6 +2843,11 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
|
2841 | 2843 | return -EINVAL;
|
2842 | 2844 | }
|
2843 | 2845 |
|
| 2846 | + if ((info.flags & UBLK_F_QUIESCE) && !(info.flags & UBLK_F_USER_RECOVERY)) { |
| 2847 | + pr_warn("UBLK_F_QUIESCE requires UBLK_F_USER_RECOVERY\n"); |
| 2848 | + return -EINVAL; |
| 2849 | + } |
| 2850 | + |
2844 | 2851 | /*
|
2845 | 2852 | * unprivileged device can't be trusted, but RECOVERY and
|
2846 | 2853 | * RECOVERY_REISSUE still may hang error handling, so can't
|
@@ -3233,6 +3240,117 @@ static void ublk_ctrl_set_size(struct ublk_device *ub, const struct ublksrv_ctrl
|
3233 | 3240 | set_capacity_and_notify(ub->ub_disk, p->dev_sectors);
|
3234 | 3241 | mutex_unlock(&ub->mutex);
|
3235 | 3242 | }
|
| 3243 | + |
| 3244 | +struct count_busy { |
| 3245 | + const struct ublk_queue *ubq; |
| 3246 | + unsigned int nr_busy; |
| 3247 | +}; |
| 3248 | + |
| 3249 | +static bool ublk_count_busy_req(struct request *rq, void *data) |
| 3250 | +{ |
| 3251 | + struct count_busy *idle = data; |
| 3252 | + |
| 3253 | + if (!blk_mq_request_started(rq) && rq->mq_hctx->driver_data == idle->ubq) |
| 3254 | + idle->nr_busy += 1; |
| 3255 | + return true; |
| 3256 | +} |
| 3257 | + |
| 3258 | +/* uring_cmd is guaranteed to be active if the associated request is idle */ |
| 3259 | +static bool ubq_has_idle_io(const struct ublk_queue *ubq) |
| 3260 | +{ |
| 3261 | + struct count_busy data = { |
| 3262 | + .ubq = ubq, |
| 3263 | + }; |
| 3264 | + |
| 3265 | + blk_mq_tagset_busy_iter(&ubq->dev->tag_set, ublk_count_busy_req, &data); |
| 3266 | + return data.nr_busy < ubq->q_depth; |
| 3267 | +} |
| 3268 | + |
| 3269 | +/* Wait until each hw queue has at least one idle IO */ |
| 3270 | +static int ublk_wait_for_idle_io(struct ublk_device *ub, |
| 3271 | + unsigned int timeout_ms) |
| 3272 | +{ |
| 3273 | + unsigned int elapsed = 0; |
| 3274 | + int ret; |
| 3275 | + |
| 3276 | + while (elapsed < timeout_ms && !signal_pending(current)) { |
| 3277 | + unsigned int queues_cancelable = 0; |
| 3278 | + int i; |
| 3279 | + |
| 3280 | + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { |
| 3281 | + struct ublk_queue *ubq = ublk_get_queue(ub, i); |
| 3282 | + |
| 3283 | + queues_cancelable += !!ubq_has_idle_io(ubq); |
| 3284 | + } |
| 3285 | + |
| 3286 | + /* |
| 3287 | + * Each queue needs at least one active command for |
| 3288 | + * notifying ublk server |
| 3289 | + */ |
| 3290 | + if (queues_cancelable == ub->dev_info.nr_hw_queues) |
| 3291 | + break; |
| 3292 | + |
| 3293 | + msleep(UBLK_REQUEUE_DELAY_MS); |
| 3294 | + elapsed += UBLK_REQUEUE_DELAY_MS; |
| 3295 | + } |
| 3296 | + |
| 3297 | + if (signal_pending(current)) |
| 3298 | + ret = -EINTR; |
| 3299 | + else if (elapsed >= timeout_ms) |
| 3300 | + ret = -EBUSY; |
| 3301 | + else |
| 3302 | + ret = 0; |
| 3303 | + |
| 3304 | + return ret; |
| 3305 | +} |
| 3306 | + |
| 3307 | +static int ublk_ctrl_quiesce_dev(struct ublk_device *ub, |
| 3308 | + const struct ublksrv_ctrl_cmd *header) |
| 3309 | +{ |
| 3310 | + /* zero means wait forever */ |
| 3311 | + u64 timeout_ms = header->data[0]; |
| 3312 | + struct gendisk *disk; |
| 3313 | + int i, ret = -ENODEV; |
| 3314 | + |
| 3315 | + if (!(ub->dev_info.flags & UBLK_F_QUIESCE)) |
| 3316 | + return -EOPNOTSUPP; |
| 3317 | + |
| 3318 | + mutex_lock(&ub->mutex); |
| 3319 | + disk = ublk_get_disk(ub); |
| 3320 | + if (!disk) |
| 3321 | + goto unlock; |
| 3322 | + if (ub->dev_info.state == UBLK_S_DEV_DEAD) |
| 3323 | + goto put_disk; |
| 3324 | + |
| 3325 | + ret = 0; |
| 3326 | + /* already in expected state */ |
| 3327 | + if (ub->dev_info.state != UBLK_S_DEV_LIVE) |
| 3328 | + goto put_disk; |
| 3329 | + |
| 3330 | + /* Mark all queues as canceling */ |
| 3331 | + blk_mq_quiesce_queue(disk->queue); |
| 3332 | + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { |
| 3333 | + struct ublk_queue *ubq = ublk_get_queue(ub, i); |
| 3334 | + |
| 3335 | + ubq->canceling = true; |
| 3336 | + } |
| 3337 | + blk_mq_unquiesce_queue(disk->queue); |
| 3338 | + |
| 3339 | + if (!timeout_ms) |
| 3340 | + timeout_ms = UINT_MAX; |
| 3341 | + ret = ublk_wait_for_idle_io(ub, timeout_ms); |
| 3342 | + |
| 3343 | +put_disk: |
| 3344 | + ublk_put_disk(disk); |
| 3345 | +unlock: |
| 3346 | + mutex_unlock(&ub->mutex); |
| 3347 | + |
| 3348 | + /* Cancel pending uring_cmd */ |
| 3349 | + if (!ret) |
| 3350 | + ublk_cancel_dev(ub); |
| 3351 | + return ret; |
| 3352 | +} |
| 3353 | + |
3236 | 3354 | /*
|
3237 | 3355 | * All control commands are sent via /dev/ublk-control, so we have to check
|
3238 | 3356 | * the destination device's permission
|
@@ -3319,6 +3437,7 @@ static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub,
|
3319 | 3437 | case UBLK_CMD_START_USER_RECOVERY:
|
3320 | 3438 | case UBLK_CMD_END_USER_RECOVERY:
|
3321 | 3439 | case UBLK_CMD_UPDATE_SIZE:
|
| 3440 | + case UBLK_CMD_QUIESCE_DEV: |
3322 | 3441 | mask = MAY_READ | MAY_WRITE;
|
3323 | 3442 | break;
|
3324 | 3443 | default:
|
@@ -3414,6 +3533,9 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
|
3414 | 3533 | ublk_ctrl_set_size(ub, header);
|
3415 | 3534 | ret = 0;
|
3416 | 3535 | break;
|
| 3536 | + case UBLK_CMD_QUIESCE_DEV: |
| 3537 | + ret = ublk_ctrl_quiesce_dev(ub, header); |
| 3538 | + break; |
3417 | 3539 | default:
|
3418 | 3540 | ret = -EOPNOTSUPP;
|
3419 | 3541 | break;
|
|
0 commit comments