Merge remote-tracking branch 'bonzini/scsi.2' into staging

Conflicts:
	hw/usb-msd.c
This commit is contained in:
Anthony Liguori 2011-05-31 08:20:56 -05:00
commit ed7ec84007
9 changed files with 867 additions and 572 deletions

View File

@ -61,10 +61,11 @@ struct ESPState {
int32_t ti_size;
uint32_t ti_rptr, ti_wptr;
uint8_t ti_buf[TI_BUFSZ];
uint32_t sense;
uint32_t status;
uint32_t dma;
SCSIBus bus;
SCSIDevice *current_dev;
SCSIRequest *current_req;
uint8_t cmdbuf[TI_BUFSZ];
uint32_t cmdlen;
uint32_t do_cmd;
@ -187,6 +188,17 @@ static void esp_dma_enable(void *opaque, int irq, int level)
}
}
static void esp_request_cancelled(SCSIRequest *req)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
if (req == s->current_req) {
scsi_req_unref(s->current_req);
s->current_req = NULL;
s->current_dev = NULL;
}
}
static uint32_t get_cmd(ESPState *s, uint8_t *buf)
{
uint32_t dmalen;
@ -209,7 +221,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
if (s->current_dev) {
/* Started a new command before the old one finished. Cancel it. */
s->current_dev->info->cancel_io(s->current_dev, 0);
scsi_req_cancel(s->current_req);
s->async_len = 0;
}
@ -232,7 +244,8 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7;
datalen = s->current_dev->info->send_command(s->current_dev, 0, buf, lun);
s->current_req = scsi_req_new(s->current_dev, 0, lun);
datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
s->rregs[ESP_RSTAT] = STAT_TC;
@ -240,11 +253,10 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
s->dma_counter = 0;
if (datalen > 0) {
s->rregs[ESP_RSTAT] |= STAT_DI;
s->current_dev->info->read_data(s->current_dev, 0);
} else {
s->rregs[ESP_RSTAT] |= STAT_DO;
s->current_dev->info->write_data(s->current_dev, 0);
}
scsi_req_continue(s->current_req);
}
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
s->rregs[ESP_RSEQ] = SEQ_CD;
@ -306,8 +318,8 @@ static void handle_satn_stop(ESPState *s)
static void write_response(ESPState *s)
{
DPRINTF("Transfer status (sense=%d)\n", s->sense);
s->ti_buf[0] = s->sense;
DPRINTF("Transfer status (status=%d)\n", s->status);
s->ti_buf[0] = s->status;
s->ti_buf[1] = 0;
if (s->dma) {
s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
@ -370,46 +382,50 @@ static void esp_do_dma(ESPState *s)
else
s->ti_size -= len;
if (s->async_len == 0) {
if (to_device) {
// ti_size is negative
s->current_dev->info->write_data(s->current_dev, 0);
} else {
s->current_dev->info->read_data(s->current_dev, 0);
scsi_req_continue(s->current_req);
/* If there is still data to be read from the device then
complete the DMA operation immediately. Otherwise defer
until the scsi layer has completed. */
if (s->dma_left == 0 && s->ti_size > 0) {
esp_dma_done(s);
if (to_device || s->dma_left != 0 || s->ti_size == 0) {
return;
}
}
} else {
/* Partially filled a scsi buffer. Complete immediately. */
esp_dma_done(s);
}
}
static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
uint32_t arg)
static void esp_command_complete(SCSIRequest *req, uint32_t status)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent);
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
if (reason == SCSI_REASON_DONE) {
DPRINTF("SCSI Command complete\n");
if (s->ti_size != 0)
if (s->ti_size != 0) {
DPRINTF("SCSI command completed unexpectedly\n");
}
s->ti_size = 0;
s->dma_left = 0;
s->async_len = 0;
if (arg)
if (status) {
DPRINTF("Command failed\n");
s->sense = arg;
}
s->status = status;
s->rregs[ESP_RSTAT] = STAT_ST;
esp_dma_done(s);
if (s->current_req) {
scsi_req_unref(s->current_req);
s->current_req = NULL;
s->current_dev = NULL;
} else {
}
}
static void esp_transfer_data(SCSIRequest *req, uint32_t len)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
s->async_len = arg;
s->async_buf = s->current_dev->info->get_buf(s->current_dev, 0);
s->async_len = len;
s->async_buf = scsi_req_get_buf(req);
if (s->dma_left) {
esp_do_dma(s);
} else if (s->dma_counter != 0 && s->ti_size <= 0) {
@ -417,7 +433,6 @@ static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
completion interrupt is deferred to here. */
esp_dma_done(s);
}
}
}
static void handle_ti(ESPState *s)
@ -678,7 +693,7 @@ static const VMStateDescription vmstate_esp = {
VMSTATE_UINT32(ti_rptr, ESPState),
VMSTATE_UINT32(ti_wptr, ESPState),
VMSTATE_BUFFER(ti_buf, ESPState),
VMSTATE_UINT32(sense, ESPState),
VMSTATE_UINT32(status, ESPState),
VMSTATE_UINT32(dma, ESPState),
VMSTATE_BUFFER(cmdbuf, ESPState),
VMSTATE_UINT32(cmdlen, ESPState),
@ -714,6 +729,12 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
*dma_enable = qdev_get_gpio_in(dev, 1);
}
static const struct SCSIBusOps esp_scsi_ops = {
.transfer_data = esp_transfer_data,
.complete = esp_command_complete,
.cancel = esp_request_cancelled
};
static int esp_init1(SysBusDevice *dev)
{
ESPState *s = FROM_SYSBUS(ESPState, dev);
@ -728,7 +749,7 @@ static int esp_init1(SysBusDevice *dev)
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete);
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops);
return scsi_bus_legacy_handle_cmdline(&s->bus);
}

View File

