mirror of https://github.com/xemu-project/xemu.git
Merge remote-tracking branch 'bonzini/scsi-next' into staging
* bonzini/scsi-next: (32 commits) virtio-scsi: enable MSI-X support virtio-scsi: add ioeventfd support virtio-scsi: report parameter change events virtio-scsi: do not report dropped events after reset virtio-scsi: Report missed events virtio-scsi: Implement hotplug support for virtio-scsi scsi: report parameter changes to HBA drivers scsi-disk: report resized disk via sense codes scsi: establish precedence levels for unit attention scsi: introduce hotplug() and hot_unplug() interfaces for SCSI bus scsi: add tracepoint for scsi_req_cancel scsi-disk: removable hard disks support load/eject scsi-disk: Fail medium writes with proper sense for readonly LUNs scsi-disk: improve the lba-out-of-range tests for read/write/verify scsi-disk: rd/wr/vr-protect !=0 is an error scsi-disk: support toggling the write cache scsi-disk: parse MODE SELECT commands and parameters scsi-disk: fix changeable values for MODE_PAGE_R_W_ERROR scsi-disk: adjust offsets in MODE SENSE by 2 scsi-disk: support emulated TO_DEV requests ...
This commit is contained in:
commit
d4a06f466a
7
cutils.c
7
cutils.c
|
@ -28,6 +28,13 @@
|
|||
#include "qemu_socket.h"
|
||||
#include "iov.h"
|
||||
|
||||
void strpadcpy(char *buf, int buf_size, const char *str, char pad)
|
||||
{
|
||||
int len = qemu_strnlen(str, buf_size);
|
||||
memcpy(buf, str, len);
|
||||
memset(buf + len, pad, buf_size - len);
|
||||
}
|
||||
|
||||
void pstrcpy(char *buf, int buf_size, const char *str)
|
||||
{
|
||||
int c;
|
||||
|
|
|
@ -282,8 +282,6 @@ static inline int lsi_irq_on_rsl(LSIState *s)
|
|||
|
||||
static void lsi_soft_reset(LSIState *s)
|
||||
{
|
||||
lsi_request *p;
|
||||
|
||||
DPRINTF("Reset\n");
|
||||
s->carry = 0;
|
||||
|
||||
|
@ -350,15 +348,8 @@ static void lsi_soft_reset(LSIState *s)
|
|||
s->sbc = 0;
|
||||
s->csbc = 0;
|
||||
s->sbr = 0;
|
||||
while (!QTAILQ_EMPTY(&s->queue)) {
|
||||
p = QTAILQ_FIRST(&s->queue);
|
||||
QTAILQ_REMOVE(&s->queue, p, next);
|
||||
g_free(p);
|
||||
}
|
||||
if (s->current) {
|
||||
g_free(s->current);
|
||||
s->current = NULL;
|
||||
}
|
||||
assert(QTAILQ_EMPTY(&s->queue));
|
||||
assert(!s->current);
|
||||
}
|
||||
|
||||
static int lsi_dma_40bit(LSIState *s)
|
||||
|
@ -650,23 +641,24 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void lsi_request_free(LSIState *s, lsi_request *p)
|
||||
{
|
||||
if (p == s->current) {
|
||||
s->current = NULL;
|
||||
} else {
|
||||
QTAILQ_REMOVE(&s->queue, p, next);
|
||||
}
|
||||
g_free(p);
|
||||
}
|
||||
|
||||
static void lsi_request_cancelled(SCSIRequest *req)
|
||||
{
|
||||
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
|
||||
lsi_request *p = req->hba_private;
|
||||
|
||||
if (s->current && req == s->current->req) {
|
||||
scsi_req_unref(req);
|
||||
g_free(s->current);
|
||||
s->current = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (p) {
|
||||
QTAILQ_REMOVE(&s->queue, p, next);
|
||||
scsi_req_unref(req);
|
||||
g_free(p);
|
||||
}
|
||||
req->hba_private = NULL;
|
||||
lsi_request_free(s, p);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
/* Record that data is available for a queued command. Returns zero if
|
||||
|
@ -714,10 +706,10 @@ static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid
|
|||
lsi_set_phase(s, PHASE_ST);
|
||||
}
|
||||
|
||||
if (s->current && req == s->current->req) {
|
||||
scsi_req_unref(s->current->req);
|
||||
g_free(s->current);
|
||||
s->current = NULL;
|
||||
if (req->hba_private == s->current) {
|
||||
req->hba_private = NULL;
|
||||
lsi_request_free(s, s->current);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
lsi_resume_script(s);
|
||||
}
|
||||
|
@ -728,7 +720,8 @@ 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->hba_private != s->current ||
|
||||
assert(req->hba_private);
|
||||
if (s->waiting == 1 || req->hba_private != s->current ||
|
||||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
|
||||
if (lsi_queue_req(s, req, len)) {
|
||||
return;
|
||||
|
@ -1738,7 +1731,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
|||
lsi_execute_script(s);
|
||||
}
|
||||
if (val & LSI_ISTAT0_SRST) {
|
||||
lsi_soft_reset(s);
|
||||
qdev_reset_all(&s->dev.qdev);
|
||||
}
|
||||
break;
|
||||
case 0x16: /* MBOX0 */
|
||||
|
|
39
hw/megasas.c
39
hw/megasas.c
|
@ -544,7 +544,7 @@ static void megasas_reset_frames(MegasasState *s)
|
|||
static void megasas_abort_command(MegasasCmd *cmd)
|
||||
{
|
||||
if (cmd->req) {
|
||||
scsi_req_abort(cmd->req, ABORTED_COMMAND);
|
||||
scsi_req_cancel(cmd->req);
|
||||
cmd->req = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -1290,35 +1290,16 @@ static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd)
|
|||
|
||||
static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd)
|
||||
{
|
||||
uint8_t *dummy = g_malloc(cmd->iov_size);
|
||||
struct mfi_ctrl_props info;
|
||||
size_t dcmd_size = sizeof(info);
|
||||
|
||||
dma_buf_write(dummy, cmd->iov_size, &cmd->qsg);
|
||||
|
||||
trace_megasas_dcmd_dump_frame(0,
|
||||
dummy[0x00], dummy[0x01], dummy[0x02], dummy[0x03],
|
||||
dummy[0x04], dummy[0x05], dummy[0x06], dummy[0x07]);
|
||||
trace_megasas_dcmd_dump_frame(1,
|
||||
dummy[0x08], dummy[0x09], dummy[0x0a], dummy[0x0b],
|
||||
dummy[0x0c], dummy[0x0d], dummy[0x0e], dummy[0x0f]);
|
||||
trace_megasas_dcmd_dump_frame(2,
|
||||
dummy[0x10], dummy[0x11], dummy[0x12], dummy[0x13],
|
||||
dummy[0x14], dummy[0x15], dummy[0x16], dummy[0x17]);
|
||||
trace_megasas_dcmd_dump_frame(3,
|
||||
dummy[0x18], dummy[0x19], dummy[0x1a], dummy[0x1b],
|
||||
dummy[0x1c], dummy[0x1d], dummy[0x1e], dummy[0x1f]);
|
||||
trace_megasas_dcmd_dump_frame(4,
|
||||
dummy[0x20], dummy[0x21], dummy[0x22], dummy[0x23],
|
||||
dummy[0x24], dummy[0x25], dummy[0x26], dummy[0x27]);
|
||||
trace_megasas_dcmd_dump_frame(5,
|
||||
dummy[0x28], dummy[0x29], dummy[0x2a], dummy[0x2b],
|
||||
dummy[0x2c], dummy[0x2d], dummy[0x2e], dummy[0x2f]);
|
||||
trace_megasas_dcmd_dump_frame(6,
|
||||
dummy[0x30], dummy[0x31], dummy[0x32], dummy[0x33],
|
||||
dummy[0x34], dummy[0x35], dummy[0x36], dummy[0x37]);
|
||||
trace_megasas_dcmd_dump_frame(7,
|
||||
dummy[0x38], dummy[0x39], dummy[0x3a], dummy[0x3b],
|
||||
dummy[0x3c], dummy[0x3d], dummy[0x3e], dummy[0x3f]);
|
||||
g_free(dummy);
|
||||
if (cmd->iov_size < dcmd_size) {
|
||||
trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
|
||||
dcmd_size);
|
||||
return MFI_STAT_INVALID_PARAMETER;
|
||||
}
|
||||
dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg);
|
||||
trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
|
||||
return MFI_STAT_OK;
|
||||
}
|
||||
|
||||
|
|
100
hw/scsi-bus.c
100
hw/scsi-bus.c
|
@ -186,6 +186,10 @@ static int scsi_qdev_init(DeviceState *qdev)
|
|||
dev);
|
||||
}
|
||||
|
||||
if (bus->info->hotplug) {
|
||||
bus->info->hotplug(bus, dev);
|
||||
}
|
||||
|
||||
err:
|
||||
return rc;
|
||||
}
|
||||
|
@ -1068,6 +1072,16 @@ int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void scsi_device_report_change(SCSIDevice *dev, SCSISense sense)
|
||||
{
|
||||
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
|
||||
|
||||
scsi_device_set_ua(dev, sense);
|
||||
if (bus->info->change) {
|
||||
bus->info->change(bus, dev, sense);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Predefined sense codes
|
||||
*/
|
||||
|
@ -1112,6 +1126,16 @@ const struct SCSISense sense_code_INVALID_FIELD = {
|
|||
.key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Illegal request, Invalid field in parameter list */
|
||||
const struct SCSISense sense_code_INVALID_PARAM = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Illegal request, Parameter list length error */
|
||||
const struct SCSISense sense_code_INVALID_PARAM_LEN = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Illegal request, LUN not supported */
|
||||
const struct SCSISense sense_code_LUN_NOT_SUPPORTED = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00
|
||||
|
@ -1147,6 +1171,11 @@ const struct SCSISense sense_code_LUN_FAILURE = {
|
|||
.key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01
|
||||
};
|
||||
|
||||
/* Unit attention, Capacity data has changed */
|
||||
const struct SCSISense sense_code_CAPACITY_CHANGED = {
|
||||
.key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09
|
||||
};
|
||||
|
||||
/* Unit attention, Power on, reset or bus device reset occurred */
|
||||
const struct SCSISense sense_code_RESET = {
|
||||
.key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00
|
||||
|
@ -1172,6 +1201,11 @@ const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = {
|
|||
.key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04
|
||||
};
|
||||
|
||||
/* Data Protection, Write Protected */
|
||||
const struct SCSISense sense_code_WRITE_PROTECTED = {
|
||||
.key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
* scsi_build_sense
|
||||
*
|
||||
|
@ -1481,6 +1515,7 @@ void scsi_req_complete(SCSIRequest *req, int status)
|
|||
|
||||
void scsi_req_cancel(SCSIRequest *req)
|
||||
{
|
||||
trace_scsi_req_cancel(req->dev->id, req->lun, req->tag);
|
||||
if (!req->enqueued) {
|
||||
return;
|
||||
}
|
||||
|
@ -1511,6 +1546,55 @@ void scsi_req_abort(SCSIRequest *req, int status)
|
|||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
static int scsi_ua_precedence(SCSISense sense)
|
||||
{
|
||||
if (sense.key != UNIT_ATTENTION) {
|
||||
return INT_MAX;
|
||||
}
|
||||
if (sense.asc == 0x29 && sense.ascq == 0x04) {
|
||||
/* DEVICE INTERNAL RESET goes with POWER ON OCCURRED */
|
||||
return 1;
|
||||
} else if (sense.asc == 0x3F && sense.ascq == 0x01) {
|
||||
/* MICROCODE HAS BEEN CHANGED goes with SCSI BUS RESET OCCURRED */
|
||||
return 2;
|
||||
} else if (sense.asc == 0x29 && (sense.ascq == 0x05 || sense.ascq == 0x06)) {
|
||||
/* These two go with "all others". */
|
||||
;
|
||||
} else if (sense.asc == 0x29 && sense.ascq <= 0x07) {
|
||||
/* POWER ON, RESET OR BUS DEVICE RESET OCCURRED = 0
|
||||
* POWER ON OCCURRED = 1
|
||||
* SCSI BUS RESET OCCURRED = 2
|
||||
* BUS DEVICE RESET FUNCTION OCCURRED = 3
|
||||
* I_T NEXUS LOSS OCCURRED = 7
|
||||
*/
|
||||
return sense.ascq;
|
||||
} else if (sense.asc == 0x2F && sense.ascq == 0x01) {
|
||||
/* COMMANDS CLEARED BY POWER LOSS NOTIFICATION */
|
||||
return 8;
|
||||
}
|
||||
return (sense.asc << 8) | sense.ascq;
|
||||
}
|
||||
|
||||
void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense)
|
||||
{
|
||||
int prec1, prec2;
|
||||
if (sense.key != UNIT_ATTENTION) {
|
||||
return;
|
||||
}
|
||||
trace_scsi_device_set_ua(sdev->id, sdev->lun, sense.key,
|
||||
sense.asc, sense.ascq);
|
||||
|
||||
/*
|
||||
* Override a pre-existing unit attention condition, except for a more
|
||||
* important reset condition.
|
||||
*/
|
||||
prec1 = scsi_ua_precedence(sdev->unit_attention);
|
||||
prec2 = scsi_ua_precedence(sense);
|
||||
if (prec2 < prec1) {
|
||||
sdev->unit_attention = sense;
|
||||
}
|
||||
}
|
||||
|
||||
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
|
||||
{
|
||||
SCSIRequest *req;
|
||||
|
@ -1519,7 +1603,8 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
|
|||
req = QTAILQ_FIRST(&sdev->requests);
|
||||
scsi_req_cancel(req);
|
||||
}
|
||||
sdev->unit_attention = sense;
|
||||
|
||||
scsi_device_set_ua(sdev, sense);
|
||||
}
|
||||
|
||||
static char *scsibus_get_dev_path(DeviceState *dev)
|
||||
|
@ -1634,6 +1719,17 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int scsi_qdev_unplug(DeviceState *qdev)
|
||||
{
|
||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
|
||||
|
||||
if (bus->info->hot_unplug) {
|
||||
bus->info->hot_unplug(bus, dev);
|
||||
}
|
||||
return qdev_simple_unplug_cb(qdev);
|
||||
}
|
||||
|
||||
static const VMStateInfo vmstate_info_scsi_requests = {
|
||||
.name = "scsi-requests",
|
||||
.get = get_scsi_requests,
|
||||
|
@ -1670,7 +1766,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data)
|
|||
DeviceClass *k = DEVICE_CLASS(klass);
|
||||
k->bus_type = TYPE_SCSI_BUS;
|
||||
k->init = scsi_qdev_init;
|
||||
k->unplug = qdev_simple_unplug_cb;
|
||||
k->unplug = scsi_qdev_unplug;
|
||||
k->exit = scsi_qdev_exit;
|
||||
k->props = scsi_props;
|
||||
}
|
||||
|
|
826
hw/scsi-disk.c
826
hw/scsi-disk.c
File diff suppressed because it is too large
Load Diff
13
hw/scsi.h
13
hw/scsi.h
|
@ -131,6 +131,9 @@ struct SCSIBusInfo {
|
|||
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
|
||||
void (*complete)(SCSIRequest *req, uint32_t arg, size_t resid);
|
||||
void (*cancel)(SCSIRequest *req);
|
||||
void (*hotplug)(SCSIBus *bus, SCSIDevice *dev);
|
||||
void (*hot_unplug)(SCSIBus *bus, SCSIDevice *dev);
|
||||
void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense);
|
||||
QEMUSGList *(*get_sg_list)(SCSIRequest *req);
|
||||
|
||||
void (*save_request)(QEMUFile *f, SCSIRequest *req);
|
||||
|
@ -180,6 +183,10 @@ extern const struct SCSISense sense_code_INVALID_OPCODE;
|
|||
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, Invalid field in parameter list */
|
||||
extern const struct SCSISense sense_code_INVALID_PARAM;
|
||||
/* Illegal request, Parameter list length error */
|
||||
extern const struct SCSISense sense_code_INVALID_PARAM_LEN;
|
||||
/* Illegal request, LUN not supported */
|
||||
extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED;
|
||||
/* Illegal request, Saving parameters not supported */
|
||||
|
@ -194,6 +201,8 @@ extern const struct SCSISense sense_code_IO_ERROR;
|
|||
extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
|
||||
/* Command aborted, Logical Unit failure */
|
||||
extern const struct SCSISense sense_code_LUN_FAILURE;
|
||||
/* LUN not ready, Capacity data has changed */
|
||||
extern const struct SCSISense sense_code_CAPACITY_CHANGED;
|
||||
/* LUN not ready, Medium not present */
|
||||
extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM;
|
||||
/* Unit attention, Power on, reset or bus device reset occurred */
|
||||
|
@ -204,6 +213,8 @@ extern const struct SCSISense sense_code_MEDIUM_CHANGED;
|
|||
extern const struct SCSISense sense_code_REPORTED_LUNS_CHANGED;
|
||||
/* Unit attention, Device internal reset */
|
||||
extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET;
|
||||
/* Data Protection, Write Protected */
|
||||
extern const struct SCSISense sense_code_WRITE_PROTECTED;
|
||||
|
||||
#define SENSE_CODE(x) sense_code_ ## x
|
||||
|
||||
|
@ -231,6 +242,8 @@ void scsi_req_abort(SCSIRequest *req, int status);
|
|||
void scsi_req_cancel(SCSIRequest *req);
|
||||
void scsi_req_retry(SCSIRequest *req);
|
||||
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
|
||||
void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense);
|
||||
void scsi_device_report_change(SCSIDevice *dev, SCSISense sense);
|
||||
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
|
||||
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
|
||||
|
||||
|
|
|
@ -1023,7 +1023,9 @@ static int virtio_scsi_init_pci(PCIDevice *pci_dev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
vdev->nvectors = proxy->nvectors;
|
||||
vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED
|
||||
? proxy->scsi.num_queues + 3
|
||||
: proxy->nvectors;
|
||||
virtio_init_pci(proxy, vdev);
|
||||
|
||||
/* make the actual value visible */
|
||||
|
@ -1040,7 +1042,8 @@ static int virtio_scsi_exit_pci(PCIDevice *pci_dev)
|
|||
}
|
||||
|
||||
static Property virtio_scsi_properties[] = {
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED),
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOPCIProxy, host_features, scsi),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
|
115
hw/virtio-scsi.c
115
hw/virtio-scsi.c
|
@ -24,6 +24,11 @@
|
|||
#define VIRTIO_SCSI_MAX_TARGET 255
|
||||
#define VIRTIO_SCSI_MAX_LUN 16383
|
||||
|
||||
/* Feature Bits */
|
||||
#define VIRTIO_SCSI_F_INOUT 0
|
||||
#define VIRTIO_SCSI_F_HOTPLUG 1
|
||||
#define VIRTIO_SCSI_F_CHANGE 2
|
||||
|
||||
/* Response codes */
|
||||
#define VIRTIO_SCSI_S_OK 0
|
||||
#define VIRTIO_SCSI_S_OVERRUN 1
|
||||
|
@ -59,6 +64,12 @@
|
|||
#define VIRTIO_SCSI_T_NO_EVENT 0
|
||||
#define VIRTIO_SCSI_T_TRANSPORT_RESET 1
|
||||
#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2
|
||||
#define VIRTIO_SCSI_T_PARAM_CHANGE 3
|
||||
|
||||
/* Reasons for transport reset event */
|
||||
#define VIRTIO_SCSI_EVT_RESET_HARD 0
|
||||
#define VIRTIO_SCSI_EVT_RESET_RESCAN 1
|
||||
#define VIRTIO_SCSI_EVT_RESET_REMOVED 2
|
||||
|
||||
/* SCSI command request, followed by data-out */
|
||||
typedef struct {
|
||||
|
@ -132,6 +143,7 @@ typedef struct {
|
|||
uint32_t sense_size;
|
||||
uint32_t cdb_size;
|
||||
int resetting;
|
||||
bool events_dropped;
|
||||
VirtQueue *ctrl_vq;
|
||||
VirtQueue *event_vq;
|
||||
VirtQueue *cmd_vqs[0];
|
||||
|
@ -206,11 +218,13 @@ static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg,
|
|||
static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq,
|
||||
VirtIOSCSIReq *req)
|
||||
{
|
||||
assert(req->elem.out_num && req->elem.in_num);
|
||||
assert(req->elem.in_num);
|
||||
req->vq = vq;
|
||||
req->dev = s;
|
||||
req->sreq = NULL;
|
||||
req->req.buf = req->elem.out_sg[0].iov_base;
|
||||
if (req->elem.out_num) {
|
||||
req->req.buf = req->elem.out_sg[0].iov_base;
|
||||
}
|
||||
req->resp.buf = req->elem.in_sg[0].iov_base;
|
||||
|
||||
if (req->elem.out_num > 1) {
|
||||
|
@ -405,10 +419,6 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
|||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
}
|
||||
|
||||
static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
|
||||
size_t resid)
|
||||
{
|
||||
|
@ -545,6 +555,8 @@ static void virtio_scsi_set_config(VirtIODevice *vdev,
|
|||
static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
|
||||
uint32_t requested_features)
|
||||
{
|
||||
requested_features |= (1UL << VIRTIO_SCSI_F_HOTPLUG);
|
||||
requested_features |= (1UL << VIRTIO_SCSI_F_CHANGE);
|
||||
return requested_features;
|
||||
}
|
||||
|
||||
|
@ -554,6 +566,7 @@ static void virtio_scsi_reset(VirtIODevice *vdev)
|
|||
|
||||
s->sense_size = VIRTIO_SCSI_SENSE_SIZE;
|
||||
s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
|
||||
s->events_dropped = false;
|
||||
}
|
||||
|
||||
/* The device does not have anything to save beyond the virtio data.
|
||||
|
@ -577,6 +590,93 @@ static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||
uint32_t event, uint32_t reason)
|
||||
{
|
||||
VirtIOSCSIReq *req = virtio_scsi_pop_req(s, s->event_vq);
|
||||
VirtIOSCSIEvent *evt;
|
||||
int in_size;
|
||||
|
||||
if (!req) {
|
||||
s->events_dropped = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->elem.out_num || req->elem.in_num != 1) {
|
||||
virtio_scsi_bad_req();
|
||||
}
|
||||
|
||||
if (s->events_dropped) {
|
||||
event |= VIRTIO_SCSI_T_EVENTS_MISSED;
|
||||
s->events_dropped = false;
|
||||
}
|
||||
|
||||
in_size = req->elem.in_sg[0].iov_len;
|
||||
if (in_size < sizeof(VirtIOSCSIEvent)) {
|
||||
virtio_scsi_bad_req();
|
||||
}
|
||||
|
||||
evt = req->resp.event;
|
||||
memset(evt, 0, sizeof(VirtIOSCSIEvent));
|
||||
evt->event = event;
|
||||
evt->reason = reason;
|
||||
if (!dev) {
|
||||
assert(event == VIRTIO_SCSI_T_NO_EVENT);
|
||||
} else {
|
||||
evt->lun[0] = 1;
|
||||
evt->lun[1] = dev->id;
|
||||
|
||||
/* Linux wants us to keep the same encoding we use for REPORT LUNS. */
|
||||
if (dev->lun >= 256) {
|
||||
evt->lun[2] = (dev->lun >> 8) | 0x40;
|
||||
}
|
||||
evt->lun[3] = dev->lun & 0xFF;
|
||||
}
|
||||
virtio_scsi_complete_req(req);
|
||||
}
|
||||
|
||||
static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||
|
||||
if (s->events_dropped) {
|
||||
virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
|
||||
{
|
||||
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
|
||||
|
||||
if (((s->vdev.guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) &&
|
||||
(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) &&
|
||||
dev->type != TYPE_ROM) {
|
||||
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE,
|
||||
sense.asc | (sense.ascq << 8));
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev)
|
||||
{
|
||||
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
|
||||
|
||||
if (((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) &&
|
||||
(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
|
||||
VIRTIO_SCSI_EVT_RESET_RESCAN);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev)
|
||||
{
|
||||
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
|
||||
|
||||
if ((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
|
||||
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
|
||||
VIRTIO_SCSI_EVT_RESET_REMOVED);
|
||||
}
|
||||
}
|
||||
|
||||
static struct SCSIBusInfo virtio_scsi_scsi_info = {
|
||||
.tcq = true,
|
||||
.max_channel = VIRTIO_SCSI_MAX_CHANNEL,
|
||||
|
@ -585,6 +685,9 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = {
|
|||
|
||||
.complete = virtio_scsi_command_complete,
|
||||
.cancel = virtio_scsi_request_cancelled,
|
||||
.change = virtio_scsi_change,
|
||||
.hotplug = virtio_scsi_hotplug,
|
||||
.hot_unplug = virtio_scsi_hot_unplug,
|
||||
.get_sg_list = virtio_scsi_get_sg_list,
|
||||
.save_request = virtio_scsi_save_request,
|
||||
.load_request = virtio_scsi_load_request,
|
||||
|
|
|
@ -138,6 +138,7 @@ int qemu_timedate_diff(struct tm *tm);
|
|||
|
||||
/* cutils.c */
|
||||
void pstrcpy(char *buf, int buf_size, const char *str);
|
||||
void strpadcpy(char *buf, int buf_size, const char *str, char pad);
|
||||
char *pstrcat(char *buf, int buf_size, const char *s);
|
||||
int strstart(const char *str, const char *val, const char **ptr);
|
||||
int stristart(const char *str, const char *val, const char **ptr);
|
||||
|
|
|
@ -403,6 +403,7 @@ usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s"
|
|||
|
||||
# hw/scsi-bus.c
|
||||
scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
scsi_req_cancel(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
|
||||
scsi_req_data_canceled(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
|
||||
scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
|
@ -411,6 +412,7 @@ scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "targ
|
|||
scsi_req_parsed_lba(int target, int lun, int tag, int cmd, uint64_t lba) "target %d lun %d tag %d command %d lba %"PRIu64
|
||||
scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
|
||||
scsi_req_build_sense(int target, int lun, int tag, int key, int asc, int ascq) "target %d lun %d tag %d key %#02x asc %#02x ascq %#02x"
|
||||
scsi_device_set_ua(int target, int lun, int key, int asc, int ascq) "target %d lun %d key %#02x asc %#02x ascq %#02x"
|
||||
scsi_report_luns(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
scsi_inquiry(int target, int lun, int tag, int cdb1, int cdb2) "target %d lun %d tag %d page %#02x/%#02x"
|
||||
scsi_test_unit_ready(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
|
@ -595,7 +597,7 @@ megasas_dcmd_ld_get_list(int cmd, int num, int max) "scmd %d: DCMD LD get list:
|
|||
megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d"
|
||||
megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d"
|
||||
megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x"
|
||||
megasas_dcmd_dump_frame(int offset, char f0, char f1, char f2, char f3, char f4, char f5, char f6, char f7) "0x%x: %02x %02x %02x %02x %02x %02x %02x %02x"
|
||||
megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld"
|
||||
megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x"
|
||||
megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 ""
|
||||
megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x"
|
||||
|
|
Loading…
Reference in New Issue