@@ -21,6 +21,7 @@ static Property scsi_props[] = {
DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
DEFINE_PROP_UINT64("lun", SCSIDevice, lun, -1),
+ DEFINE_PROP_UINT8("protocol", SCSIDevice, protocol, SCSI_PROTOCOL_SAS),
DEFINE_PROP_END_OF_LIST(),
};
@@ -189,6 +190,15 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
}
}
+ if (dev->protocol != SCSI_PROTOCOL_FCP &&
+ dev->protocol != SCSI_PROTOCOL_SPI &&
+ dev->protocol != SCSI_PROTOCOL_SRP &&
+ dev->protocol != SCSI_PROTOCOL_ISCSI &&
+ dev->protocol != SCSI_PROTOCOL_SAS &&
+ dev->protocol != SCSI_PROTOCOL_UAS) {
+ error_setg(errp, "invalid scsi protocol id: %d", dev->protocol);
+ return;
+ }
if (dev->id == -1) {
int id = -1;
if (dev->lun == -1) {
@@ -668,7 +668,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
}
if (s->qdev.port_wwn) {
- outbuf[buflen++] = 0x61; // SAS / Binary
+ outbuf[buflen++] = s->qdev.protocol << 8 | 0x1; // Binary
outbuf[buflen++] = 0x93; // PIV / Target port / NAA
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = 8;
@@ -677,7 +677,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
}
if (s->port_index) {
- outbuf[buflen++] = 0x61; // SAS / Binary
+ outbuf[buflen++] = s->qdev.protocol << 8 | 0x1; // Binary
outbuf[buflen++] = 0x94; // PIV / Target port / relative target port
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = 4;
@@ -2355,6 +2355,18 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
return;
}
+ if (dev->protocol == SCSI_PROTOCOL_FCP) {
+ if (!s->qdev.port_wwn) {
+ error_setg(errp,
+ "Missing port_wwn for FCP protocol");
+ return;
+ }
+ if (!s->qdev.node_wwn && (s->qdev.port_wwn >> 60) != 0x02) {
+ error_setg(errp,
+ "port_wwn is not a IEEE Extended identifier");
+ return;
+ }
+ }
if (dev->type == TYPE_DISK) {
blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err);
if (err) {
@@ -19,6 +19,7 @@
#include "hw/virtio/virtio-scsi.h"
#include "qemu/error-report.h"
#include "qemu/iov.h"
+#include "qemu/cutils.h"
#include "sysemu/block-backend.h"
#include "hw/scsi/scsi.h"
#include "scsi/constants.h"
@@ -386,6 +387,7 @@ fail:
static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
VirtIODevice *vdev = (VirtIODevice *)s;
+ VirtIOSCSICommon *c = VIRTIO_SCSI_COMMON(vdev);
uint32_t type;
int r = 0;
@@ -415,6 +417,55 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
req->resp.an.event_actual = 0;
req->resp.an.response = VIRTIO_SCSI_S_OK;
}
+ } else if (type == VIRTIO_SCSI_T_RESCAN) {
+ if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSIRescanReq),
+ sizeof(VirtIOSCSIRescanResp)) < 0) {
+ virtio_scsi_bad_req(req);
+ return;
+ } else {
+ BusChild *kid;
+ SCSIDevice *dev = NULL;
+
+ if (req->req.rescan.next_id != -1) {
+ QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+ DeviceState *qdev = kid->child;
+ SCSIDevice *d = SCSI_DEVICE(qdev);
+
+ if (d->id >= req->req.rescan.next_id) {
+ dev = d;
+ break;
+ }
+ }
+ }
+ if (dev) {
+ req->resp.rescan.id = dev->id;
+ req->resp.rescan.transport = dev->protocol;
+ if (dev->protocol == SCSI_PROTOCOL_FCP &&
+ dev->port_wwn && !dev->node_wwn) {
+ dev->node_wwn = ((uint64_t)1 << 56) |
+ (dev->port_wwn & ~((uint64_t)0xff << 56));
+ }
+ stq_be_p(req->resp.rescan.node_wwn, dev->node_wwn);
+ stq_be_p(req->resp.rescan.port_wwn, dev->port_wwn);
+ } else {
+ req->resp.rescan.id = -1;
+ if (c->conf.wwnn && c->conf.wwpn) {
+ req->resp.rescan.transport = SCSI_PROTOCOL_FCP;
+ } else {
+ req->resp.rescan.transport = SCSI_PROTOCOL_SAS;
+ }
+ if (c->conf.wwnn) {
+ uint64_t wwnn;
+ qemu_strtou64(c->conf.wwnn, NULL, 16, &wwnn);
+ stq_be_p(req->resp.rescan.node_wwn, wwnn);
+ }
+ if (c->conf.wwpn) {
+ uint64_t wwpn;
+ qemu_strtou64(c->conf.wwpn, NULL, 16, &wwpn);
+ stq_be_p(req->resp.rescan.port_wwn, wwpn);
+ }
+ }
+ }
}
if (r == 0) {
virtio_scsi_complete_req(req);
@@ -927,8 +978,12 @@ static Property virtio_scsi_properties[] = {
VIRTIO_SCSI_F_HOTPLUG, true),
DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features,
VIRTIO_SCSI_F_CHANGE, true),
+ DEFINE_PROP_BIT("rescan", VirtIOSCSI, host_features,
+ VIRTIO_SCSI_F_RESCAN, true),
DEFINE_PROP_LINK("iothread", VirtIOSCSI, parent_obj.conf.iothread,
TYPE_IOTHREAD, IOThread *),
+ DEFINE_PROP_STRING("wwpn", VirtIOSCSI, parent_obj.conf.wwpn),
+ DEFINE_PROP_STRING("wwnn", VirtIOSCSI, parent_obj.conf.wwnn),
DEFINE_PROP_END_OF_LIST(),
};
@@ -71,19 +71,21 @@ struct SCSIDevice
DeviceState qdev;
VMChangeStateEntry *vmsentry;
QEMUBH *bh;
- uint32_t id;
BlockConf conf;
SCSISense unit_attention;
bool sense_is_ua;
uint8_t sense[SCSI_SENSE_BUF_SIZE];
uint32_t sense_len;
QTAILQ_HEAD(, SCSIRequest) requests;
+ uint32_t id;
uint32_t channel;
uint64_t lun;
int blocksize;
- int type;
+ uint8_t type;
+ uint8_t protocol;
uint64_t max_lba;
uint64_t wwn;
+ uint64_t node_wwn;
uint64_t port_wwn;
};
@@ -42,6 +42,8 @@ typedef struct virtio_scsi_ctrl_tmf_req VirtIOSCSICtrlTMFReq;
typedef struct virtio_scsi_ctrl_tmf_resp VirtIOSCSICtrlTMFResp;
typedef struct virtio_scsi_ctrl_an_req VirtIOSCSICtrlANReq;
typedef struct virtio_scsi_ctrl_an_resp VirtIOSCSICtrlANResp;
+typedef struct virtio_scsi_rescan_req VirtIOSCSIRescanReq;
+typedef struct virtio_scsi_rescan_resp VirtIOSCSIRescanResp;
typedef struct virtio_scsi_event VirtIOSCSIEvent;
typedef struct virtio_scsi_config VirtIOSCSIConfig;
@@ -52,8 +54,9 @@ struct VirtIOSCSIConf {
uint32_t cmd_per_lun;
#ifdef CONFIG_VHOST_SCSI
char *vhostfd;
- char *wwpn;
#endif
+ char *wwpn;
+ char *wwnn;
CharBackend chardev;
uint32_t boot_tpgt;
IOThread *iothread;
@@ -116,12 +119,14 @@ typedef struct VirtIOSCSIReq {
VirtIOSCSICmdResp cmd;
VirtIOSCSICtrlTMFResp tmf;
VirtIOSCSICtrlANResp an;
+ VirtIOSCSIRescanResp rescan;
VirtIOSCSIEvent event;
} resp;
union {
VirtIOSCSICmdReq cmd;
VirtIOSCSICtrlTMFReq tmf;
VirtIOSCSICtrlANReq an;
+ VirtIOSCSIRescanReq rescan;
} req;
} VirtIOSCSIReq;
@@ -223,6 +223,17 @@
#define TYPE_INACTIVE 0x20
#define TYPE_NO_LUN 0x7f
+/*
+ * Protocol identifiers
+ */
+
+#define SCSI_PROTOCOL_FCP 0x00
+#define SCSI_PROTOCOL_SPI 0x01
+#define SCSI_PROTOCOL_SRP 0x04
+#define SCSI_PROTOCOL_ISCSI 0x05
+#define SCSI_PROTOCOL_SAS 0x06
+#define SCSI_PROTOCOL_UAS 0x09
+#define SCSI_PROTOCOL_UNSPEC 0x0f
/* Mode page codes for mode sense/set */
#define MODE_PAGE_R_W_ERROR 0x01
#define MODE_PAGE_HD_GEOMETRY 0x04
@@ -96,6 +96,19 @@ struct virtio_scsi_ctrl_an_resp {
uint8_t response;
} QEMU_PACKED;
+/* Target rescan */
+struct virtio_scsi_rescan_req {
+ __virtio32 type;
+ __virtio32 next_id;
+} QEMU_PACKED;
+
+struct virtio_scsi_rescan_resp {
+ __virtio32 id;
+ __virtio32 transport;
+ uint8_t node_wwn[8];
+ uint8_t port_wwn[8];
+} QEMU_PACKED;
+
struct virtio_scsi_event {
__virtio32 event;
uint8_t lun[8];
@@ -120,6 +133,7 @@ struct virtio_scsi_config {
#define VIRTIO_SCSI_F_HOTPLUG 1
#define VIRTIO_SCSI_F_CHANGE 2
#define VIRTIO_SCSI_F_T10_PI 3
+#define VIRTIO_SCSI_F_RESCAN 4
/* Response codes */
#define VIRTIO_SCSI_S_OK 0
@@ -140,6 +154,7 @@ struct virtio_scsi_config {
#define VIRTIO_SCSI_T_TMF 0
#define VIRTIO_SCSI_T_AN_QUERY 1
#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2
+#define VIRTIO_SCSI_T_RESCAN 3
/* Valid TMF subtypes. */
#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
Implement a new virtio-scsi command 'rescan' to return a list of attached targets. The guest is required to set the 'next_id' field to the next expected target id; the host will return either that or the next higher target id (if present), or -1 if no additional targets are found. Signed-off-by: Hannes Reinecke <hare@suse.com> --- hw/scsi/scsi-bus.c | 10 +++++ hw/scsi/scsi-disk.c | 16 +++++++- hw/scsi/virtio-scsi.c | 55 ++++++++++++++++++++++++++++ include/hw/scsi/scsi.h | 6 ++- include/hw/virtio/virtio-scsi.h | 7 +++- include/scsi/constants.h | 11 ++++++ include/standard-headers/linux/virtio_scsi.h | 15 ++++++++ 7 files changed, 115 insertions(+), 5 deletions(-)