@ -174,6 +174,7 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
#define LSI_TAG_VALID (1 << 16)
typedef struct lsi_request {
SCSIRequest *req;
uint32_t tag;
uint32_t dma_len;
uint8_t *dma_buf;
@ -567,11 +568,9 @@ static void lsi_do_dma(LSIState *s, int out)
s->csbc += count;
s->dnad += count;
s->dbc -= count;
if (s->current->dma_buf == NULL) {
s->current->dma_buf = dev->info->get_buf(dev, s->current->tag);
s->current->dma_buf = scsi_req_get_buf(s->current->req);
}
/* ??? Set SFBR to first data byte. */
if (out) {
cpu_physical_memory_read(addr, s->current->dma_buf, count);
@ -581,13 +580,7 @@ static void lsi_do_dma(LSIState *s, int out)
s->current->dma_len -= count;
if (s->current->dma_len == 0) {
s->current->dma_buf = NULL;
if (out) {
/* Write the data. */
dev->info->write_data(dev, s->current->tag);
} else {
/* Request any remaining data. */
dev->info->read_data(dev, s->current->tag);
}
scsi_req_continue(s->current->req);
} else {
s->current->dma_buf += count;
lsi_resume_script(s);
@ -652,18 +645,55 @@ static void lsi_reselect(LSIState *s, lsi_request *p)
}
}
/* Record that data is available for a queued command. Returns zero if
the device was reselected, nonzero if the IO is deferred. */
static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
{
lsi_request *p;
QTAILQ_FOREACH(p, &s->queue, next) {
if (p->tag == tag) {
return p;
}
}
return NULL;
}
static void lsi_request_cancelled(SCSIRequest *req)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
lsi_request *p;
if (s->current && req == s->current->req) {
scsi_req_unref(req);
qemu_free(s->current);
s->current = NULL;
return;
}
p = lsi_find_by_tag(s, req->tag);
if (p) {
QTAILQ_REMOVE(&s->queue, p, next);
scsi_req_unref(req);
qemu_free(p);
}
}
/* Record that data is available for a queued command. Returns zero if
the device was reselected, nonzero if the IO is deferred. */
static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len)
{
lsi_request *p;
p = lsi_find_by_tag(s, tag);
if (!p) {
BADF("IO with unknown tag %d\n", tag);
return 1;
}
if (p->pending) {
BADF("Multiple IO pending for tag %d\n", tag);
}
p->pending = arg;
p->pending = len;
/* Reselect if waiting for it, or if reselection triggers an IRQ
and the bus is free.
Since no interrupt stacking is implemented in the emulation, it
@ -677,26 +707,20 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
return 0;
} else {
DPRINTF("Queueing IO tag=0x%x\n", tag);
p->pending = arg;
p->pending = len;
return 1;
}
}
}
BADF("IO with unknown tag %d\n", tag);
return 1;
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
uint32_t arg)
/* Callback to indicate that the SCSI layer has completed a command. */
static void lsi_command_complete(SCSIRequest *req, uint32_t status)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
if (reason == SCSI_REASON_DONE) {
DPRINTF("Command complete status=%d\n", (int)arg);
s->status = arg;
DPRINTF("Command complete status=%d\n", (int)status);
s->status = status;
s->command_complete = 2;
if (s->waiting && s->dbc != 0) {
/* Raise phase mismatch for short transfers. */
@ -705,30 +729,40 @@ static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
lsi_set_phase(s, PHASE_ST);
}
if (s->current && req == s->current->req) {
scsi_req_unref(s->current->req);
qemu_free(s->current);
s->current = NULL;
}
lsi_resume_script(s);
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
if (lsi_queue_tag(s, req->tag, len)) {
return;
}
}
if (s->waiting == 1 || !s->current || tag != s->current->tag ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
if (lsi_queue_tag(s, tag, arg))
return;
}
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
/* host adapter (re)connected */
DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg);
s->current->dma_len = arg;
DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len);
s->current->dma_len = len;
s->command_complete = 1;
if (!s->waiting)
return;
if (s->waiting) {
if (s->waiting == 1 || s->dbc == 0) {
lsi_resume_script(s);
} else {
lsi_do_dma(s, out);
}
}
}
static void lsi_do_command(LSIState *s)
@ -755,16 +789,17 @@ static void lsi_do_command(LSIState *s)
assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag;
s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun);
n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun);
n = scsi_req_enqueue(s->current->req, buf);
if (n) {
if (n > 0) {
lsi_set_phase(s, PHASE_DI);
dev->info->read_data(dev, s->current->tag);
} else if (n < 0) {
lsi_set_phase(s, PHASE_DO);
dev->info->write_data(dev, s->current->tag);
}
scsi_req_continue(s->current->req);
}
if (!s->command_complete) {
if (n) {
/* Command did not complete immediately so disconnect. */
@ -855,13 +890,15 @@ static void lsi_do_msgout(LSIState *s)
int len;
uint32_t current_tag;
SCSIDevice *current_dev;
lsi_request *p, *p_next;
lsi_request *current_req, *p, *p_next;
int id;
if (s->current) {
current_tag = s->current->tag;
current_req = s->current;
} else {
current_tag = s->select_tag;
current_req = lsi_find_by_tag(s, current_tag);
}
id = (current_tag >> 8) & 0xf;
current_dev = s->bus.devs[id];
@ -913,7 +950,9 @@ static void lsi_do_msgout(LSIState *s)
case 0x0d:
/* The ABORT TAG message clears the current I/O process only. */
DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
current_dev->info->cancel_io(current_dev, current_tag);
if (current_req) {
scsi_req_cancel(current_req->req);
}
lsi_disconnect(s);
break;
case 0x06:
@ -936,7 +975,9 @@ static void lsi_do_msgout(LSIState *s)
}
/* clear the current I/O process */
current_dev->info->cancel_io(current_dev, current_tag);
if (s->current) {
scsi_req_cancel(s->current->req);
}
/* As the current implemented devices scsi_disk and scsi_generic
only support one LUN, we don't need to keep track of LUNs.
@ -948,8 +989,7 @@ static void lsi_do_msgout(LSIState *s)
id = current_tag & 0x0000ff00;
QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
if ((p->tag & 0x0000ff00) == id) {
current_dev->info->cancel_io(current_dev, p->tag);
QTAILQ_REMOVE(&s->queue, p, next);
scsi_req_cancel(p->req);
}
}
@ -2205,6 +2245,12 @@ static int lsi_scsi_uninit(PCIDevice *d)
return 0;
}
static const struct SCSIBusOps lsi_scsi_ops = {
.transfer_data = lsi_transfer_data,
.complete = lsi_command_complete,
.cancel = lsi_request_cancelled
};
static int lsi_scsi_init(PCIDevice *dev)
{
LSIState *s = DO_UPCAST(LSIState, dev, dev);
@ -2241,7 +2287,7 @@ static int lsi_scsi_init(PCIDevice *dev)
PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc);
QTAILQ_INIT(&s->queue);
scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete);
scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops);
if (!dev->qdev.hotplugged) {
return scsi_bus_legacy_handle_cmdline(&s->bus);
}

View File

@ -4,6 +4,7 @@
#include "scsi-defs.h"
#include "qdev.h"
#include "blockdev.h"
#include "trace.h"
static char *scsibus_get_fw_dev_path(DeviceState *dev);
@ -20,13 +21,13 @@ static int next_scsi_bus;
/* Create a scsi bus, and attach devices to it. */
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
scsi_completionfn complete)
const SCSIBusOps *ops)
{
qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
bus->busnr = next_scsi_bus++;
bus->tcq = tcq;
bus->ndev = ndev;
bus->complete = complete;
bus->ops = ops;
bus->qbus.allow_hotplug = 1;
}
@ -135,42 +136,60 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
SCSIRequest *req;
req = qemu_mallocz(size);
req->refcount = 1;
req->bus = scsi_bus_from_device(d);
req->dev = d;
req->tag = tag;
req->lun = lun;
req->status = -1;
req->enqueued = true;
QTAILQ_INSERT_TAIL(&d->requests, req, next);
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
return req;
}
SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
return d->info->alloc_req(d, tag, lun);
}
QTAILQ_FOREACH(req, &d->requests, next) {
if (req->tag == tag) {
return req;
uint8_t *scsi_req_get_buf(SCSIRequest *req)
{
return req->dev->info->get_buf(req);
}
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
{
if (req->dev->info->get_sense) {
return req->dev->info->get_sense(req, buf, len);
} else {
return 0;
}
}
return NULL;
}
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf)
{
int32_t rc;
assert(!req->enqueued);
scsi_req_ref(req);
req->enqueued = true;
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
scsi_req_ref(req);
rc = req->dev->info->send_command(req, buf);
scsi_req_unref(req);
return rc;
}
static void scsi_req_dequeue(SCSIRequest *req)
{
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
if (req->enqueued) {
QTAILQ_REMOVE(&req->dev->requests, req, next);
req->enqueued = false;
scsi_req_unref(req);
}
}
void scsi_req_free(SCSIRequest *req)
{
scsi_req_dequeue(req);
qemu_free(req);
}
static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
{
switch (cmd[0] >> 5) {
@ -195,6 +214,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
req->cmd.len = 12;
break;
default:
trace_scsi_req_parse_bad(req->dev->id, req->lun, req->tag, cmd[0]);
return -1;
}
@ -392,9 +412,100 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf)
memcpy(req->cmd.buf, buf, req->cmd.len);
scsi_req_xfer_mode(req);
req->cmd.lba = scsi_req_lba(req);
trace_scsi_req_parsed(req->dev->id, req->lun, req->tag, buf[0],
req->cmd.mode, req->cmd.xfer, req->cmd.lba);
return 0;
}
/*
* Predefined sense codes
*/
/* No sense data available */
const struct SCSISense sense_code_NO_SENSE = {
.key = NO_SENSE , .asc = 0x00 , .ascq = 0x00
};
/* LUN not ready, Manual intervention required */
const struct SCSISense sense_code_LUN_NOT_READY = {
.key = NOT_READY, .asc = 0x04, .ascq = 0x03
};
/* LUN not ready, Medium not present */
const struct SCSISense sense_code_NO_MEDIUM = {
.key = NOT_READY, .asc = 0x3a, .ascq = 0x00
};
/* Hardware error, internal target failure */
const struct SCSISense sense_code_TARGET_FAILURE = {
.key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00
};
/* Illegal request, invalid command operation code */
const struct SCSISense sense_code_INVALID_OPCODE = {
.key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00
};
/* Illegal request, LBA out of range */
const struct SCSISense sense_code_LBA_OUT_OF_RANGE = {
.key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00
};
/* Illegal request, Invalid field in CDB */
const struct SCSISense sense_code_INVALID_FIELD = {
.key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00
};
/* Illegal request, LUN not supported */
const struct SCSISense sense_code_LUN_NOT_SUPPORTED = {
.key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00
};
/* Command aborted, I/O process terminated */
const struct SCSISense sense_code_IO_ERROR = {
.key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06
};
/* Command aborted, I_T Nexus loss occurred */
const struct SCSISense sense_code_I_T_NEXUS_LOSS = {
.key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07
};
/* Command aborted, Logical Unit failure */
const struct SCSISense sense_code_LUN_FAILURE = {
.key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01
};
/*
* scsi_build_sense
*
* Build a sense buffer
*/
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed)
{
if (!fixed && len < 8) {
return 0;
}
memset(buf, 0, len);
if (fixed) {
/* Return fixed format sense buffer */
buf[0] = 0xf0;
buf[2] = sense.key;
buf[7] = 7;
buf[12] = sense.asc;
buf[13] = sense.ascq;
return MIN(len, 18);
} else {
/* Return descriptor format sense buffer */
buf[0] = 0x72;
buf[1] = sense.key;
buf[2] = sense.asc;
buf[3] = sense.ascq;
return 8;
}
}
static const char *scsi_command_name(uint8_t cmd)
{
static const char *names[] = {
@ -489,6 +600,43 @@ static const char *scsi_command_name(uint8_t cmd)
return names[cmd];
}
SCSIRequest *scsi_req_ref(SCSIRequest *req)
{
req->refcount++;
return req;
}
void scsi_req_unref(SCSIRequest *req)
{
if (--req->refcount == 0) {
if (req->dev->info->free_req) {
req->dev->info->free_req(req);
}
qemu_free(req);
}
}
/* Tell the device that we finished processing this chunk of I/O. It
will start the next chunk or complete the command. */
void scsi_req_continue(SCSIRequest *req)
{
trace_scsi_req_continue(req->dev->id, req->lun, req->tag);
if (req->cmd.mode == SCSI_XFER_TO_DEV) {
req->dev->info->write_data(req);
} else {
req->dev->info->read_data(req);
}
}
/* Called by the devices when data is ready for the HBA. The HBA should
start a DMA operation to read or fill the device's data buffer.
Once it completes, calling scsi_req_continue will restart I/O. */
void scsi_req_data(SCSIRequest *req, int len)
{
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
req->bus->ops->transfer_data(req, len);
}
void scsi_req_print(SCSIRequest *req)
{
FILE *fp = stderr;
@ -520,10 +668,42 @@ void scsi_req_print(SCSIRequest *req)
void scsi_req_complete(SCSIRequest *req)
{
assert(req->status != -1);
scsi_req_ref(req);
scsi_req_dequeue(req);
req->bus->complete(req->bus, SCSI_REASON_DONE,
req->tag,
req->status);
req->bus->ops->complete(req, req->status);
scsi_req_unref(req);
}
void scsi_req_cancel(SCSIRequest *req)
{
if (req->dev && req->dev->info->cancel_io) {
req->dev->info->cancel_io(req);
}
scsi_req_ref(req);
scsi_req_dequeue(req);
if (req->bus->ops->cancel) {
req->bus->ops->cancel(req);
}
scsi_req_unref(req);
}
void scsi_req_abort(SCSIRequest *req, int status)
{
req->status = status;
if (req->dev && req->dev->info->cancel_io) {
req->dev->info->cancel_io(req);
}
scsi_req_complete(req);
}
void scsi_device_purge_requests(SCSIDevice *sdev)
{
SCSIRequest *req;
while (!QTAILQ_EMPTY(&sdev->requests)) {
req = QTAILQ_FIRST(&sdev->requests);
scsi_req_cancel(req);
}
}
static char *scsibus_get_fw_dev_path(DeviceState *dev)

View File

@ -49,14 +49,8 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
typedef struct SCSIDiskState SCSIDiskState;
typedef struct SCSISense {
uint8_t key;
} SCSISense;
typedef struct SCSIDiskReq {
SCSIRequest req;
/* ??? We should probably keep track of whether the data transfer is
a read or a write. Currently we rely on the host getting it right. */
/* Both sector and sector_count are in terms of qemu 512 byte blocks. */
uint64_t sector;
uint32_t sector_count;
@ -86,27 +80,24 @@ struct SCSIDiskState
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf);
static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag,
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
uint32_t lun)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req;
SCSIDiskReq *r;
req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun);
r = DO_UPCAST(SCSIDiskReq, req, req);
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
return r;
return req;
}
static void scsi_remove_request(SCSIDiskReq *r)
static void scsi_free_request(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
qemu_vfree(r->iov.iov_base);
scsi_req_free(&r->req);
}
static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag)
{
return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag));
}
static void scsi_disk_clear_sense(SCSIDiskState *s)
@ -114,42 +105,33 @@ static void scsi_disk_clear_sense(SCSIDiskState *s)
memset(&s->sense, 0, sizeof(s->sense));
}
static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key)
{
s->sense.key = key;
}
static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code)
static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
r->req.status = status;
scsi_disk_set_sense(s, sense_code);
s->sense = sense;
}
/* Helper function for command completion. */
static void scsi_command_complete(SCSIDiskReq *r, int status, int sense)
static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense)
{
DPRINTF("Command complete tag=0x%x status=%d sense=%d\n",
r->req.tag, status, sense);
DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n",
r->req.tag, status, sense.key, sense.asc, sense.ascq);
scsi_req_set_status(r, status, sense);
scsi_req_complete(&r->req);
scsi_remove_request(r);
}
/* Cancel a pending data transfer. */
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
static void scsi_cancel_io(SCSIRequest *req)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIDiskReq *r;
DPRINTF("Cancel tag=0x%x\n", tag);
r = scsi_find_request(s, tag);
if (r) {
if (r->req.aiocb)
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
DPRINTF("Cancel tag=0x%x\n", req->tag);
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
r->req.aiocb = NULL;
scsi_remove_request(r);
}
r->req.aiocb = NULL;
}
static void scsi_read_complete(void * opaque, int ret)
@ -170,30 +152,38 @@ static void scsi_read_complete(void * opaque, int ret)
n = r->iov.iov_len / 512;
r->sector += n;
r->sector_count -= n;
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
scsi_req_data(&r->req, r->iov.iov_len);
}
static void scsi_read_request(SCSIDiskReq *r)
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
if (r->sector_count == (uint32_t)-1) {
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
r->sector_count = 0;
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
scsi_req_data(&r->req, r->iov.iov_len);
return;
}
DPRINTF("Read sector_count=%d\n", r->sector_count);
if (r->sector_count == 0) {
scsi_command_complete(r, GOOD, NO_SENSE);
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
return;
}
/* No data transfer may already be in progress */
assert(r->req.aiocb == NULL);
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
scsi_read_complete(r, -EINVAL);
return;
}
n = r->sector_count;
if (n > SCSI_DMA_BUF_SIZE / 512)
n = SCSI_DMA_BUF_SIZE / 512;
@ -207,23 +197,6 @@ static void scsi_read_request(SCSIDiskReq *r)
}
}
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIDevice *d, uint32_t tag)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIDiskReq *r;
r = scsi_find_request(s, tag);
if (!r) {
BADF("Bad read tag 0x%x\n", tag);
/* ??? This is the wrong error. */
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
return;
}
scsi_read_request(r);
}
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
{
int is_read = (type == SCSI_REQ_STATUS_RETRY_READ);
@ -245,13 +218,24 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
vm_stop(VMSTOP_DISKFULL);
} else {
if (type == SCSI_REQ_STATUS_RETRY_READ) {
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0);
scsi_req_data(&r->req, 0);
}
switch (error) {
case ENOMEM:
scsi_command_complete(r, CHECK_CONDITION,
HARDWARE_ERROR);
SENSE_CODE(TARGET_FAILURE));
break;
case EINVAL:
scsi_command_complete(r, CHECK_CONDITION,
SENSE_CODE(INVALID_FIELD));
break;
default:
scsi_command_complete(r, CHECK_CONDITION,
SENSE_CODE(IO_ERROR));
break;
}
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
}
@ -273,7 +257,7 @@ static void scsi_write_complete(void * opaque, int ret)
r->sector += n;
r->sector_count -= n;
if (r->sector_count == 0) {
scsi_command_complete(r, GOOD, NO_SENSE);
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
} else {
len = r->sector_count * 512;
if (len > SCSI_DMA_BUF_SIZE) {
@ -281,25 +265,32 @@ static void scsi_write_complete(void * opaque, int ret)
}
r->iov.iov_len = len;
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len);
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
scsi_req_data(&r->req, len);
}
}
static void scsi_write_request(SCSIDiskReq *r)
static void scsi_write_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
/* No data transfer may already be in progress */
assert(r->req.aiocb == NULL);
if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
scsi_write_complete(r, -EINVAL);
return;
}
n = r->iov.iov_len / 512;
if (n) {
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n,
scsi_write_complete, r);
if (r->req.aiocb == NULL) {
scsi_write_complete(r, -EIO);
scsi_write_complete(r, -ENOMEM);
}
} else {
/* Invoke completion routine to fetch data from host. */
@ -307,26 +298,6 @@ static void scsi_write_request(SCSIDiskReq *r)
}
}
/* Write data to a scsi device. Returns nonzero on failure.
The transfer may complete asynchronously. */
static int scsi_write_data(SCSIDevice *d, uint32_t tag)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIDiskReq *r;
DPRINTF("Write data tag=0x%x\n", tag);
r = scsi_find_request(s, tag);
if (!r) {
BADF("Bad write tag 0x%x\n", tag);
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
return 1;
}
scsi_write_request(r);
return 0;
}
static void scsi_dma_restart_bh(void *opaque)
{
SCSIDiskState *s = opaque;
@ -347,15 +318,15 @@ static void scsi_dma_restart_bh(void *opaque)
switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) {
case SCSI_REQ_STATUS_RETRY_READ:
scsi_read_request(r);
scsi_read_data(&r->req);
break;
case SCSI_REQ_STATUS_RETRY_WRITE:
scsi_write_request(r);
scsi_write_data(&r->req);
break;
case SCSI_REQ_STATUS_RETRY_FLUSH:
ret = scsi_disk_emulate_command(r, r->iov.iov_base);
if (ret == 0) {
scsi_command_complete(r, GOOD, NO_SENSE);
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
}
}
}
@ -376,19 +347,21 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason)
}
/* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
static uint8_t *scsi_get_buf(SCSIRequest *req)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIDiskReq *r;
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
r = scsi_find_request(s, tag);
if (!r) {
BADF("Bad buffer tag 0x%x\n", tag);
return NULL;
}
return (uint8_t *)r->iov.iov_base;
}
/* Copy sense information into the provided buffer */
static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
return scsi_build_sense(s->sense, outbuf, len, len > 14);
}
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
@ -545,7 +518,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memset(outbuf, 0, buflen);
if (req->lun || req->cmd.buf[1] >> 5) {
if (req->lun) {
outbuf[0] = 0x7f; /* LUN not supported */
return buflen;
}
@ -860,19 +833,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
case REQUEST_SENSE:
if (req->cmd.xfer < 4)
goto illegal_request;
memset(outbuf, 0, 4);
buflen = 4;
if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) {
memset(outbuf, 0, 18);
buflen = 18;
outbuf[7] = 10;
/* asc 0x3a, ascq 0: Medium not present */
outbuf[12] = 0x3a;
outbuf[13] = 0;
}
outbuf[0] = 0xf0;
outbuf[1] = 0;
outbuf[2] = s->sense.key;
buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer,
req->cmd.xfer > 13);
scsi_disk_clear_sense(s);
break;
case INQUIRY:
@ -1010,17 +972,22 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
}
break;
default:
goto illegal_request;
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
return -1;
}
scsi_req_set_status(r, GOOD, NO_SENSE);
scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE));
return buflen;
not_ready:
scsi_command_complete(r, CHECK_CONDITION, NOT_READY);
if (!bdrv_is_inserted(s->bs)) {
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM));
} else {
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY));
}
return -1;
illegal_request:
scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
return -1;
}
@ -1029,33 +996,23 @@ illegal_request:
(eg. disk reads), negative for transfers to the device (eg. disk writes),
and zero if the command does not transfer any data. */
static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
uint8_t *buf, int lun)
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
uint32_t len;
int is_write;
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
int32_t len;
uint8_t command;
uint8_t *outbuf;
SCSIDiskReq *r;
int rc;
command = buf[0];
r = scsi_find_request(s, tag);
if (r) {
BADF("Tag 0x%x already in use\n", tag);
scsi_cancel_io(d, tag);
}
/* ??? Tags are not unique for different luns. We only implement a
single lun, so this should not matter. */
r = scsi_new_request(s, tag, lun);
outbuf = (uint8_t *)r->iov.iov_base;
is_write = 0;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
if (scsi_req_parse(&r->req, buf) != 0) {
BADF("Unsupported command length, command %x\n", command);
goto fail;
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
return 0;
}
#ifdef DEBUG_SCSI
{
@ -1067,11 +1024,14 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
#endif
if (lun || buf[1] >> 5) {
if (req->lun) {
/* Only LUN 0 supported. */
DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
if (command != REQUEST_SENSE && command != INQUIRY)
goto fail;
DPRINTF("Unimplemented LUN %d\n", req->lun);
if (command != REQUEST_SENSE && command != INQUIRY) {
scsi_command_complete(r, CHECK_CONDITION,
SENSE_CODE(LUN_NOT_SUPPORTED));
return 0;
}
}
switch (command) {
case TEST_UNIT_READY:
@ -1104,7 +1064,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case READ_10:
case READ_12:
case READ_16:
len = r->req.cmd.xfer / d->blocksize;
len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len);
if (r->req.cmd.lba > s->max_lba)
goto illegal_lba;
@ -1118,7 +1078,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case WRITE_VERIFY:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
len = r->req.cmd.xfer / d->blocksize;
len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
(command & 0xe) == 0xe ? "And Verify " : "",
r->req.cmd.lba, len);
@ -1126,7 +1086,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
goto illegal_lba;
r->sector = r->req.cmd.lba * s->cluster_size;
r->sector_count = len * s->cluster_size;
is_write = 1;
break;
case MODE_SELECT:
DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer);
@ -1153,7 +1112,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
break;
case WRITE_SAME_16:
len = r->req.cmd.xfer / d->blocksize;
len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
r->req.cmd.lba, len);
@ -1179,18 +1138,20 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
break;
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
return 0;
fail:
scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
return 0;
illegal_lba:
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE));
return 0;
}
if (r->sector_count == 0 && r->iov.iov_len == 0) {
scsi_command_complete(r, GOOD, NO_SENSE);
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
}
len = r->sector_count * 512 + r->iov.iov_len;
if (is_write) {
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
return -len;
} else {
if (!r->sector_count)
@ -1199,25 +1160,12 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
}
static void scsi_disk_purge_requests(SCSIDiskState *s)
{
SCSIDiskReq *r;
while (!QTAILQ_EMPTY(&s->qdev.requests)) {
r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests));
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
}
scsi_remove_request(r);
}
}
static void scsi_disk_reset(DeviceState *dev)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
uint64_t nb_sectors;
scsi_disk_purge_requests(s);
scsi_device_purge_requests(&s->qdev);
bdrv_get_geometry(s->bs, &nb_sectors);
nb_sectors /= s->cluster_size;
@ -1231,7 +1179,7 @@ static void scsi_destroy(SCSIDevice *dev)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
scsi_disk_purge_requests(s);
scsi_device_purge_requests(&s->qdev);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
@ -1321,11 +1269,14 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_hd_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
.get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
@ -1339,11 +1290,14 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_cd_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
.get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_END_OF_LIST(),
@ -1356,11 +1310,14 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_disk_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
.get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),

