scsi_bdev: respect allocation length in MODE SENSE 6 and 10

This refactor MODE SENSE 6 and 10 related functions to respect buffer
size parameter.

Change-Id: I03bad456bac0554a8bf7b56f69d1f9cf5b1991f6
Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
This commit is contained in:
Pawel Wodkowski 2016-10-20 18:20:22 +02:00 committed by Jim Harris
parent f30f0c76f1
commit 0244b5d78d
2 changed files with 104 additions and 137 deletions

View File

@ -34,6 +34,7 @@
#include "scsi_internal.h" #include "scsi_internal.h"
#include "spdk/env.h"
#include "spdk/bdev.h" #include "spdk/bdev.h"
#include "spdk/endian.h" #include "spdk/endian.h"
#include "spdk/string.h" #include "spdk/string.h"
@ -783,6 +784,9 @@ inq_error:
static void static void
mode_sense_page_init(uint8_t *buf, int len, int page, int subpage) mode_sense_page_init(uint8_t *buf, int len, int page, int subpage)
{ {
if (!buf)
return;
memset(buf, 0, len); memset(buf, 0, len);
if (subpage != 0) { if (subpage != 0) {
buf[0] = page | 0x40; /* PAGE + SPF=1 */ buf[0] = page | 0x40; /* PAGE + SPF=1 */
@ -797,9 +801,9 @@ mode_sense_page_init(uint8_t *buf, int len, int page, int subpage)
static int static int
spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev, spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
uint8_t *cdb, int pc, int page, int subpage, uint8_t *cdb, int pc, int page, int subpage,
uint8_t *data, int alloc_len) uint8_t *data)
{ {
uint8_t *cp; uint8_t *cp = data;
int len = 0; int len = 0;
int plen; int plen;
int i; int i;
@ -816,7 +820,6 @@ spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
return -1; return -1;
} }
cp = &data[len];
switch (page) { switch (page) {
case 0x00: case 0x00:
/* Vendor specific */ /* Vendor specific */
@ -874,18 +877,12 @@ spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
plen = 0x12 + 2; plen = 0x12 + 2;
mode_sense_page_init(cp, plen, page, subpage); mode_sense_page_init(cp, plen, page, subpage);
if (bdev->write_cache) if (cp && bdev->write_cache)
cp[2] |= 0x4; /* WCE */ cp[2] |= 0x4; /* WCE */
// TODO:
//fd = bdev->fd;
//rc = fcntl(fd , F_GETFL, 0);
//if (rc != -1 && !(rc & O_FSYNC))
// cp[2] |= 0x4; /* WCE=1 */
//else
// cp[2] &= 0xfb; /* WCE = 0 */
/* Read Cache Disable (RCD) = 1 */ /* Read Cache Disable (RCD) = 1 */
cp[2] |= 0x1; if (cp)
cp[2] |= 0x1;
len += plen; len += plen;
break; break;
@ -916,13 +913,11 @@ spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
len += spdk_bdev_scsi_mode_sense_page(bdev, len += spdk_bdev_scsi_mode_sense_page(bdev,
cdb, pc, page, cdb, pc, page,
0x00, 0x00,
&data[len], cp ? &cp[len] : NULL);
alloc_len);
len += spdk_bdev_scsi_mode_sense_page(bdev, len += spdk_bdev_scsi_mode_sense_page(bdev,
cdb, pc, page, cdb, pc, page,
0x01, 0x01,
&data[len], cp ? &cp[len] : NULL);
alloc_len);
break; break;
default: default:
/* 0x02-0x3e: Reserved */ /* 0x02-0x3e: Reserved */
@ -1039,7 +1034,7 @@ spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
for (i = 0x00; i < 0x3e; i ++) { for (i = 0x00; i < 0x3e; i ++) {
len += spdk_bdev_scsi_mode_sense_page( len += spdk_bdev_scsi_mode_sense_page(
bdev, cdb, pc, i, 0x00, bdev, cdb, pc, i, 0x00,
&cp[len], alloc_len); cp ? &cp[len] : NULL);
} }
break; break;
case 0xff: case 0xff:
@ -1047,12 +1042,12 @@ spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
for (i = 0x00; i < 0x3e; i ++) { for (i = 0x00; i < 0x3e; i ++) {
len += spdk_bdev_scsi_mode_sense_page( len += spdk_bdev_scsi_mode_sense_page(
bdev, cdb, pc, i, 0x00, bdev, cdb, pc, i, 0x00,
&cp[len], alloc_len); cp ? &cp[len] : NULL);
} }
for (i = 0x00; i < 0x3e; i ++) { for (i = 0x00; i < 0x3e; i ++) {
len += spdk_bdev_scsi_mode_sense_page( len += spdk_bdev_scsi_mode_sense_page(
bdev, cdb, pc, i, 0xff, bdev, cdb, pc, i, 0xff,
&cp[len], alloc_len); cp ? &cp[len] : NULL);
} }
break; break;
default: default:
@ -1065,124 +1060,75 @@ spdk_bdev_scsi_mode_sense_page(struct spdk_bdev *bdev,
} }
static int static int
spdk_bdev_scsi_mode_sense6(struct spdk_bdev *bdev, spdk_bdev_scsi_mode_sense(struct spdk_bdev *bdev, int md,
uint8_t *cdb, int dbd, int pc, int page, uint8_t *cdb, int dbd, int llbaa, int pc,
int subpage, uint8_t *data, int alloc_len) int page, int subpage, uint8_t *data)
{ {
uint8_t *cp; uint8_t *hdr, *bdesc, *pages;
int hlen, len = 0, plen; int hlen;
int total; int blen;
int llbaa = 0; int plen, total;
data[0] = 0; /* Mode Data Length */ assert(md == 6 || md == 10);
data[1] = 0; /* Medium Type */
data[2] = 0; /* Device-Specific Parameter */
data[3] = 0; /* Block Descripter Length */
hlen = 4;
cp = &data[4]; if (md == 6) {
if (dbd) { /* Disable Block Descripters */ hlen = 4;
len = 0; blen = 8; /* For MODE SENSE 6 only short LBA */
} else { } else {
if (llbaa) { hlen = 8;
/* Number of Blocks */ blen = llbaa ? 16 : 8;
to_be64(cp, bdev->blockcnt);
/* Reserved */
memset(&cp[8], 0, 4);
/* Block Length */
to_be32(&cp[12], bdev->blocklen);
len = 16;
} else {
/* Number of Blocks */
if (bdev->blockcnt > 0xffffffffULL)
memset(cp, 0xff, 4);
else
to_be32(cp, bdev->blockcnt);
/* Block Length */
to_be32(&cp[4], bdev->blocklen);
len = 8;
}
cp += len;
} }
data[3] = len; /* Block Descripter Length */ if (dbd) {
blen = 0;
}
pages = data ? &data[hlen + blen] : NULL;
plen = spdk_bdev_scsi_mode_sense_page(bdev, cdb, pc, page, plen = spdk_bdev_scsi_mode_sense_page(bdev, cdb, pc, page,
subpage, &cp[0], alloc_len); subpage,
pages);
if (plen < 0) { if (plen < 0) {
return -1; return -1;
} }
total = hlen + len + plen; total = hlen + blen + plen;
data[0] = total - 1; /* Mode Data Length */ if (data == NULL)
return total;
return total; hdr = &data[0];
} if (hlen == 4) {
hdr[0] = total - 1; /* Mode Data Length */
static int hdr[1] = 0; /* Medium Type */
spdk_bdev_scsi_mode_sense10(struct spdk_bdev *bdev, hdr[2] = 0; /* Device-Specific Parameter */
uint8_t *cdb, int dbd, int llbaa, int pc, hdr[3] = blen; /* Block Descripter Length */
int page, int subpage, uint8_t *data,
int alloc_len)
{
uint8_t *cp;
int hlen, len = 0, plen;
int total;
/* Mode Data Length */
/* Medium Type */
/* Device-Specific Parameter */
memset(data, 0, 4);
if (llbaa) {
data[4] = 0x1; /* Long LBA */
} else { } else {
data[4] = 0; /* Short LBA */ to_be16(&hdr[0], total - 2); /* Mode Data Length */
hdr[2] = 0; /* Medium Type */
hdr[3] = 0; /* Device-Specific Parameter */
hdr[4] = llbaa ? 0x1 : 0; /* Long/short LBA */
hdr[5] = 0; /* Reserved */
to_be16(&hdr[6], blen); /* Block Descripter Length */
} }
/* Reserved */ bdesc = &data[hlen];
/* Block Descripter Length */ if (blen == 16) {
memset(&data[5], 0, 3); /* Number of Blocks */
hlen = 8; to_be64(&bdesc[0], bdev->blockcnt);
/* Reserved */
memset(&bdesc[8], 0, 4);
/* Block Length */
to_be32(&bdesc[12], bdev->blocklen);
} else if (blen == 8) {
/* Number of Blocks */
if (bdev->blockcnt > 0xffffffffULL)
memset(&bdesc[0], 0xff, 4);
else
to_be32(&bdesc[0], bdev->blockcnt);
cp = &data[8]; /* Block Length */
if (dbd) { /* Disable Block Descripters */ to_be32(&bdesc[4], bdev->blocklen);
len = 0;
} else {
if (llbaa) {
/* Number of Blocks */
to_be64(cp, bdev->blockcnt);
/* Reserved */
memset(&cp[8], 0, 4);
/* Block Length */
to_be32(&cp[12], bdev->blocklen);
len = 16;
} else {
/* Number of Blocks */
if (bdev->blockcnt > 0xffffffffULL)
memset(cp, 0xff, 4);
else
to_be32(cp, bdev->blockcnt);
/* Block Length */
to_be32(&cp[4], bdev->blocklen);
len = 8;
}
cp += len;
} }
to_be16(&data[6], len); /* Block Descripter Length */
plen = spdk_bdev_scsi_mode_sense_page(bdev, cdb, pc, page,
subpage, &cp[0], alloc_len);
if (plen < 0)
return -1;
total = hlen + len + plen;
to_be16(data, total - 2); /* Mode Data Length */
return total; return total;
} }
@ -1657,7 +1603,7 @@ static int
spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev, spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
struct spdk_scsi_task *task) struct spdk_scsi_task *task)
{ {
uint32_t alloc_len; int alloc_len;
int data_len; int data_len;
uint8_t *cdb = task->cdb; uint8_t *cdb = task->cdb;
uint8_t *data; uint8_t *data;
@ -1793,24 +1739,11 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
page = cdb[2] & 0x3f; page = cdb[2] & 0x3f;
subpage = cdb[3]; subpage = cdb[3];
data = spdk_scsi_task_alloc_data(task, alloc_len); /* First call with no buffer to discover needed buffer size */
data_len = spdk_bdev_scsi_mode_sense(bdev, md,
if (md == 6) { cdb, dbd, llba, pc,
data_len = spdk_bdev_scsi_mode_sense6(bdev, page, subpage,
cdb, dbd, pc, NULL);
page, subpage,
data,
alloc_len);
} else {
data_len = spdk_bdev_scsi_mode_sense10(bdev,
cdb, dbd,
llba, pc,
page,
subpage,
data,
alloc_len);
}
if (data_len < 0) { if (data_len < 0) {
/* INVALID FIELD IN CDB */ /* INVALID FIELD IN CDB */
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
@ -1820,6 +1753,26 @@ spdk_bdev_scsi_process_primary(struct spdk_bdev *bdev,
break; 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->data_transferred = (uint64_t)data_len;
task->status = SPDK_SCSI_STATUS_GOOD; task->status = SPDK_SCSI_STATUS_GOOD;
break; break;

View File

@ -43,6 +43,20 @@ SPDK_LOG_REGISTER_TRACE_FLAG("scsi", SPDK_TRACE_SCSI)
struct spdk_scsi_globals g_spdk_scsi; struct spdk_scsi_globals g_spdk_scsi;
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;
}
void
spdk_free(void *buf)
{
free(buf);
}
void void
spdk_scsi_lun_clear_all(struct spdk_scsi_lun *lun) spdk_scsi_lun_clear_all(struct spdk_scsi_lun *lun)
{ {