Commit f6613e37 authored by zhenwei pi's avatar zhenwei pi Committed by Tomasz Zawadzki
Browse files

scsi: Abstract child IO split mechanism



Child IO split mechanism[1] for UNMAP allows a single SCSI command
to split into several SPDK Bdev IOs.
Separate the common child IO split mechanism from the UNMAP codes,
it's possible to implement SCSI WRITE SAME easily in the next step.

[1] SPDK commit: 73bc324b('scsi: Refine split processing for unmap')

Change-Id: I3248f51d2cdc1380e20ddaccc074369bf2b48ec8
Signed-off-by: default avatarzhenwei pi <pizhenwei@bytedance.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/20399


Reviewed-by: default avatarShuhei Matsumoto <smatsumoto@nvidia.com>
Reviewed-by: default avatarChangpeng Liu <changpeng.liu@intel.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Community-CI: Mellanox Build Bot
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent e205d23b
Loading
Loading
Loading
Loading
+84 −74
Original line number Diff line number Diff line
@@ -1325,21 +1325,79 @@ check_condition:
	return SPDK_SCSI_TASK_COMPLETE;
}

struct spdk_bdev_scsi_unmap_ctx {
struct spdk_bdev_scsi_split_ctx {
	struct spdk_scsi_task		*task;
	struct spdk_scsi_unmap_bdesc	desc[DEFAULT_MAX_UNMAP_BLOCK_DESCRIPTOR_COUNT];
	uint16_t			remaining_count;
	uint16_t			current_count;
	uint16_t			outstanding_count;
	int	(*fn)(struct spdk_bdev_scsi_split_ctx *ctx);
	char				name[16];
};

static int _bdev_scsi_unmap(struct spdk_bdev_scsi_unmap_ctx *ctx);
static int bdev_scsi_split(struct spdk_bdev_scsi_split_ctx *ctx);

static void
bdev_scsi_task_complete_unmap_cmd(struct spdk_bdev_io *bdev_io, bool success,
bdev_scsi_split_resubmit(void *arg)
{
	struct spdk_bdev_scsi_split_ctx	*ctx = arg;

	bdev_scsi_split(ctx);
}

static int
bdev_scsi_split(struct spdk_bdev_scsi_split_ctx *ctx)
{
	struct spdk_scsi_task *task = ctx->task;
	int rc;

	while (ctx->remaining_count != 0) {
		rc = ctx->fn(ctx);
		if (rc == 0) {
			ctx->current_count++;
			ctx->remaining_count--;
		} else {
			ctx->outstanding_count--;
			if (rc == -ENOMEM) {
				if (ctx->outstanding_count == 0) {
					/*
					 * If there are outstanding child I/Os, the last
					 * completion will call this function again to try
					 * the next split. Hence, queue the parent task only
					 * if there is no outstanding child I/O.
					 */
					bdev_scsi_queue_io(task, bdev_scsi_split_resubmit, ctx);
				}
				return SPDK_SCSI_TASK_PENDING;
			} else {
				SPDK_ERRLOG("SCSI %s failed\n", ctx->name);
				spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
							  SPDK_SCSI_SENSE_NO_SENSE,
							  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
							  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
				/* If any child I/O failed, stop further splitting process. */
				ctx->current_count += ctx->remaining_count;
				ctx->remaining_count = 0;
				/* We can't complete here - we may have to wait for previously
				 * submitted child I/Os to complete */
				break;
			}
		}
	}

	if (ctx->outstanding_count == 0) {
		free(ctx);
		return SPDK_SCSI_TASK_COMPLETE;
	}

	return SPDK_SCSI_TASK_PENDING;
}

static void
bdev_scsi_task_complete_split_cmd(struct spdk_bdev_io *bdev_io, bool success,
				  void *cb_arg)
{
	struct spdk_bdev_scsi_unmap_ctx *ctx = cb_arg;
	struct spdk_bdev_scsi_split_ctx *ctx = cb_arg;
	struct spdk_scsi_task *task = ctx->task;

	spdk_bdev_free_io(bdev_io);
@@ -1368,11 +1426,11 @@ bdev_scsi_task_complete_unmap_cmd(struct spdk_bdev_io *bdev_io, bool success,
	}

	/* Continue with splitting process. */
	_bdev_scsi_unmap(ctx);
	bdev_scsi_split(ctx);
}

static int
__copy_desc(struct spdk_bdev_scsi_unmap_ctx *ctx, uint8_t *data, size_t data_len)
__copy_desc(struct spdk_bdev_scsi_split_ctx *ctx, uint8_t *data, size_t data_len)
{
	uint16_t	desc_data_len;
	uint16_t	desc_count;
@@ -1405,22 +1463,11 @@ __copy_desc(struct spdk_bdev_scsi_unmap_ctx *ctx, uint8_t *data, size_t data_len
	return desc_count;
}

static void
bdev_scsi_unmap_resubmit(void *arg)
{
	struct spdk_bdev_scsi_unmap_ctx	*ctx = arg;

	_bdev_scsi_unmap(ctx);
}

static int
_bdev_scsi_unmap(struct spdk_bdev_scsi_unmap_ctx *ctx)
_bdev_scsi_unmap(struct spdk_bdev_scsi_split_ctx *ctx)
{
	struct spdk_scsi_task *task = ctx->task;
	struct spdk_scsi_lun *lun = task->lun;
	int rc;

	while (ctx->remaining_count != 0) {
	struct spdk_scsi_unmap_bdesc	*desc;
	uint64_t offset_blocks;
	uint64_t num_blocks;
@@ -1431,61 +1478,22 @@ _bdev_scsi_unmap(struct spdk_bdev_scsi_unmap_ctx *ctx)
	num_blocks = from_be32(&desc->block_count);

	if (num_blocks == 0) {
			rc = 0;
		} else {
		return 0;
	}

	ctx->outstanding_count++;
			rc = spdk_bdev_unmap_blocks(lun->bdev_desc,
	return spdk_bdev_unmap_blocks(lun->bdev_desc,
				      lun->io_channel,
				      offset_blocks,
				      num_blocks,
						    bdev_scsi_task_complete_unmap_cmd,
				      bdev_scsi_task_complete_split_cmd,
				      ctx);
}

		if (rc == 0) {
			ctx->current_count++;
			ctx->remaining_count--;
		} else {
			ctx->outstanding_count--;
			if (rc == -ENOMEM) {
				if (ctx->outstanding_count == 0) {
					/*
					 * If there are outstanding child I/Os, the last
					 * completion will call this function again to try
					 * the next split. Hence, queue the parent task only
					 * if there is no outstanding child I/O.
					 */
					bdev_scsi_queue_io(task, bdev_scsi_unmap_resubmit, ctx);
				}
				return SPDK_SCSI_TASK_PENDING;
			} else {
				SPDK_ERRLOG("SCSI Unmapping failed\n");
				spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
							  SPDK_SCSI_SENSE_NO_SENSE,
							  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
							  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
				/* If any child I/O failed, stop further splitting process. */
				ctx->current_count += ctx->remaining_count;
				ctx->remaining_count = 0;
				/* We can't complete here - we may have to wait for previously
				 * submitted child I/Os to complete */
				break;
			}
		}
	}

	if (ctx->outstanding_count == 0) {
		free(ctx);
		return SPDK_SCSI_TASK_COMPLETE;
	}

	return SPDK_SCSI_TASK_PENDING;
}

static int
bdev_scsi_unmap(struct spdk_bdev *bdev, struct spdk_scsi_task *task)
{
	struct spdk_bdev_scsi_unmap_ctx	*ctx;
	struct spdk_bdev_scsi_split_ctx	*ctx;
	uint8_t				*data;
	int				desc_count = -1;
	int				data_len;
@@ -1504,6 +1512,8 @@ bdev_scsi_unmap(struct spdk_bdev *bdev, struct spdk_scsi_task *task)
	ctx->task = task;
	ctx->current_count = 0;
	ctx->outstanding_count = 0;
	ctx->fn = _bdev_scsi_unmap;
	snprintf(ctx->name, sizeof(ctx->name), "UNMAP");

	if (task->iovcnt == 1) {
		data = (uint8_t *)task->iovs[0].iov_base;
@@ -1528,7 +1538,7 @@ bdev_scsi_unmap(struct spdk_bdev *bdev, struct spdk_scsi_task *task)

	ctx->remaining_count = desc_count;

	return _bdev_scsi_unmap(ctx);
	return bdev_scsi_split(ctx);
}

static int