View File

@ -66,23 +66,49 @@ struct SCSIGenericState
uint8_t senselen;
};
static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
static void scsi_set_sense(SCSIGenericState *s, SCSISense sense)
{
s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0);
s->driver_status = SG_ERR_DRIVER_SENSE;
}
static void scsi_clear_sense(SCSIGenericState *s)
{
memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE);
s->senselen = 0;
s->driver_status = 0;
}
static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
int size = SCSI_SENSE_BUF_SIZE;
if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) {
size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf,
SCSI_SENSE_BUF_SIZE, 0);
}
if (size > len) {
size = len;
}
memcpy(outbuf, s->sensebuf, size);
return size;
}
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
return DO_UPCAST(SCSIGenericReq, req, req);
return req;
}
static void scsi_remove_request(SCSIGenericReq *r)
static void scsi_free_request(SCSIRequest *req)
{
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
qemu_free(r->buf);
scsi_req_free(&r->req);
}
static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag)
{
return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag));
}
/* Helper function for command completion. */
@ -91,13 +117,30 @@ static void scsi_command_complete(void *opaque, int ret)
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
r->req.aiocb = NULL;
s->driver_status = r->io_header.driver_status;
if (s->driver_status & SG_ERR_DRIVER_SENSE)
s->senselen = r->io_header.sb_len_wr;
if (ret != 0)
r->req.status = BUSY;
else {
if (ret != 0) {
switch (ret) {
case -EDOM:
r->req.status = TASK_SET_FULL;
break;
case -EINVAL:
r->req.status = CHECK_CONDITION;
scsi_set_sense(s, SENSE_CODE(INVALID_FIELD));
break;
case -ENOMEM:
r->req.status = CHECK_CONDITION;
scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE));
break;
default:
r->req.status = CHECK_CONDITION;
scsi_set_sense(s, SENSE_CODE(IO_ERROR));
break;
}
} else {
if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
r->req.status = BUSY;
BADF("Driver Timeout\n");
@ -112,23 +155,18 @@ static void scsi_command_complete(void *opaque, int ret)
r, r->req.tag, r->req.status);
scsi_req_complete(&r->req);
scsi_remove_request(r);
}
/* Cancel a pending data transfer. */
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
static void scsi_cancel_io(SCSIRequest *req)
{
DPRINTF("scsi_cancel_io 0x%x\n", tag);
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIGenericReq *r;
DPRINTF("Cancel tag=0x%x\n", tag);
r = scsi_find_request(s, tag);
if (r) {
if (r->req.aiocb)
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
DPRINTF("Cancel tag=0x%x\n", req->tag);
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
r->req.aiocb = NULL;
scsi_remove_request(r);
}
r->req.aiocb = NULL;
}
static int execute_command(BlockDriverState *bdrv,
@ -152,7 +190,7 @@ static int execute_command(BlockDriverState *bdrv,
r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
if (r->req.aiocb == NULL) {
BADF("execute_command: read failed !\n");
return -1;
return -ENOMEM;
}
return 0;
@ -163,6 +201,7 @@ static void scsi_read_complete(void * opaque, int ret)
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
int len;
r->req.aiocb = NULL;
if (ret) {
DPRINTF("IO error ret %d\n", ret);
scsi_command_complete(r, ret);
@ -172,27 +211,21 @@ static void scsi_read_complete(void * opaque, int ret)
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
r->len = -1;
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
if (len == 0)
if (len == 0) {
scsi_command_complete(r, 0);
} else {
scsi_req_data(&r->req, len);
}
}
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIDevice *d, uint32_t tag)
static void scsi_read_data(SCSIRequest *req)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIGenericReq *r;
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
int ret;
DPRINTF("scsi_read_data 0x%x\n", tag);
r = scsi_find_request(s, tag);
if (!r) {
BADF("Bad read tag 0x%x\n", tag);
/* ??? This is the wrong error. */
scsi_command_complete(r, -EINVAL);
return;
}
DPRINTF("scsi_read_data 0x%x\n", req->tag);
if (r->len == -1) {
scsi_command_complete(r, 0);
return;
@ -210,13 +243,15 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
r->buf[0], r->buf[1], r->buf[2], r->buf[3],
r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen);
scsi_req_data(&r->req, s->senselen);
/* Clear sensebuf after REQUEST_SENSE */
scsi_clear_sense(s);
return;
}
ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
if (ret == -1) {
scsi_command_complete(r, -EINVAL);
if (ret < 0) {
scsi_command_complete(r, ret);
return;
}
}
@ -227,6 +262,7 @@ static void scsi_write_complete(void * opaque, int ret)
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
DPRINTF("scsi_write_complete() ret = %d\n", ret);
r->req.aiocb = NULL;
if (ret) {
DPRINTF("IO error\n");
scsi_command_complete(r, ret);
@ -244,46 +280,30 @@ static void scsi_write_complete(void * opaque, int ret)
/* Write data to a scsi device. Returns nonzero on failure.
The transfer may complete asynchronously. */
static int scsi_write_data(SCSIDevice *d, uint32_t tag)
static void scsi_write_data(SCSIRequest *req)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIGenericReq *r;
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
int ret;
DPRINTF("scsi_write_data 0x%x\n", tag);
r = scsi_find_request(s, tag);
if (!r) {
BADF("Bad write tag 0x%x\n", tag);
/* ??? This is the wrong error. */
scsi_command_complete(r, -EINVAL);
return 0;
}
DPRINTF("scsi_write_data 0x%x\n", req->tag);
if (r->len == 0) {
r->len = r->buflen;
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len);
return 0;
scsi_req_data(&r->req, r->len);
return;
}
ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
if (ret == -1) {
scsi_command_complete(r, -EINVAL);
return 1;
if (ret < 0) {
scsi_command_complete(r, ret);
}
return 0;
}
/* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
static uint8_t *scsi_get_buf(SCSIRequest *req)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIGenericReq *r;
r = scsi_find_request(s, tag);
if (!r) {
BADF("Bad buffer tag 0x%x\n", tag);
return NULL;
}
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
return r->buf;
}
@ -311,42 +331,23 @@ static void scsi_req_fixup(SCSIRequest *req)
(eg. disk reads), negative for transfers to the device (eg. disk writes),
and zero if the command does not transfer any data. */
static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
uint8_t *cmd, int lun)
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIGenericReq *r;
SCSIBus *bus;
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
int ret;
if (cmd[0] != REQUEST_SENSE &&
(lun != s->lun || (cmd[1] >> 5) != s->lun)) {
DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
s->sensebuf[0] = 0x70;
s->sensebuf[1] = 0x00;
s->sensebuf[2] = ILLEGAL_REQUEST;
s->sensebuf[3] = 0x00;
s->sensebuf[4] = 0x00;
s->sensebuf[5] = 0x00;
s->sensebuf[6] = 0x00;
s->senselen = 7;
s->driver_status = SG_ERR_DRIVER_SENSE;
bus = scsi_bus_from_device(d);
bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION);
if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) {
DPRINTF("Unimplemented LUN %d\n", req->lun);
scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
r->req.status = CHECK_CONDITION;
scsi_req_complete(&r->req);
return 0;
}
r = scsi_find_request(s, tag);
if (r) {
BADF("Tag 0x%x already in use %p\n", tag, r);
scsi_cancel_io(d, tag);
}
r = scsi_new_request(d, tag, lun);
if (-1 == scsi_req_parse(&r->req, cmd)) {
BADF("Unsupported command length, command %x\n", cmd[0]);
scsi_remove_request(r);
scsi_command_complete(r, -EINVAL);
return 0;
}
scsi_req_fixup(&r->req);
@ -370,8 +371,8 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
r->buflen = 0;
r->buf = NULL;
ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
if (ret == -1) {
scsi_command_complete(r, -EINVAL);
if (ret < 0) {
scsi_command_complete(r, ret);
return 0;
}
return 0;
@ -389,9 +390,9 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
r->len = 0;
return -r->req.cmd.xfer;
}
} else {
return r->req.cmd.xfer;
}
}
static int get_blocksize(BlockDriverState *bdrv)
@ -455,31 +456,18 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
}
static void scsi_generic_purge_requests(SCSIGenericState *s)
{
SCSIGenericReq *r;
while (!QTAILQ_EMPTY(&s->qdev.requests)) {
r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests));
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
}
scsi_remove_request(r);
}
}
static void scsi_generic_reset(DeviceState *dev)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
scsi_generic_purge_requests(s);
scsi_device_purge_requests(&s->qdev);
}
static void scsi_destroy(SCSIDevice *d)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
scsi_generic_purge_requests(s);
scsi_device_purge_requests(&s->qdev);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
@ -556,11 +544,14 @@ static SCSIDeviceInfo scsi_generic_info = {
.qdev.reset = scsi_generic_reset,
.init = scsi_generic_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
.get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
DEFINE_PROP_END_OF_LIST(),

View File

@ -9,17 +9,11 @@
#define SCSI_CMD_BUF_SIZE 16
/* scsi-disk.c */
enum scsi_reason {
SCSI_REASON_DONE, /* Command complete. */
SCSI_REASON_DATA /* Transfer complete, more data required. */
};
typedef struct SCSIBus SCSIBus;
typedef struct SCSIBusOps SCSIBusOps;
typedef struct SCSIDevice SCSIDevice;
typedef struct SCSIDeviceInfo SCSIDeviceInfo;
typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag,
uint32_t arg);
typedef struct SCSIRequest SCSIRequest;
enum SCSIXferMode {
SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */
@ -27,9 +21,16 @@ enum SCSIXferMode {
SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */
};
typedef struct SCSIRequest {
typedef struct SCSISense {
uint8_t key;
uint8_t asc;
uint8_t ascq;
} SCSISense;
struct SCSIRequest {
SCSIBus *bus;
SCSIDevice *dev;
uint32_t refcount;
uint32_t tag;
uint32_t lun;
uint32_t status;
@ -43,7 +44,7 @@ typedef struct SCSIRequest {
BlockDriverAIOCB *aiocb;
bool enqueued;
QTAILQ_ENTRY(SCSIRequest) next;
} SCSIRequest;
};
struct SCSIDevice
{
@ -66,28 +67,34 @@ struct SCSIDeviceInfo {
DeviceInfo qdev;
scsi_qdev_initfn init;
void (*destroy)(SCSIDevice *s);
int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf,
int lun);
void (*read_data)(SCSIDevice *s, uint32_t tag);
int (*write_data)(SCSIDevice *s, uint32_t tag);
void (*cancel_io)(SCSIDevice *s, uint32_t tag);
uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun);
void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
void (*read_data)(SCSIRequest *req);
void (*write_data)(SCSIRequest *req);
void (*cancel_io)(SCSIRequest *req);
uint8_t *(*get_buf)(SCSIRequest *req);
int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len);
};
struct SCSIBusOps {
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
void (*complete)(SCSIRequest *req, uint32_t arg);
void (*cancel)(SCSIRequest *req);
};
typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
int unit);
struct SCSIBus {
BusState qbus;
int busnr;
int tcq, ndev;
scsi_completionfn complete;
const SCSIBusOps *ops;
SCSIDevice *devs[MAX_SCSI_DEVS];
};
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
scsi_completionfn complete);
const SCSIBusOps *ops);
void scsi_qdev_register(SCSIDeviceInfo *info);
static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
@ -99,12 +106,54 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
int unit, bool removable);
int scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
/*
* Predefined sense codes
*/
/* No sense data available */
extern const struct SCSISense sense_code_NO_SENSE;
/* LUN not ready, Manual intervention required */
extern const struct SCSISense sense_code_LUN_NOT_READY;
/* LUN not ready, Medium not present */
extern const struct SCSISense sense_code_NO_MEDIUM;
/* Hardware error, internal target failure */
extern const struct SCSISense sense_code_TARGET_FAILURE;
/* Illegal request, invalid command operation code */
extern const struct SCSISense sense_code_INVALID_OPCODE;
/* Illegal request, LBA out of range */
extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE;
/* Illegal request, Invalid field in CDB */
extern const struct SCSISense sense_code_INVALID_FIELD;
/* Illegal request, LUN not supported */
extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED;
/* Command aborted, I/O process terminated */
extern const struct SCSISense sense_code_IO_ERROR;
/* Command aborted, I_T Nexus loss occurred */
extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
/* Command aborted, Logical Unit failure */
extern const struct SCSISense sense_code_LUN_FAILURE;
#define SENSE_CODE(x) sense_code_ ## x
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag);
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun);
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf);
void scsi_req_free(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
void scsi_req_unref(SCSIRequest *req);
int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
void scsi_req_print(SCSIRequest *req);
void scsi_req_continue(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
uint8_t *scsi_req_get_buf(SCSIRequest *req);
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
void scsi_req_abort(SCSIRequest *req, int status);
void scsi_req_cancel(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev);
#endif

View File

@ -74,7 +74,7 @@ typedef struct vscsi_req {
union viosrp_iu iu;
/* SCSI request tracking */
SCSIDevice *sdev;
SCSIRequest *sreq;
uint32_t qtag; /* qemu tag != srp tag */
int lun;
int active;
@ -123,11 +123,16 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
{
if (req->sreq != NULL) {
scsi_req_unref(req->sreq);
}
req->sreq = NULL;
req->active = 0;
}
static vscsi_req *vscsi_find_req(VSCSIState *s, uint32_t tag)
static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req)
{
uint32_t tag = req->tag;
if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
return NULL;
}
@ -442,10 +447,18 @@ static int vscsi_preprocess_desc(vscsi_req *req)
static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
{
SCSIDevice *sdev = req->sdev;
uint8_t *cdb = req->iu.srp.cmd.cdb;
int n;
n = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense));
if (n) {
req->senselen = n;
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
return;
}
dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
cdb[0] = 3;
cdb[1] = 0;
cdb[2] = 0;
@ -453,49 +466,38 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
cdb[4] = 96;
cdb[5] = 0;
req->sensing = 1;
n = sdev->info->send_command(sdev, req->qtag, cdb, req->lun);
n = scsi_req_enqueue(req->sreq, cdb);
dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag);
if (n < 0) {
fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n");
sdev->info->cancel_io(sdev, req->qtag);
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
scsi_req_abort(req->sreq, CHECK_CONDITION);
return;
} else if (n == 0) {
return;
}
sdev->info->read_data(sdev, req->qtag);
scsi_req_continue(req->sreq);
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
uint32_t arg)
static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, tag);
SCSIDevice *sdev;
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq);
uint8_t *buf;
int32_t res_in = 0, res_out = 0;
int len, rc = 0;
int rc = 0;
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
reason, tag, arg, req);
dprintf("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n",
sreq->tag, len, req);
if (req == NULL) {
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", tag);
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
return;
}
sdev = req->sdev;
if (req->sensing) {
if (reason == SCSI_REASON_DONE) {
dprintf("VSCSI: Sense done !\n");
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
} else {
uint8_t *buf = sdev->info->get_buf(sdev, tag);
uint8_t *buf = scsi_req_get_buf(sreq);
len = MIN(arg, SCSI_SENSE_BUF_SIZE);
len = MIN(len, SCSI_SENSE_BUF_SIZE);
dprintf("VSCSI: Sense data, %d bytes:\n", len);
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[0], buf[1], buf[2], buf[3],
@ -505,14 +507,51 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
buf[12], buf[13], buf[14], buf[15]);
memcpy(req->sense, buf, len);
req->senselen = len;
sdev->info->read_data(sdev, req->qtag);
}
scsi_req_continue(req->sreq);
return;
}
if (reason == SCSI_REASON_DONE) {
dprintf("VSCSI: Command complete err=%d\n", arg);
if (arg == 0) {
if (len) {
buf = scsi_req_get_buf(sreq);
rc = vscsi_srp_transfer_data(s, req, req->writing, buf, len);
}
if (rc < 0) {
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
scsi_req_abort(req->sreq, CHECK_CONDITION);
return;
}
/* Start next chunk */
req->data_len -= rc;
scsi_req_continue(sreq);
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq);
int32_t res_in = 0, res_out = 0;
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n",
reason, sreq->tag, status, req);
if (req == NULL) {
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
return;
}
if (!req->sensing && status == CHECK_CONDITION) {
vscsi_send_request_sense(s, req);
return;
}
if (req->sensing) {
dprintf("VSCSI: Sense done !\n");
status = CHECK_CONDITION;
} else {
dprintf("VSCSI: Command complete err=%d\n", status);
if (status == 0) {
/* We handle overflows, not underflows for normal commands,
* but hopefully nobody cares
*/
@ -521,41 +560,18 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
} else {
res_in = req->data_len;
}
}
}
vscsi_send_rsp(s, req, 0, res_in, res_out);
} else if (arg == CHECK_CONDITION) {
dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
vscsi_send_request_sense(s, req);
return;
} else {
vscsi_send_rsp(s, req, arg, 0, 0);
}
vscsi_put_req(s, req);
return;
}
}
static void vscsi_request_cancelled(SCSIRequest *sreq)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq);
/* "arg" is how much we have read for reads and how much we want
* to write for writes (ie, how much is to be DMA'd)
*/
if (arg) {
buf = sdev->info->get_buf(sdev, tag);
rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg);
}
if (rc < 0) {
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
sdev->info->cancel_io(sdev, req->qtag);
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
return;
}
/* Start next chunk */
req->data_len -= rc;
if (req->writing) {
sdev->info->write_data(sdev, req->qtag);
} else {
sdev->info->read_data(sdev, req->qtag);
}
}
static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
@ -642,9 +658,9 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
} return 1;
}
req->sdev = sdev;
req->lun = lun;
n = sdev->info->send_command(sdev, req->qtag, srp->cmd.cdb, lun);
req->sreq = scsi_req_new(sdev, req->qtag, lun);
n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
req->qtag, srp->cmd.cdb[0], id, lun, n);
@ -657,15 +673,14 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
/* Preprocess RDMA descriptors */
vscsi_preprocess_desc(req);
}
/* Get transfer direction and initiate transfer */
if (n > 0) {
req->data_len = n;
sdev->info->read_data(sdev, req->qtag);
} else if (n < 0) {
req->data_len = -n;
sdev->info->write_data(sdev, req->qtag);
}
scsi_req_continue(req->sreq);
}
/* Don't touch req here, it may have been recycled already */
@ -907,6 +922,12 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
return 0;
}
static const struct SCSIBusOps vscsi_scsi_ops = {
.transfer_data = vscsi_transfer_data,
.complete = vscsi_command_complete,
.cancel = vscsi_request_cancelled
};
static int spapr_vscsi_init(VIOsPAPRDevice *dev)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev);
@ -923,7 +944,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev)
dev->crq.SendFunc = vscsi_do_crq;
scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT,
vscsi_command_complete);
&vscsi_scsi_ops);
if (!dev->qdev.hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus);
}

