lib/scsi: handle scattered input/output buffers for non IO commands
Fix buffer overflow/underflow for commands with alloc length scattered into multiple preallocated buffers (eg. INQUIRY) Change-Id: If6f7cabc7a6a7fb384bb015e14dc38548f484d0f Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
This commit is contained in:
parent
8c5738ec5c
commit
a1948352a3
@ -275,14 +275,17 @@ void spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t l
|
||||
* returned buffer and must not free it. Caller is permitted to call
|
||||
* spdk_scsi_task_free_data() to free internal buffer if it is not required
|
||||
* anymore, but must assert that task is done and not used by library.
|
||||
* The count of io vectors must by one. Any previously allocated buffer will be
|
||||
* invalid after this call.
|
||||
*
|
||||
* Allocated buffer is stored in iov field of task object.
|
||||
*
|
||||
* \param task Task struct
|
||||
* \param alloc_len Size of allocated buffer.
|
||||
* \return Pointer to buffer or NULL on error.
|
||||
*/
|
||||
void *spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len);
|
||||
|
||||
int spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t len);
|
||||
void *spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len);
|
||||
void spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc,
|
||||
int ascq);
|
||||
void spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk, int asc,
|
||||
|
@ -184,7 +184,6 @@ static void
|
||||
complete_task_with_no_lun(struct spdk_scsi_task *task)
|
||||
{
|
||||
uint8_t buffer[36];
|
||||
uint8_t *data;
|
||||
uint32_t allocation_len;
|
||||
uint32_t data_len;
|
||||
|
||||
@ -194,24 +193,19 @@ complete_task_with_no_lun(struct spdk_scsi_task *task)
|
||||
* must be served with PERIPHERAL QUALIFIER = 0x3 and
|
||||
* PERIPHERAL DEVICE TYPE = 0x1F.
|
||||
*/
|
||||
allocation_len = from_be16(&task->cdb[3]);
|
||||
data_len = sizeof(buffer);
|
||||
if (allocation_len > data_len)
|
||||
allocation_len = data_len;
|
||||
|
||||
if (allocation_len) {
|
||||
memset(buffer, 0, data_len);
|
||||
/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
|
||||
buffer[0] = 0x03 << 5 | 0x1f;
|
||||
/* ADDITIONAL LENGTH */
|
||||
buffer[4] = data_len - 5;
|
||||
|
||||
data = spdk_scsi_task_alloc_data(task, allocation_len);
|
||||
memcpy(data, buffer, allocation_len);
|
||||
}
|
||||
|
||||
allocation_len = from_be16(&task->cdb[3]);
|
||||
if (spdk_scsi_task_scatter_data(task, buffer, SPDK_MIN(allocation_len, data_len)) >= 0) {
|
||||
task->data_transferred = data_len;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
}
|
||||
} else {
|
||||
/* LOGICAL UNIT NOT SUPPORTED */
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
|
@ -1455,9 +1455,17 @@ spdk_bdev_scsi_unmap(struct spdk_bdev *bdev,
|
||||
{
|
||||
|
||||
uint8_t *data;
|
||||
uint16_t bdesc_data_len, bdesc_count;
|
||||
struct spdk_scsi_unmap_bdesc *desc;
|
||||
uint32_t bdesc_count;
|
||||
int bdesc_data_len;
|
||||
int data_len;
|
||||
|
||||
if (task->iovcnt == 1) {
|
||||
data = (uint8_t *)task->iovs[0].iov_base;
|
||||
data_len = task->iovs[0].iov_len;
|
||||
} else {
|
||||
data = spdk_scsi_task_gather_data(task, &data_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* The UNMAP BLOCK DESCRIPTOR DATA LENGTH field specifies the length in
|
||||
@ -1469,6 +1477,15 @@ spdk_bdev_scsi_unmap(struct spdk_bdev *bdev,
|
||||
*/
|
||||
bdesc_data_len = from_be16(&data[2]);
|
||||
bdesc_count = bdesc_data_len / 16;
|
||||
assert(bdesc_data_len <= data_len);
|
||||
|
||||
if (task->iovcnt == 1) {
|
||||
desc = (struct spdk_scsi_unmap_bdesc *)&data[8];
|
||||
} else {
|
||||
desc = spdk_scsi_task_alloc_data(task, bdesc_data_len - 8);
|
||||
memcpy(desc, &data[8], bdesc_data_len - 8);
|
||||
spdk_free(data);
|
||||
}
|
||||
|
||||
if (bdesc_count > bdev->max_unmap_bdesc_count) {
|
||||
SPDK_ERRLOG("Error - supported unmap block descriptor count limit"
|
||||
@ -1478,9 +1495,17 @@ spdk_bdev_scsi_unmap(struct spdk_bdev *bdev,
|
||||
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return SPDK_SCSI_TASK_COMPLETE;
|
||||
} else if (bdesc_data_len > data_len) {
|
||||
SPDK_ERRLOG("Error - bdesc_data_len (%d) > data_len (%d)",
|
||||
bdesc_data_len, data_len);
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return SPDK_SCSI_TASK_COMPLETE;
|
||||
}
|
||||
|
||||
task->blockdev_io = spdk_bdev_unmap(bdev, task->ch, (struct spdk_scsi_unmap_bdesc *)&data[8],
|
||||
task->blockdev_io = spdk_bdev_unmap(bdev, task->ch, desc,
|
||||
bdesc_count, spdk_bdev_scsi_task_complete,
|
||||
task);
|
||||
|
||||
@ -1504,7 +1529,6 @@ spdk_bdev_scsi_process_block(struct spdk_bdev *bdev,
|
||||
uint32_t xfer_len;
|
||||
uint32_t len = 0;
|
||||
uint8_t *cdb = task->cdb;
|
||||
uint8_t *data;
|
||||
|
||||
/* XXX: We need to support FUA bit for writes! */
|
||||
switch (cdb[0]) {
|
||||
@ -1547,13 +1571,11 @@ spdk_bdev_scsi_process_block(struct spdk_bdev *bdev,
|
||||
}
|
||||
to_be32(&buffer[4], bdev->blocklen);
|
||||
|
||||
len = task->length < 8 ? task->length : sizeof(buffer);
|
||||
if (len) {
|
||||
data = spdk_scsi_task_alloc_data(task, len);
|
||||
memcpy(data, buffer, len);
|
||||
}
|
||||
len = SPDK_MIN(task->length, sizeof(buffer));
|
||||
if (spdk_scsi_task_scatter_data(task, buffer, len) < 0)
|
||||
break;
|
||||
|
||||
task->data_transferred = 8;
|
||||
task->data_transferred = len;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
break;
|
||||
}
|
||||
@ -1574,12 +1596,9 @@ spdk_bdev_scsi_process_block(struct spdk_bdev *bdev,
|
||||
buffer[14] |= 1 << 7;
|
||||
}
|
||||
|
||||
len = from_be32(&cdb[10]);
|
||||
if (len) {
|
||||
len = len < sizeof(buffer) ? len : sizeof(buffer);
|
||||
data = spdk_scsi_task_alloc_data(task, len);
|
||||
memcpy(data, buffer, len);
|
||||
}
|
||||
len = SPDK_MIN(from_be32(&cdb[10]), sizeof(buffer));
|
||||
if (spdk_scsi_task_scatter_data(task, buffer, len) < 0)
|
||||
break;
|
||||
|
||||
task->data_transferred = len;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
@ -1618,38 +1637,48 @@ spdk_bdev_scsi_process_block(struct spdk_bdev *bdev,
|
||||
return SPDK_SCSI_TASK_COMPLETE;
|
||||
}
|
||||
|
||||
static int
|
||||
spdk_bdev_scsi_check_len(struct spdk_scsi_task *task, int len, int min_len)
|
||||
{
|
||||
if (len >= min_len)
|
||||
return 0;
|
||||
|
||||
/* INVALID FIELD IN CDB */
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
struct spdk_scsi_task *task)
|
||||
{
|
||||
int alloc_len;
|
||||
int data_len;
|
||||
int alloc_len = -1;
|
||||
int data_len = -1;
|
||||
uint8_t *cdb = task->cdb;
|
||||
uint8_t *data;
|
||||
uint8_t *data = NULL;
|
||||
int rc = 0;
|
||||
int pllen, md = 0;
|
||||
int pf, sp;
|
||||
int bdlen, llba;
|
||||
int dbd, pc, page, subpage;
|
||||
int cmd_parsed = 0;
|
||||
|
||||
|
||||
switch (cdb[0]) {
|
||||
case SPDK_SPC_INQUIRY:
|
||||
alloc_len = from_be16(&cdb[3]);
|
||||
if (alloc_len) {
|
||||
data = spdk_scsi_task_alloc_data(task, alloc_len);
|
||||
} else {
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
data_len = spdk_bdev_scsi_inquiry(bdev, task, cdb,
|
||||
data, alloc_len);
|
||||
if (data_len < 0) {
|
||||
data_len = SPDK_MAX(4096, alloc_len);
|
||||
data = spdk_zmalloc(data_len, 0, NULL);
|
||||
rc = spdk_bdev_scsi_inquiry(bdev, task, cdb, data, data_len);
|
||||
data_len = SPDK_MIN(rc, data_len);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
SPDK_TRACEDUMP(SPDK_TRACE_DEBUG, "INQUIRY", data, data_len);
|
||||
task->data_transferred = (uint64_t)data_len;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
break;
|
||||
|
||||
case SPDK_SPC_REPORT_LUNS: {
|
||||
@ -1659,18 +1688,16 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
SPDK_TRACELOG(SPDK_TRACE_DEBUG, "sel=%x\n", sel);
|
||||
|
||||
alloc_len = from_be32(&cdb[6]);
|
||||
if (alloc_len < 16) {
|
||||
/* INVALID FIELD IN CDB */
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
rc = spdk_bdev_scsi_check_len(task, alloc_len, 16);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
data = spdk_scsi_task_alloc_data(task, alloc_len);
|
||||
data_len = spdk_bdev_scsi_report_luns(task->lun, sel, data, task->alloc_len);
|
||||
if (data_len < 0) {
|
||||
data_len = SPDK_MAX(4096, alloc_len);
|
||||
data = spdk_zmalloc(data_len, 0, NULL);
|
||||
rc = spdk_bdev_scsi_report_luns(task->lun, sel, data, data_len);
|
||||
data_len = rc;
|
||||
if (rc < 0) {
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_NO_SENSE,
|
||||
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
|
||||
@ -1679,61 +1706,62 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
}
|
||||
|
||||
SPDK_TRACEDUMP(SPDK_TRACE_DEBUG, "REPORT LUNS", data, data_len);
|
||||
task->data_transferred = (uint64_t)data_len;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
break;
|
||||
}
|
||||
|
||||
case SPDK_SPC_MODE_SELECT_6:
|
||||
case SPDK_SPC_MODE_SELECT_10:
|
||||
data = task->iovs[0].iov_base;
|
||||
|
||||
if (cdb[0] == SPDK_SPC_MODE_SELECT_6) {
|
||||
/* MODE_SELECT(6) must have at least a 4 byte header. */
|
||||
md = 4;
|
||||
pllen = cdb[4];
|
||||
} else {
|
||||
/* MODE_SELECT(10) must have at least an 8 byte header. */
|
||||
md = 8;
|
||||
pllen = from_be16(&cdb[7]);
|
||||
}
|
||||
|
||||
if (pllen == 0) {
|
||||
task->data_transferred = 0;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
break;
|
||||
} else if (cdb[0] == SPDK_SPC_MODE_SELECT_6 && pllen < 4) {
|
||||
/* MODE_SELECT(6) must have at least a 4 byte header. */
|
||||
/* INVALID FIELD IN CDB */
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
break;
|
||||
} else if (cdb[0] == SPDK_SPC_MODE_SELECT_10 && pllen < 8) {
|
||||
/* MODE_SELECT(10) must have at least an 8 byte header. */
|
||||
/* INVALID FIELD IN CDB */
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
rc = spdk_bdev_scsi_check_len(task, pllen, md);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
data = spdk_scsi_task_gather_data(task, &rc);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
data_len = rc;
|
||||
if (cdb[0] == SPDK_SPC_MODE_SELECT_6) {
|
||||
rc = spdk_bdev_scsi_check_len(task, data_len, 4);
|
||||
if (rc >= 0) {
|
||||
bdlen = data[3];
|
||||
}
|
||||
|
||||
} else {
|
||||
rc = spdk_bdev_scsi_check_len(task, data_len, 8);
|
||||
if (rc >= 0) {
|
||||
bdlen = from_be16(&data[6]);
|
||||
}
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
pf = !!(cdb[1] & 0x10);
|
||||
sp = !!(cdb[1] & 0x1);
|
||||
|
||||
/* page data */
|
||||
data_len = spdk_bdev_scsi_mode_select_page(
|
||||
rc = spdk_bdev_scsi_mode_select_page(
|
||||
bdev, cdb,
|
||||
pf, sp,
|
||||
&data[md + bdlen],
|
||||
pllen - (md + bdlen));
|
||||
if (data_len != 0) {
|
||||
if (rc < 0) {
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_NO_SENSE,
|
||||
SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
|
||||
@ -1741,8 +1769,8 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
break;
|
||||
}
|
||||
|
||||
task->data_transferred = pllen;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
rc = pllen;
|
||||
data_len = 0;
|
||||
break;
|
||||
|
||||
case SPDK_SPC_MODE_SENSE_6:
|
||||
@ -1764,11 +1792,23 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
subpage = cdb[3];
|
||||
|
||||
/* First call with no buffer to discover needed buffer size */
|
||||
data_len = spdk_bdev_scsi_mode_sense(bdev, md,
|
||||
rc = spdk_bdev_scsi_mode_sense(bdev, md,
|
||||
cdb, dbd, llba, pc,
|
||||
page, subpage,
|
||||
NULL);
|
||||
if (data_len < 0) {
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
data_len = rc;
|
||||
data = spdk_zmalloc(data_len, 0, NULL);
|
||||
|
||||
/* First call with no buffer to discover needed buffer size */
|
||||
rc = spdk_bdev_scsi_mode_sense(bdev, md,
|
||||
cdb, dbd, llba, pc,
|
||||
page, subpage,
|
||||
data);
|
||||
if (rc < 0) {
|
||||
/* INVALID FIELD IN CDB */
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
@ -1776,29 +1816,6 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (alloc_len > data_len)
|
||||
alloc_len = data_len;
|
||||
|
||||
if (alloc_len) {
|
||||
uint8_t *buffer;
|
||||
|
||||
data = spdk_scsi_task_alloc_data(task, alloc_len);
|
||||
buffer = alloc_len < data_len ? spdk_zmalloc(data_len, 0, NULL) : data;
|
||||
|
||||
data_len = spdk_bdev_scsi_mode_sense(bdev, md,
|
||||
cdb, dbd, llba, pc,
|
||||
page, subpage,
|
||||
buffer);
|
||||
assert(data_len >= 0);
|
||||
if (buffer != data) {
|
||||
memcpy(data, buffer, alloc_len);
|
||||
spdk_free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
task->data_transferred = (uint64_t)data_len;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
break;
|
||||
|
||||
case SPDK_SPC_REQUEST_SENSE: {
|
||||
@ -1816,7 +1833,6 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
}
|
||||
|
||||
alloc_len = cdb[4];
|
||||
data = spdk_scsi_task_alloc_data(task, alloc_len);
|
||||
|
||||
/* NO ADDITIONAL SENSE INFORMATION */
|
||||
sk = SPDK_SCSI_SENSE_NO_SENSE;
|
||||
@ -1826,9 +1842,8 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
|
||||
|
||||
data_len = task->sense_data_len;
|
||||
data = spdk_zmalloc(data_len, 0, NULL);
|
||||
memcpy(data, task->sense_data, data_len);
|
||||
task->data_transferred = (uint64_t)data_len;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1845,6 +1860,7 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
rc = -1;
|
||||
break;
|
||||
|
||||
case SPDK_SPC_TEST_UNIT_READY:
|
||||
@ -1855,14 +1871,27 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
|
||||
SPDK_TRACELOG(SPDK_TRACE_SCSI, "START_STOP_UNIT\n");
|
||||
}
|
||||
|
||||
task->data_transferred = 0;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
rc = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return SPDK_SCSI_TASK_UNKNOWN;
|
||||
}
|
||||
|
||||
if (rc >= 0 && data_len > 0) {
|
||||
assert(alloc_len >= 0);
|
||||
spdk_scsi_task_scatter_data(task, data, SPDK_MIN(alloc_len, data_len));
|
||||
rc = SPDK_MIN(data_len, alloc_len);
|
||||
}
|
||||
|
||||
if (rc >= 0) {
|
||||
task->data_transferred = rc;
|
||||
task->status = SPDK_SCSI_STATUS_GOOD;
|
||||
}
|
||||
|
||||
if (data)
|
||||
spdk_free(data);
|
||||
|
||||
return SPDK_SCSI_TASK_COMPLETE;
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,25 @@
|
||||
|
||||
#include "spdk_internal/log.h"
|
||||
|
||||
|
||||
/**
|
||||
* Macro to return the minimum of two numbers
|
||||
*/
|
||||
#define SPDK_MIN(a, b) ({ \
|
||||
typeof (a) _a = (a); \
|
||||
typeof (b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Macro to return the maximum of two numbers
|
||||
*/
|
||||
#define SPDK_MAX(a, b) ({ \
|
||||
typeof (a) _a = (a); \
|
||||
typeof (b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
enum {
|
||||
SPDK_SCSI_TASK_UNKNOWN = -1,
|
||||
SPDK_SCSI_TASK_COMPLETE,
|
||||
|
@ -122,21 +122,87 @@ spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
|
||||
{
|
||||
assert(task->alloc_len == 0);
|
||||
|
||||
/* Only one buffer is managable */
|
||||
if (task->iovcnt != 1) {
|
||||
task->iov.iov_base = spdk_zmalloc(alloc_len, 0, NULL);
|
||||
task->iov.iov_len = alloc_len;
|
||||
task->alloc_len = alloc_len;
|
||||
|
||||
return task->iov.iov_base;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len)
|
||||
{
|
||||
size_t len = 0;
|
||||
size_t buf_left = buf_len;
|
||||
int i;
|
||||
struct iovec *iovs = task->iovs;
|
||||
const uint8_t *pos;
|
||||
|
||||
if (buf_len == 0)
|
||||
return 0;
|
||||
|
||||
if (task->iovcnt == 1 && iovs[0].iov_base == NULL) {
|
||||
spdk_scsi_task_alloc_data(task, buf_len);
|
||||
iovs[0] = task->iov;
|
||||
}
|
||||
|
||||
for (i = 0; i < task->iovcnt; i++) {
|
||||
assert(iovs[i].iov_base != NULL);
|
||||
len += iovs[i].iov_len;
|
||||
}
|
||||
|
||||
if (len < buf_len) {
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = src;
|
||||
|
||||
for (i = 0; i < task->iovcnt; i++) {
|
||||
len = SPDK_MIN(iovs[i].iov_len, buf_left);
|
||||
buf_left -= len;
|
||||
memcpy(iovs[i].iov_base, pos, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return buf_len;
|
||||
}
|
||||
|
||||
void *
|
||||
spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len)
|
||||
{
|
||||
int i;
|
||||
struct iovec *iovs = task->iovs;
|
||||
size_t buf_len = 0;
|
||||
uint8_t *buf, *pos;
|
||||
|
||||
for (i = 0; i < task->iovcnt; i++) {
|
||||
assert(iovs[i].iov_base != NULL);
|
||||
buf_len += iovs[i].iov_len;
|
||||
}
|
||||
|
||||
if (buf_len == 0) {
|
||||
*len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This is workaround for buffers shorter than 4kb */
|
||||
if (task->iov.iov_base == NULL) {
|
||||
task->iov.iov_base = spdk_zmalloc(alloc_len, 0, NULL);
|
||||
task->alloc_len = alloc_len;
|
||||
buf = spdk_malloc(buf_len, 0, NULL);
|
||||
if (buf == NULL) {
|
||||
*len = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task->iov.iov_len = alloc_len;
|
||||
assert(&task->iov == task->iovs);
|
||||
pos = buf;
|
||||
for (i = 0; i < task->iovcnt; i++) {
|
||||
memcpy(pos, iovs[i].iov_base, iovs[i].iov_len);
|
||||
pos += iovs[i].iov_len;
|
||||
}
|
||||
|
||||
return task->iov.iov_base;
|
||||
*len = buf_len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
#include "spdk_cunit.h"
|
||||
|
||||
#include "task.c"
|
||||
#include "lun.c"
|
||||
#include "lun_db.c"
|
||||
|
||||
@ -55,6 +56,12 @@ void spdk_trace_record(uint16_t tpoint_id, uint16_t poller_id, uint32_t size,
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
spdk_lun_ut_free_task(struct spdk_scsi_task *task)
|
||||
{
|
||||
free(task);
|
||||
}
|
||||
|
||||
static struct spdk_scsi_task *
|
||||
spdk_get_task(uint32_t *owner_task_ctr)
|
||||
{
|
||||
@ -65,43 +72,41 @@ spdk_get_task(uint32_t *owner_task_ctr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task->id = g_task_count;
|
||||
task->iovs = &task->iov;
|
||||
task->iovcnt = 1;
|
||||
|
||||
g_task_count++;
|
||||
spdk_scsi_task_construct(task, &g_task_count, NULL);
|
||||
task->free_fn = spdk_lun_ut_free_task;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_task_put(struct spdk_scsi_task *task)
|
||||
void *
|
||||
spdk_malloc(size_t size, size_t align, uint64_t *phys_addr)
|
||||
{
|
||||
g_task_count--;
|
||||
if (task->alloc_len) {
|
||||
free(task->iov.iov_base);
|
||||
}
|
||||
free(task);
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
|
||||
int asc, int ascq)
|
||||
{
|
||||
task->status = sc;
|
||||
void *buf = malloc(size);
|
||||
if (phys_addr)
|
||||
*phys_addr = (uint64_t)buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void *
|
||||
spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
|
||||
spdk_zmalloc(size_t size, size_t align, uint64_t *phys_addr)
|
||||
{
|
||||
if (alloc_len < 4096)
|
||||
alloc_len = 4096;
|
||||
void *buf = calloc(size, 1);
|
||||
if (phys_addr)
|
||||
*phys_addr = (uint64_t)buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
task->iovs = &task->iov;
|
||||
task->iov.iov_base = malloc(alloc_len);
|
||||
task->iov.iov_len = alloc_len;
|
||||
task->alloc_len = alloc_len;
|
||||
return task->iov.iov_base;
|
||||
void
|
||||
spdk_free(void *buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_free_io(struct spdk_bdev_io *bdev_io)
|
||||
{
|
||||
CU_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev,
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "task.c"
|
||||
#include "scsi_bdev.c"
|
||||
|
||||
#include "spdk_cunit.h"
|
||||
@ -43,12 +44,23 @@ SPDK_LOG_REGISTER_TRACE_FLAG("scsi", SPDK_TRACE_SCSI)
|
||||
|
||||
struct spdk_scsi_globals g_spdk_scsi;
|
||||
|
||||
void *
|
||||
spdk_malloc(size_t size, size_t align, uint64_t *phys_addr)
|
||||
{
|
||||
void *buf = malloc(size);
|
||||
if (phys_addr)
|
||||
*phys_addr = (uint64_t)buf;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void *
|
||||
spdk_zmalloc(size_t size, size_t align, uint64_t *phys_addr)
|
||||
{
|
||||
void *buf = calloc(size, 1);
|
||||
if (phys_addr)
|
||||
*phys_addr = (uint64_t)buf;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -57,6 +69,14 @@ spdk_free(void *buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_free_io(struct spdk_bdev_io *bdev_io)
|
||||
{
|
||||
CU_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_lun_clear_all(struct spdk_scsi_lun *lun)
|
||||
{
|
||||
@ -67,19 +87,15 @@ spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *ta
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
|
||||
int asc, int ascq)
|
||||
{
|
||||
spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
|
||||
task->status = sc;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
spdk_put_task(struct spdk_scsi_task *task)
|
||||
{
|
||||
spdk_scsi_task_free_data(task);
|
||||
if (task->alloc_len)
|
||||
free(task->iov.iov_base);
|
||||
|
||||
task->iov.iov_base = NULL;
|
||||
task->iov.iov_len = 0;
|
||||
task->alloc_len = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -92,88 +108,6 @@ spdk_init_task(struct spdk_scsi_task *task)
|
||||
task->iovcnt = 1;
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len)
|
||||
{
|
||||
assert(task->alloc_len == 0);
|
||||
task->iov.iov_base = data;
|
||||
task->iov.iov_len = len;
|
||||
task->alloc_len = 0;
|
||||
}
|
||||
|
||||
void *
|
||||
spdk_scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
|
||||
{
|
||||
if (task->iov.iov_base != NULL) {
|
||||
if (task->alloc_len != 0 && alloc_len > task->alloc_len) {
|
||||
spdk_put_task(task);
|
||||
} else if (task->alloc_len == 0 && alloc_len > task->iov.iov_len) {
|
||||
/* External data buffer less than requested. */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (task->iov.iov_base == NULL) {
|
||||
task->iov.iov_base = calloc(alloc_len, 1);
|
||||
task->alloc_len = alloc_len;
|
||||
}
|
||||
|
||||
task->iov.iov_len = alloc_len;
|
||||
return task->iov.iov_base;
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_task_free_data(struct spdk_scsi_task *task)
|
||||
{
|
||||
if (task->alloc_len)
|
||||
free(task->iov.iov_base);
|
||||
|
||||
task->iov.iov_base = NULL;
|
||||
task->iov.iov_len = 0;
|
||||
task->alloc_len = 0;
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq)
|
||||
{
|
||||
uint8_t *cp;
|
||||
int resp_code;
|
||||
|
||||
resp_code = 0x70; /* Current + Fixed format */
|
||||
|
||||
/* Sense Data */
|
||||
cp = task->sense_data;
|
||||
|
||||
/* VALID(7) RESPONSE CODE(6-0) */
|
||||
cp[0] = 0x80 | resp_code;
|
||||
/* Obsolete */
|
||||
cp[1] = 0;
|
||||
/* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
|
||||
cp[2] = sk & 0xf;
|
||||
/* INFORMATION */
|
||||
memset(&cp[3], 0, 4);
|
||||
|
||||
/* ADDITIONAL SENSE LENGTH */
|
||||
cp[7] = 10;
|
||||
|
||||
/* COMMAND-SPECIFIC INFORMATION */
|
||||
memset(&cp[8], 0, 4);
|
||||
/* ADDITIONAL SENSE CODE */
|
||||
cp[12] = asc;
|
||||
/* ADDITIONAL SENSE CODE QUALIFIER */
|
||||
cp[13] = ascq;
|
||||
/* FIELD REPLACEABLE UNIT CODE */
|
||||
cp[14] = 0;
|
||||
|
||||
/* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
|
||||
cp[15] = 0;
|
||||
cp[16] = 0;
|
||||
cp[17] = 0;
|
||||
|
||||
/* SenseLength */
|
||||
task->sense_data_len = 18;
|
||||
}
|
||||
|
||||
void
|
||||
spdk_scsi_nvme_translate(struct spdk_bdev_io *bdev_io, int *sc, int *sk,
|
||||
int *asc, int *ascq)
|
||||
|
Loading…
Reference in New Issue
Block a user