|
@@ -656,7 +656,7 @@ static void scatter_data_area(struct tcmu_dev *udev,
|
|
|
}
|
|
|
|
|
|
static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd,
|
|
|
- bool bidi)
|
|
|
+ bool bidi, uint32_t read_len)
|
|
|
{
|
|
|
struct se_cmd *se_cmd = cmd->se_cmd;
|
|
|
int i, dbi;
|
|
@@ -689,7 +689,7 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd,
|
|
|
for_each_sg(data_sg, sg, data_nents, i) {
|
|
|
int sg_remaining = sg->length;
|
|
|
to = kmap_atomic(sg_page(sg)) + sg->offset;
|
|
|
- while (sg_remaining > 0) {
|
|
|
+ while (sg_remaining > 0 && read_len > 0) {
|
|
|
if (block_remaining == 0) {
|
|
|
if (from)
|
|
|
kunmap_atomic(from);
|
|
@@ -701,6 +701,8 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd,
|
|
|
}
|
|
|
copy_bytes = min_t(size_t, sg_remaining,
|
|
|
block_remaining);
|
|
|
+ if (read_len < copy_bytes)
|
|
|
+ copy_bytes = read_len;
|
|
|
offset = DATA_BLOCK_SIZE - block_remaining;
|
|
|
tcmu_flush_dcache_range(from, copy_bytes);
|
|
|
memcpy(to + sg->length - sg_remaining, from + offset,
|
|
@@ -708,8 +710,11 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd,
|
|
|
|
|
|
sg_remaining -= copy_bytes;
|
|
|
block_remaining -= copy_bytes;
|
|
|
+ read_len -= copy_bytes;
|
|
|
}
|
|
|
kunmap_atomic(to - sg->offset);
|
|
|
+ if (read_len == 0)
|
|
|
+ break;
|
|
|
}
|
|
|
if (from)
|
|
|
kunmap_atomic(from);
|
|
@@ -1042,6 +1047,8 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
|
|
|
{
|
|
|
struct se_cmd *se_cmd = cmd->se_cmd;
|
|
|
struct tcmu_dev *udev = cmd->tcmu_dev;
|
|
|
+ bool read_len_valid = false;
|
|
|
+ uint32_t read_len = se_cmd->data_length;
|
|
|
|
|
|
/*
|
|
|
* cmd has been completed already from timeout, just reclaim
|
|
@@ -1056,13 +1063,28 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
|
|
|
pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n",
|
|
|
cmd->se_cmd);
|
|
|
entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION;
|
|
|
- } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) {
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (se_cmd->data_direction == DMA_FROM_DEVICE &&
|
|
|
+ (entry->hdr.uflags & TCMU_UFLAG_READ_LEN) && entry->rsp.read_len) {
|
|
|
+ read_len_valid = true;
|
|
|
+ if (entry->rsp.read_len < read_len)
|
|
|
+ read_len = entry->rsp.read_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) {
|
|
|
transport_copy_sense_to_cmd(se_cmd, entry->rsp.sense_buffer);
|
|
|
- } else if (se_cmd->se_cmd_flags & SCF_BIDI) {
|
|
|
+ if (!read_len_valid )
|
|
|
+ goto done;
|
|
|
+ else
|
|
|
+ se_cmd->se_cmd_flags |= SCF_TREAT_READ_AS_NORMAL;
|
|
|
+ }
|
|
|
+ if (se_cmd->se_cmd_flags & SCF_BIDI) {
|
|
|
/* Get Data-In buffer before clean up */
|
|
|
- gather_data_area(udev, cmd, true);
|
|
|
+ gather_data_area(udev, cmd, true, read_len);
|
|
|
} else if (se_cmd->data_direction == DMA_FROM_DEVICE) {
|
|
|
- gather_data_area(udev, cmd, false);
|
|
|
+ gather_data_area(udev, cmd, false, read_len);
|
|
|
} else if (se_cmd->data_direction == DMA_TO_DEVICE) {
|
|
|
/* TODO: */
|
|
|
} else if (se_cmd->data_direction != DMA_NONE) {
|
|
@@ -1070,7 +1092,13 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
|
|
|
se_cmd->data_direction);
|
|
|
}
|
|
|
|
|
|
- target_complete_cmd(cmd->se_cmd, entry->rsp.scsi_status);
|
|
|
+done:
|
|
|
+ if (read_len_valid) {
|
|
|
+ pr_debug("read_len = %d\n", read_len);
|
|
|
+ target_complete_cmd_with_length(cmd->se_cmd,
|
|
|
+ entry->rsp.scsi_status, read_len);
|
|
|
+ } else
|
|
|
+ target_complete_cmd(cmd->se_cmd, entry->rsp.scsi_status);
|
|
|
|
|
|
out:
|
|
|
cmd->se_cmd = NULL;
|
|
@@ -1740,7 +1768,7 @@ static int tcmu_configure_device(struct se_device *dev)
|
|
|
/* Initialise the mailbox of the ring buffer */
|
|
|
mb = udev->mb_addr;
|
|
|
mb->version = TCMU_MAILBOX_VERSION;
|
|
|
- mb->flags = TCMU_MAILBOX_FLAG_CAP_OOOC;
|
|
|
+ mb->flags = TCMU_MAILBOX_FLAG_CAP_OOOC | TCMU_MAILBOX_FLAG_CAP_READ_LEN;
|
|
|
mb->cmdr_off = CMDR_OFF;
|
|
|
mb->cmdr_size = udev->cmdr_size;
|
|
|
|