View File

@ -48,6 +48,7 @@ typedef struct {
uint32_t data_len;
uint32_t residue;
uint32_t tag;
SCSIRequest *req;
SCSIBus bus;
BlockConf conf;
SCSIDevice *scsi_dev;
@ -191,11 +192,7 @@ static void usb_msd_copy_data(MSDState *s)
s->scsi_buf += len;
s->data_len -= len;
if (s->scsi_len == 0 || s->data_len == 0) {
if (s->mode == USB_MSDM_DATAIN) {
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
} else if (s->mode == USB_MSDM_DATAOUT) {
s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
}
scsi_req_continue(s->req);
}
}
@ -213,44 +210,18 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
memcpy(p->data, &csw, len);
}
static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
uint32_t arg)
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
if (tag != s->tag) {
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag);
if (req->tag != s->tag) {
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
if (reason == SCSI_REASON_DONE) {
DPRINTF("Command complete %d\n", arg);
s->residue = s->data_len;
s->result = arg != 0;
if (s->packet) {
if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
/* A deferred packet with no write data remaining must be
the status read packet. */
usb_msd_send_status(s, p);
s->mode = USB_MSDM_CBW;
} else {
if (s->data_len) {
s->data_len -= s->usb_len;
if (s->mode == USB_MSDM_DATAIN)
memset(s->usb_buf, 0, s->usb_len);
s->usb_len = 0;
}
if (s->data_len == 0)
s->mode = USB_MSDM_CSW;
}
s->packet = NULL;
usb_packet_complete(&s->dev, p);
} else if (s->data_len == 0) {
s->mode = USB_MSDM_CSW;
}
return;
}
s->scsi_len = arg;
s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = len;
s->scsi_buf = scsi_req_get_buf(req);
if (p) {
usb_msd_copy_data(s);
if (s->packet && s->usb_len == 0) {
@ -264,6 +235,56 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
}
}
static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
if (req->tag != s->tag) {
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
DPRINTF("Command complete %d\n", status);
s->residue = s->data_len;
s->result = status != 0;
if (s->packet) {
if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
/* A deferred packet with no write data remaining must be
the status read packet. */
usb_msd_send_status(s, p);
s->mode = USB_MSDM_CBW;
} else {
if (s->data_len) {
s->data_len -= s->usb_len;
if (s->mode == USB_MSDM_DATAIN) {
memset(s->usb_buf, 0, s->usb_len);
}
s->usb_len = 0;
}
if (s->data_len == 0) {
s->mode = USB_MSDM_CSW;
}
}
s->packet = NULL;
usb_packet_complete(&s->dev, p);
} else if (s->data_len == 0) {
s->mode = USB_MSDM_CSW;
}
scsi_req_unref(req);
s->req = NULL;
}
static void usb_msd_request_cancelled(SCSIRequest *req)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
if (req == s->req) {
scsi_req_unref(s->req);
s->req = NULL;
s->packet = NULL;
s->scsi_len = 0;
}
}
static void usb_msd_handle_reset(USBDevice *dev)
{
MSDState *s = (MSDState *)dev;
@ -318,9 +339,7 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
{
MSDState *s = DO_UPCAST(MSDState, dev, dev);
s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
s->packet = NULL;
s->scsi_len = 0;
scsi_req_cancel(s->req);
}
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
@ -367,15 +386,12 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0;
s->scsi_len = 0;
s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
s->req = scsi_req_new(s->scsi_dev, s->tag, 0);
scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
if (s->residue == 0) {
if (s->mode == USB_MSDM_DATAIN) {
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
} else if (s->mode == USB_MSDM_DATAOUT) {
s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
}
if (s->mode != USB_MSDM_CSW && s->residue == 0) {
scsi_req_continue(s->req);
}
ret = len;
break;
@ -486,6 +502,12 @@ static void usb_msd_password_cb(void *opaque, int err)
qdev_unplug(&s->dev.qdev);
}
static const struct SCSIBusOps usb_msd_scsi_ops = {
.transfer_data = usb_msd_transfer_data,
.complete = usb_msd_command_complete,
.cancel = usb_msd_request_cancelled
};
static int usb_msd_initfn(USBDevice *dev)
{
MSDState *s = DO_UPCAST(MSDState, dev, dev);
@ -515,7 +537,7 @@ static int usb_msd_initfn(USBDevice *dev)
}
usb_desc_init(dev);
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops);
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
if (!s->scsi_dev) {
return -1;

View File

@ -205,6 +205,14 @@ disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d
disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
# hw/scsi-bus.c
disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer, uint64_t lba) "target %d lun %d tag %d command %d dir %d length %d lba %"PRIu64""
disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
# vl.c
disable vm_state_notify(int running, int reason) "running %d reason %d"