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

119
hw/esp.c
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,53 +382,56 @@ 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);
/* 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);
}
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 (to_device || s->dma_left != 0 || s->ti_size == 0) {
return;
}
} else {
/* Partially filled a scsi buffer. Complete immediately. */
esp_dma_done(s);
}
/* Partially filled a scsi buffer. Complete immediately. */
esp_dma_done(s);
}
static void esp_command_complete(SCSIRequest *req, uint32_t status)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
DPRINTF("SCSI Command complete\n");
if (s->ti_size != 0) {
DPRINTF("SCSI command completed unexpectedly\n");
}
s->ti_size = 0;
s->dma_left = 0;
s->async_len = 0;
if (status) {
DPRINTF("Command failed\n");
}
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;
}
}
static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
uint32_t arg)
static void esp_transfer_data(SCSIRequest *req, uint32_t len)
{
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)
DPRINTF("SCSI command completed unexpectedly\n");
s->ti_size = 0;
s->dma_left = 0;
s->async_len = 0;
if (arg)
DPRINTF("Command failed\n");
s->sense = arg;
s->rregs[ESP_RSTAT] = STAT_ST;
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
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) {
/* If this was the last part of a DMA transfer then the
completion interrupt is deferred to here. */
esp_dma_done(s);
s->current_dev = NULL;
} else {
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);
if (s->dma_left) {
esp_do_dma(s);
} else if (s->dma_counter != 0 && s->ti_size <= 0) {
/* If this was the last part of a DMA transfer then the
completion interrupt is deferred to here. */
esp_dma_done(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);
if (s->current->dma_buf == NULL) {
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,82 +645,123 @@ 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) {
if (p->pending) {
BADF("Multiple IO pending for tag %d\n", tag);
}
p->pending = arg;
/* 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
is also required that there are no pending interrupts waiting
for service from the device driver. */
if (s->waiting == 1 ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
!(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
/* Reselect device. */
lsi_reselect(s, p);
return 0;
} else {
DPRINTF("Queueing IO tag=0x%x\n", tag);
p->pending = arg;
return 1;
}
return p;
}
}
BADF("IO with unknown tag %d\n", tag);
return 1;
return NULL;
}
/* 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)
static void lsi_request_cancelled(SCSIRequest *req)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
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 = 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
is also required that there are no pending interrupts waiting
for service from the device driver. */
if (s->waiting == 1 ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
!(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
/* Reselect device. */
lsi_reselect(s, p);
return 0;
} else {
DPRINTF("Queueing IO tag=0x%x\n", tag);
p->pending = len;
return 1;
}
}
/* 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, 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;
s->command_complete = 2;
if (s->waiting && s->dbc != 0) {
/* Raise phase mismatch for short transfers. */
lsi_bad_phase(s, out, PHASE_ST);
} else {
lsi_set_phase(s, PHASE_ST);
}
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. */
lsi_bad_phase(s, out, PHASE_ST);
} else {
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);
return;
}
lsi_resume_script(s);
}
if (s->waiting == 1 || !s->current || tag != s->current->tag ||
/* 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, tag, arg))
if (lsi_queue_tag(s, req->tag, len)) {
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 == 1 || s->dbc == 0) {
lsi_resume_script(s);
} else {
lsi_do_dma(s, out);
if (s->waiting) {
if (s->waiting == 1 || s->dbc == 0) {
lsi_resume_script(s);
} else {
lsi_do_dma(s, out);
}
}
}
@ -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);
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);
n = scsi_req_enqueue(s->current->req, buf);
if (n) {
if (n > 0) {
lsi_set_phase(s, PHASE_DI);
} else if (n < 0) {
lsi_set_phase(s, PHASE_DO);
}
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)
bdrv_aio_cancel(r->req.aiocb);
r->req.aiocb = NULL;
scsi_remove_request(r);
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;
}
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,
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;
}
scsi_command_complete(r, CHECK_CONDITION,
HARDWARE_ERROR);
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)
bdrv_aio_cancel(r->req.aiocb);
r->req.aiocb = NULL;
scsi_remove_request(r);
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;
}
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;
}
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,66 +466,92 @@ 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);
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],
buf[4], buf[5], buf[6], buf[7]);
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[8], buf[9], buf[10], buf[11],
buf[12], buf[13], buf[14], buf[15]);
memcpy(req->sense, buf, len);
req->senselen = len;
sdev->info->read_data(sdev, req->qtag);
}
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],
buf[4], buf[5], buf[6], buf[7]);
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[8], buf[9], buf[10], buf[11],
buf[12], buf[13], buf[14], buf[15]);
memcpy(req->sense, buf, len);
req->senselen = len;
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;
}
vscsi_send_rsp(s, req, 0, res_in, res_out);
vscsi_put_req(s, req);
}
/* "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;
}
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);
/* 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);
}
vscsi_put_req(s, req);
}
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);
/* Get transfer direction and initiate transfer */
if (n > 0) {
req->data_len = n;
} else if (n < 0) {
req->data_len = -n;
}
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"