Merge remote-tracking branch 'bonzini/scsi-next' into staging

* bonzini/scsi-next:
  scsi: Add assertion for use-after-free errors
  scsi: remove useless debug messages
  scsi: set VALID bit to 0 in fixed format sense data
  scsi: do not require a minimum allocation length for REQUEST SENSE
  scsi: do not require a minimum allocation length for INQUIRY
  scsi: parse 16-byte tape CDBs
  scsi: do not report bogus overruns for commands in the 0x00-0x1F range
  scsi-disk: add dpofua property
  scsi: change "removable" field to host many features
  scsi: Specify the xfer direction for UNMAP and ATA_PASSTHROUGH commands
  scsi: fix WRITE SAME transfer length and direction
  scsi: fix refcounting for reads
  scsi: prevent data transfer overflow
  ISCSI: Add support for thin-provisioning via discard/UNMAP and bigger LUNs
This commit is contained in:
Anthony Liguori 2012-05-08 09:37:12 -05:00
commit e45bca682c
5 changed files with 170 additions and 86 deletions

View File

@ -383,6 +383,65 @@ iscsi_aio_flush(BlockDriverState *bs,
return &acb->common;
}
static void
iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *opaque)
{
IscsiAIOCB *acb = opaque;
if (acb->canceled != 0) {
qemu_aio_release(acb);
scsi_free_scsi_task(acb->task);
acb->task = NULL;
return;
}
acb->status = 0;
if (status < 0) {
error_report("Failed to unmap data on iSCSI lun. %s",
iscsi_get_error(iscsi));
acb->status = -EIO;
}
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
scsi_free_scsi_task(acb->task);
acb->task = NULL;
}
static BlockDriverAIOCB *
iscsi_aio_discard(BlockDriverState *bs,
int64_t sector_num, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
IscsiLun *iscsilun = bs->opaque;
struct iscsi_context *iscsi = iscsilun->iscsi;
IscsiAIOCB *acb;
struct unmap_list list[1];
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
acb->iscsilun = iscsilun;
acb->canceled = 0;
list[0].lba = sector_qemu2lun(sector_num, iscsilun);
list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size;
acb->task = iscsi_unmap_task(iscsi, iscsilun->lun,
0, 0, &list[0], 1,
iscsi_unmap_cb,
acb);
if (acb->task == NULL) {
error_report("iSCSI: Failed to send unmap command. %s",
iscsi_get_error(iscsi));
qemu_aio_release(acb);
return NULL;
}
iscsi_set_events(iscsilun);
return &acb->common;
}
static int64_t
iscsi_getlength(BlockDriverState *bs)
{
@ -396,11 +455,11 @@ iscsi_getlength(BlockDriverState *bs)
}
static void
iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *opaque)
{
struct IscsiTask *itask = opaque;
struct scsi_readcapacity10 *rc10;
struct scsi_readcapacity16 *rc16;
struct scsi_task *task = command_data;
if (status != 0) {
@ -412,26 +471,25 @@ iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
return;
}
rc10 = scsi_datain_unmarshall(task);
if (rc10 == NULL) {
error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
rc16 = scsi_datain_unmarshall(task);
if (rc16 == NULL) {
error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
itask->status = 1;
itask->complete = 1;
scsi_free_scsi_task(task);
return;
}
itask->iscsilun->block_size = rc10->block_size;
itask->iscsilun->num_blocks = rc10->lba;
itask->bs->total_sectors = (uint64_t)rc10->lba *
rc10->block_size / BDRV_SECTOR_SIZE ;
itask->iscsilun->block_size = rc16->block_length;
itask->iscsilun->num_blocks = rc16->returned_lba + 1;
itask->bs->total_sectors = itask->iscsilun->num_blocks *
itask->iscsilun->block_size / BDRV_SECTOR_SIZE ;
itask->status = 0;
itask->complete = 1;
scsi_free_scsi_task(task);
}
static void
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
void *opaque)
@ -445,10 +503,10 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
return;
}
task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
iscsi_readcapacity10_cb, opaque);
task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun,
iscsi_readcapacity16_cb, opaque);
if (task == NULL) {
error_report("iSCSI: failed to send readcapacity command.");
error_report("iSCSI: failed to send readcapacity16 command.");
itask->status = 1;
itask->complete = 1;
return;
@ -700,6 +758,8 @@ static BlockDriver bdrv_iscsi = {
.bdrv_aio_readv = iscsi_aio_readv,
.bdrv_aio_writev = iscsi_aio_writev,
.bdrv_aio_flush = iscsi_aio_flush,
.bdrv_aio_discard = iscsi_aio_discard,
};
static void iscsi_block_init(void)

5
configure vendored
View File

@ -2546,10 +2546,13 @@ fi
##########################################
# Do we have libiscsi
# We check for iscsi_unmap_sync() to make sure we have a
# recent enough version of libiscsi.
if test "$libiscsi" != "no" ; then
cat > $TMPC << EOF
#include <stdio.h>
#include <iscsi/iscsi.h>
int main(void) { iscsi_create_context(""); return 0; }
int main(void) { iscsi_unmap_sync(NULL,0,0,0,NULL,0); return 0; }
EOF
if compile_prog "-Werror" "-liscsi" ; then
libiscsi="yes"

View File

@ -239,6 +239,18 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
return res;
}
static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf)
{
scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
scsi_req_complete(req, CHECK_CONDITION);
return 0;
}
static const struct SCSIReqOps reqops_invalid_field = {
.size = sizeof(SCSIRequest),
.send_command = scsi_invalid_field
};
/* SCSIReqOps implementation for invalid commands. */
static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
@ -355,10 +367,6 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
if (r->req.cmd.buf[1] & 0x1) {
/* Vital product data */
uint8_t page_code = r->req.cmd.buf[2];
if (r->req.cmd.xfer < 4) {
return false;
}
r->buf[r->len++] = page_code ; /* this page */
r->buf[r->len++] = 0x00;
@ -386,10 +394,6 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
}
/* PAGE CODE == 0 */
if (r->req.cmd.xfer < 5) {
return false;
}
r->len = MIN(r->req.cmd.xfer, 36);
memset(r->buf, 0, r->len);
if (r->req.lun != 0) {
@ -423,9 +427,6 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
}
break;
case REQUEST_SENSE:
if (req->cmd.xfer < 4) {
goto illegal_request;
}
r->len = scsi_device_get_sense(r->req.dev, r->buf,
MIN(req->cmd.xfer, sizeof r->buf),
(req->cmd.buf[1] & 1) == 0);
@ -517,23 +518,25 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
cmd.lba);
}
if ((d->unit_attention.key == UNIT_ATTENTION ||
bus->unit_attention.key == UNIT_ATTENTION) &&
(buf[0] != INQUIRY &&
buf[0] != REPORT_LUNS &&
buf[0] != GET_CONFIGURATION &&
buf[0] != GET_EVENT_STATUS_NOTIFICATION &&
if (cmd.xfer > INT32_MAX) {
req = scsi_req_alloc(&reqops_invalid_field, d, tag, lun, hba_private);
} else if ((d->unit_attention.key == UNIT_ATTENTION ||
bus->unit_attention.key == UNIT_ATTENTION) &&
(buf[0] != INQUIRY &&
buf[0] != REPORT_LUNS &&
buf[0] != GET_CONFIGURATION &&
buf[0] != GET_EVENT_STATUS_NOTIFICATION &&
/*
* If we already have a pending unit attention condition,
* report this one before triggering another one.
*/
!(buf[0] == REQUEST_SENSE && d->sense_is_ua))) {
/*
* If we already have a pending unit attention condition,
* report this one before triggering another one.
*/
!(buf[0] == REQUEST_SENSE && d->sense_is_ua))) {
req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun,
hba_private);
} else if (lun != d->lun ||
buf[0] == REPORT_LUNS ||
(buf[0] == REQUEST_SENSE && (d->sense_len || cmd.xfer < 4))) {
buf[0] == REPORT_LUNS ||
(buf[0] == REQUEST_SENSE && d->sense_len)) {
req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
hba_private);
} else {
@ -646,7 +649,7 @@ void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag,
sense.key, sense.asc, sense.ascq);
memset(req->sense, 0, 18);
req->sense[0] = 0xf0;
req->sense[0] = 0x70;
req->sense[2] = sense.key;
req->sense[7] = 10;
req->sense[12] = sense.asc;
@ -721,10 +724,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case 0:
cmd->xfer = buf[4];
cmd->len = 6;
/* length 0 means 256 blocks */
if (cmd->xfer == 0) {
cmd->xfer = 256;
}
break;
case 1:
case 2:
@ -777,7 +776,8 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case MODE_SENSE:
break;
case WRITE_SAME_10:
cmd->xfer = 1;
case WRITE_SAME_16:
cmd->xfer = dev->blocksize;
break;
case READ_CAPACITY_10:
cmd->xfer = 8;
@ -793,18 +793,26 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
cmd->xfer = buf[9] | (buf[8] << 8);
}
break;
case WRITE_6:
/* length 0 means 256 blocks */
if (cmd->xfer == 0) {
cmd->xfer = 256;
}
case WRITE_10:
case WRITE_VERIFY_10:
case WRITE_6:
case WRITE_12:
case WRITE_VERIFY_12:
case WRITE_16:
case WRITE_VERIFY_16:
cmd->xfer *= dev->blocksize;
break;
case READ_10:
case READ_6:
case READ_REVERSE:
/* length 0 means 256 blocks */
if (cmd->xfer == 0) {
cmd->xfer = 256;
}
case READ_10:
case RECOVER_BUFFERED_DATA:
case READ_12:
case READ_16:
@ -872,6 +880,16 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu
cmd->xfer *= dev->blocksize;
}
break;
case READ_16:
case READ_REVERSE_16:
case VERIFY_16:
case WRITE_16:
cmd->len = 16;
cmd->xfer = buf[14] | (buf[13] << 8) | (buf[12] << 16);
if (buf[1] & 0x01) { /* fixed */
cmd->xfer *= dev->blocksize;
}
break;
case REWIND:
case START_STOP:
cmd->len = 6;
@ -895,6 +913,10 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu
static void scsi_cmd_xfer_mode(SCSICommand *cmd)
{
if (!cmd->xfer) {
cmd->mode = SCSI_XFER_NONE;
return;
}
switch (cmd->buf[0]) {
case WRITE_6:
case WRITE_10:
@ -920,6 +942,8 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
case UPDATE_BLOCK:
case WRITE_LONG_10:
case WRITE_SAME_10:
case WRITE_SAME_16:
case UNMAP:
case SEARCH_HIGH_12:
case SEARCH_EQUAL_12:
case SEARCH_LOW_12:
@ -929,14 +953,11 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
case SEND_DVD_STRUCTURE:
case PERSISTENT_RESERVE_OUT:
case MAINTENANCE_OUT:
case ATA_PASSTHROUGH:
cmd->mode = SCSI_XFER_TO_DEV;
break;
default:
if (cmd->xfer)
cmd->mode = SCSI_XFER_FROM_DEV;
else {
cmd->mode = SCSI_XFER_NONE;
}
cmd->mode = SCSI_XFER_FROM_DEV;
break;
}
}
@ -1127,7 +1148,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len,
memset(buf, 0, len);
if (fixed) {
/* Return fixed format sense buffer */
buf[0] = 0xf0;
buf[0] = 0x70;
buf[2] = sense.key;
buf[7] = 10;
buf[12] = sense.asc;
@ -1270,6 +1291,7 @@ SCSIRequest *scsi_req_ref(SCSIRequest *req)
void scsi_req_unref(SCSIRequest *req)
{
assert(req->refcount > 0);
if (--req->refcount == 0) {
if (req->ops->free_req) {
req->ops->free_req(req);

View File

@ -92,6 +92,7 @@
#define PERSISTENT_RESERVE_OUT 0x5f
#define VARLENGTH_CDB 0x7f
#define WRITE_FILEMARKS_16 0x80
#define READ_REVERSE_16 0x81
#define ALLOW_OVERWRITE 0x82
#define EXTENDED_COPY 0x83
#define ATA_PASSTHROUGH 0x85

View File

@ -28,9 +28,6 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do {} while(0)
#endif
#define BADF(fmt, ...) \
do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "qemu-common.h"
#include "qemu-error.h"
#include "scsi.h"
@ -61,10 +58,13 @@ typedef struct SCSIDiskReq {
BlockAcctCookie acct;
} SCSIDiskReq;
#define SCSI_DISK_F_REMOVABLE 0
#define SCSI_DISK_F_DPOFUA 1
struct SCSIDiskState
{
SCSIDevice qdev;
uint32_t removable;
uint32_t features;
bool media_changed;
bool media_event;
bool eject_request;
@ -296,6 +296,13 @@ static void scsi_do_read(void *opaque, int ret)
}
}
if (r->req.io_canceled) {
return;
}
/* The request is used as the AIO opaque value, so add a ref. */
scsi_req_ref(&r->req);
if (r->req.sg) {
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
r->req.resid -= r->req.sg->size;
@ -505,20 +512,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
int buflen = 0;
if (req->cmd.buf[1] & 0x2) {
/* Command support data - optional, not implemented */
BADF("optional INQUIRY command support request not implemented\n");
return -1;
}
if (req->cmd.buf[1] & 0x1) {
/* Vital product data */
uint8_t page_code = req->cmd.buf[2];
if (req->cmd.xfer < 4) {
BADF("Error: Inquiry (EVPD[%02X]) buffer size %zd is "
"less than 4\n", page_code, req->cmd.xfer);
return -1;
}
outbuf[buflen++] = s->qdev.type & 0x1f;
outbuf[buflen++] = page_code ; // this page
@ -633,8 +629,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
break;
}
default:
BADF("Error: unsupported Inquiry (EVPD[%02X]) "
"buffer size %zd\n", page_code, req->cmd.xfer);
return -1;
}
/* done with EVPD */
@ -643,18 +637,10 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
/* Standard INQUIRY data */
if (req->cmd.buf[2] != 0) {
BADF("Error: Inquiry (STANDARD) page or code "
"is non-zero [%02X]\n", req->cmd.buf[2]);
return -1;
}
/* PAGE CODE == 0 */
if (req->cmd.xfer < 5) {
BADF("Error: Inquiry (STANDARD) buffer size %zd "
"is less than 5\n", req->cmd.xfer);
return -1;
}
buflen = req->cmd.xfer;
if (buflen > SCSI_MAX_INQUIRY_LEN) {
buflen = SCSI_MAX_INQUIRY_LEN;
@ -662,7 +648,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memset(outbuf, 0, buflen);
outbuf[0] = s->qdev.type & 0x1f;
outbuf[1] = s->removable ? 0x80 : 0;
outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0;
if (s->qdev.type == TYPE_ROM) {
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
} else {
@ -1094,7 +1080,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
p = outbuf;
if (s->qdev.type == TYPE_DISK) {
dev_specific_param = 0x10; /* DPOFUA */
dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0;
if (bdrv_is_read_only(s->qdev.conf.bs)) {
dev_specific_param |= 0x80; /* Readonly. */
}
@ -1559,8 +1545,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
}
break;
case WRITE_SAME_10:
len = lduw_be_p(&buf[7]);
goto write_same;
case WRITE_SAME_16:
len = r->req.cmd.xfer / s->qdev.blocksize;
len = ldl_be_p(&buf[10]) & 0xffffffffULL;
write_same:
DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n",
r->req.cmd.lba, len);
@ -1700,7 +1689,8 @@ static int scsi_initfn(SCSIDevice *dev)
return -1;
}
if (!s->removable && !bdrv_is_inserted(s->qdev.conf.bs)) {
if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
!bdrv_is_inserted(s->qdev.conf.bs)) {
error_report("Device needs media, but drive is empty");
return -1;
}
@ -1722,7 +1712,7 @@ static int scsi_initfn(SCSIDevice *dev)
return -1;
}
if (s->removable) {
if (s->features & (1 << SCSI_DISK_F_REMOVABLE)) {
bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s);
}
bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
@ -1745,7 +1735,7 @@ static int scsi_cd_initfn(SCSIDevice *dev)
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
s->qdev.blocksize = 2048;
s->qdev.type = TYPE_ROM;
s->removable = true;
s->features |= 1 << SCSI_DISK_F_REMOVABLE;
return scsi_initfn(&s->qdev);
}
@ -1818,7 +1808,9 @@ static int get_device_type(SCSIDiskState *s)
return -1;
}
s->qdev.type = buf[0];
s->removable = (buf[1] & 0x80) != 0;
if (buf[1] & 0x80) {
s->features |= 1 << SCSI_DISK_F_REMOVABLE;
}
return 0;
}
@ -1918,7 +1910,10 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
static Property scsi_hd_properties[] = {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
DEFINE_PROP_BIT("removable", SCSIDiskState, features,
SCSI_DISK_F_REMOVABLE, false),
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
DEFINE_PROP_END_OF_LIST(),
};
@ -2020,7 +2015,10 @@ static TypeInfo scsi_block_info = {
static Property scsi_disk_properties[] = {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
DEFINE_PROP_BIT("removable", SCSIDiskState, features,
SCSI_DISK_F_REMOVABLE, false),
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
DEFINE_PROP_END_OF_LIST(),
};