aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2012-05-08 09:37:12 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2012-05-08 09:37:12 -0500
commite45bca682ca1b63cdfba9c8a6b164d1838a2eda4 (patch)
treec3661adbafcc6ff55b22a2138865a155e8d564c0
parent233ffa1653b49755e841efdccbd2ac63272ce0c8 (diff)
parent68bd348ade453821fd5378479e6718e69bf181f1 (diff)
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
-rw-r--r--block/iscsi.c86
-rwxr-xr-xconfigure5
-rw-r--r--hw/scsi-bus.c100
-rw-r--r--hw/scsi-defs.h1
-rw-r--r--hw/scsi-disk.c66
5 files changed, 171 insertions, 87 deletions
diff --git a/block/iscsi.c b/block/iscsi.c
index 5222726d0f..d37c4ee171 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -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)
diff --git a/configure b/configure
index 62aa7609e1..fd424ef0e9 100755
--- a/configure
+++ b/configure
@@ -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"
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index dbdb99ce35..8ab9bcda86 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -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 we already have a pending unit attention condition,
- * report this one before triggering another one.
- */
- !(buf[0] == REQUEST_SENSE && d->sense_is_ua))) {
+ 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))) {
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);
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index ca24192d53..219c84dfb1 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -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
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index a029ab6e84..045c764d9b 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -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(),
};