* [Qemu-devel] [RFC PATCH v2 01/13] scsi: cleanup reset and destroy callbacks
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 02/13] scsi: support parsing of SAM logical unit numbers Paolo Bonzini
` (11 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
Push scsi_device_purge_requests up from scsi-disk and scsi-generic to
SCSIDevice.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-bus.c | 12 ++++++++++++
hw/scsi-disk.c | 13 +++++--------
hw/scsi-generic.c | 9 ---------
hw/scsi.h | 4 ++--
4 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index ad6a730..3918662 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -71,6 +71,7 @@ static int scsi_qdev_exit(DeviceState *qdev)
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
assert(bus->devs[dev->id] != NULL);
+ scsi_device_purge_requests(bus->devs[dev->id]);
if (bus->devs[dev->id]->info->destroy) {
bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
}
@@ -78,12 +79,23 @@ static int scsi_qdev_exit(DeviceState *qdev)
return 0;
}
+static void scsi_qdev_reset(DeviceState *qdev)
+{
+ SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+
+ scsi_device_purge_requests(dev);
+ if (dev->info->reset) {
+ dev->info->reset(dev);
+ }
+}
+
void scsi_qdev_register(SCSIDeviceInfo *info)
{
info->qdev.bus_info = &scsi_bus_info;
info->qdev.init = scsi_qdev_init;
info->qdev.unplug = qdev_simple_unplug_cb;
info->qdev.exit = scsi_qdev_exit;
+ info->qdev.reset = scsi_qdev_reset;
qdev_register(&info->qdev);
}
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index a8c7372..76c1748 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1160,13 +1160,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
}
}
-static void scsi_disk_reset(DeviceState *dev)
+static void scsi_disk_reset(SCSIDevice *dev)
{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
uint64_t nb_sectors;
- scsi_device_purge_requests(&s->qdev);
-
bdrv_get_geometry(s->bs, &nb_sectors);
nb_sectors /= s->cluster_size;
if (nb_sectors) {
@@ -1179,7 +1177,6 @@ static void scsi_destroy(SCSIDevice *dev)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
- scsi_device_purge_requests(&s->qdev);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
@@ -1266,7 +1263,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.fw_name = "disk",
.qdev.desc = "virtual SCSI disk",
.qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
+ .reset = scsi_disk_reset,
.init = scsi_hd_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
@@ -1287,7 +1284,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.fw_name = "disk",
.qdev.desc = "virtual SCSI CD-ROM",
.qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
+ .reset = scsi_disk_reset,
.init = scsi_cd_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
@@ -1307,7 +1304,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.fw_name = "disk",
.qdev.desc = "virtual SCSI disk or CD-ROM (legacy)",
.qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
+ .reset = scsi_disk_reset,
.init = scsi_disk_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 8e59c7e..b67e154 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -456,18 +456,10 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
}
-static void scsi_generic_reset(DeviceState *dev)
-{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
-
- scsi_device_purge_requests(&s->qdev);
-}
-
static void scsi_destroy(SCSIDevice *d)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
- scsi_device_purge_requests(&s->qdev);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
@@ -541,7 +533,6 @@ static SCSIDeviceInfo scsi_generic_info = {
.qdev.name = "scsi-generic",
.qdev.desc = "pass through generic scsi device (/dev/sg*)",
.qdev.size = sizeof(SCSIGenericState),
- .qdev.reset = scsi_generic_reset,
.init = scsi_generic_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
diff --git a/hw/scsi.h b/hw/scsi.h
index c1dca35..8d60c3a 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -62,11 +62,11 @@ int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
/* scsi-bus.c */
-typedef int (*scsi_qdev_initfn)(SCSIDevice *dev);
struct SCSIDeviceInfo {
DeviceInfo qdev;
- scsi_qdev_initfn init;
+ int (*init)(SCSIDevice *dev);
void (*destroy)(SCSIDevice *s);
+ void (*reset)(SCSIDevice *s);
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);
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 02/13] scsi: support parsing of SAM logical unit numbers
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 01/13] scsi: cleanup reset and destroy callbacks Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 03/13] scsi: add initiator field to SCSIRequest Paolo Bonzini
` (10 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
SAM logical unit numbers are complicated beasts that can address
multiple levels of buses and targets before finally reaching
logical units. Begin supporting them by correctly parsing vSCSI
LUNs.
Note that the current (admittedly incorrect) code switched from
a "bus X, target 0, lun 0" representation while OpenFirmware is
running (because OF prefers access mode 0, which places bus numbers
in the top byte) to "bus 0, target Y, lun 0" while Linux is running
(because Linux uses access mode 2, which places target numbers in
the top byte). With this patch, everything consistently uses the
former notation.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-bus.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/scsi-defs.h | 22 +++++++++++
hw/scsi.h | 7 +++
hw/spapr_vscsi.c | 18 ++-------
4 files changed, 142 insertions(+), 14 deletions(-)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 3918662..3b80541 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -741,3 +741,112 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev)
return strdup(path);
}
+
+/* Decode the bus and level parts of a LUN, as defined in the SCSI architecture
+ model. If false is returned, the LUN could not be parsed. If true
+ is return, "*bus" and "*target" identify the next two steps in the
+ hierarchical LUN.
+
+ "*lun" can be used with scsi_get_lun to continue the parsing. */
+static bool scsi_decode_level(uint64_t sam_lun, int *bus, int *target,
+ uint64_t *lun)
+{
+ switch (sam_lun >> 62) {
+ case ADDR_PERIPHERAL_DEVICE:
+ *bus = (sam_lun >> 56) & 0x3f;
+ if (*bus) {
+ /* The TARGET OR LUN field selects a target; walk the next
+ 16-bits to find the LUN. */
+ *target = (sam_lun >> 48) & 0xff;
+ *lun = sam_lun << 16;
+ } else {
+ /* The TARGET OR LUN field selects a LUN on the current
+ node, identified by bus 0. */
+ *target = 0;
+ *lun = (sam_lun & 0xff000000000000LL) | (1LL << 62);
+ }
+ return true;
+ case ADDR_LOGICAL_UNIT:
+ *bus = (sam_lun >> 53) & 7;
+ *target = (sam_lun >> 56) & 0x3f;
+ *lun = (sam_lun & 0x1f000000000000LL) | (1LL << 62);
+ return true;
+ case ADDR_FLAT_SPACE:
+ *bus = 0;
+ *target = 0;
+ *lun = sam_lun;
+ return true;
+ case ADDR_LOGICAL_UNIT_EXT:
+ if ((sam_lun >> 56) == ADDR_WELL_KNOWN_LUN ||
+ (sam_lun >> 56) == ADDR_FLAT_SPACE_EXT) {
+ *bus = 0;
+ *target = 0;
+ *lun = sam_lun;
+ return true;
+ }
+ return false;
+ }
+ abort();
+}
+
+/* Extract a single-level LUN number from a LUN, as specified in the
+ SCSI architecture model. Return -1 if this is not possible because
+ the LUN includes a bus or target component. */
+static int scsi_get_lun(uint64_t sam_lun)
+{
+ int bus, target;
+
+retry:
+ switch (sam_lun >> 62) {
+ case ADDR_PERIPHERAL_DEVICE:
+ case ADDR_LOGICAL_UNIT:
+ scsi_decode_level(sam_lun, &bus, &target, &sam_lun);
+ if (bus || target) {
+ return LUN_INVALID;
+ }
+ goto retry;
+
+ case ADDR_FLAT_SPACE:
+ return (sam_lun >> 48) & 0x3fff;
+ case ADDR_LOGICAL_UNIT_EXT:
+ if ((sam_lun >> 56) == ADDR_WELL_KNOWN_LUN) {
+ return LUN_WLUN_BASE | ((sam_lun >> 48) & 0xff);
+ }
+ if ((sam_lun >> 56) == ADDR_FLAT_SPACE_EXT) {
+ return (sam_lun >> 32) & 0xffffff;
+ }
+ return LUN_INVALID;
+ }
+ abort();
+}
+
+/* Extract bus and target from the given LUN and use it to identify a
+ SCSIDevice from a SCSIBus. Right now, only 1 target per bus is
+ supported. In the future a SCSIDevice could host its own SCSIBus,
+ in an alternation of devices that select a bus (target ports) and
+ devices that select a target (initiator ports). */
+SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun)
+{
+ int bus, target, decoded_lun;
+ uint64_t next_lun;
+
+ if (!scsi_decode_level(sam_lun, &bus, &target, &next_lun)) {
+ /* Unsupported LUN format. */
+ return NULL;
+ }
+ if (bus >= sbus->ndev || (bus == 0 && target > 0)) {
+ /* Out of range. */
+ return NULL;
+ }
+ if (target != 0) {
+ /* Only one target for now. */
+ return NULL;
+ }
+
+ decoded_lun = scsi_get_lun(next_lun);
+ if (decoded_lun != LUN_INVALID) {
+ *lun = decoded_lun;
+ return sbus->devs[bus];
+ }
+ return NULL;
+}
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index 413cce0..66dfd4a 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -164,3 +164,25 @@
#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
#define TYPE_NO_LUN 0x7f
+/*
+ * SCSI addressing methods (bits 62-63 of the LUN).
+ */
+#define ADDR_PERIPHERAL_DEVICE 0
+#define ADDR_FLAT_SPACE 1
+#define ADDR_LOGICAL_UNIT 2
+#define ADDR_LOGICAL_UNIT_EXT 3
+
+/*
+ * SCSI extended addressing methods (bits 56-63 of the LUN).
+ */
+#define ADDR_WELL_KNOWN_LUN 0xc1
+#define ADDR_FLAT_SPACE_EXT 0xd2
+
+/*
+ * SCSI well known LUNs (byte 1)
+ */
+#define WLUN_REPORT_LUNS 1
+#define WLUN_ACCESS_CONTROLS 2
+#define WLUN_TARGET_LOG_PAGES 3
+#define WLUN_SECURITY_PROTOCOL 4
+#define WLUN_MANAGEMENT_PROTOCOL 5
diff --git a/hw/scsi.h b/hw/scsi.h
index 8d60c3a..4ba1801 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -21,6 +21,12 @@ enum SCSIXferMode {
SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */
};
+enum SCSILun {
+ LUN_INVALID = -256,
+ LUN_WLUN_BASE = -256,
+ LUN_WLUN_MASK = 255
+};
+
typedef struct SCSISense {
uint8_t key;
uint8_t asc;
@@ -137,6 +143,7 @@ extern const struct SCSISense sense_code_LUN_FAILURE;
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
+SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 194db23..038b9e4 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -139,13 +139,6 @@ static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req)
return &s->reqs[tag];
}
-static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
-{
- /* XXX Figure that one out properly ! This is crackpot */
- *id = (srp_lun >> 56) & 0x7f;
- *lun = (srp_lun >> 48) & 0xff;
-}
-
static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
uint64_t length, uint8_t format)
{
@@ -642,20 +635,17 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
{
union srp_iu *srp = &req->iu.srp;
SCSIDevice *sdev;
- int n, id, lun;
+ int n, lun;
- vscsi_decode_id_lun(be64_to_cpu(srp->cmd.lun), &id, &lun);
-
- /* Qemu vs. linux issue with LUNs to be sorted out ... */
- sdev = (id < 8 && lun < 16) ? s->bus.devs[id] : NULL;
+ sdev = scsi_decode_lun(&s->bus, be64_to_cpu(srp->cmd.lun), &lun);
if (!sdev) {
- dprintf("VSCSI: Command for id %d with no drive\n", id);
if (srp->cmd.cdb[0] == INQUIRY) {
vscsi_inquiry_no_target(s, req);
} else {
vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0x00);
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
- } return 1;
+ }
+ return 1;
}
req->lun = lun;
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 03/13] scsi: add initiator field to SCSIRequest
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 01/13] scsi: cleanup reset and destroy callbacks Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 02/13] scsi: support parsing of SAM logical unit numbers Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 04/13] scsi: let a SCSIDevice have children devices Paolo Bonzini
` (9 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
Rather than relaying requests across multiple levels, I'm just
skipping the intermediate layers after the LUN has been parsed,
and letting the device know the bus which ultimately knows how
to process the request.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/esp.c | 6 +++---
hw/lsi53c895a.c | 8 ++++----
hw/scsi-bus.c | 15 +++++++++------
hw/scsi-disk.c | 6 +++---
hw/scsi-generic.c | 5 +++--
hw/scsi.h | 21 ++++++++++++++++-----
hw/spapr_vscsi.c | 6 +++---
hw/usb-msd.c | 6 +++---
8 files changed, 44 insertions(+), 29 deletions(-)
diff --git a/hw/esp.c b/hw/esp.c
index 6d3f5d2..e47dfec 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -190,7 +190,7 @@ 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);
+ ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->initiator);
if (req == s->current_req) {
scsi_req_unref(s->current_req);
@@ -244,7 +244,7 @@ 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;
- s->current_req = scsi_req_new(s->current_dev, 0, lun);
+ s->current_req = scsi_req_new(s->current_dev, &s->busdev.qdev, 0, lun);
datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
@@ -397,7 +397,7 @@ static void esp_do_dma(ESPState *s)
static void esp_command_complete(SCSIRequest *req, uint32_t status)
{
- ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
+ ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->initiator);
DPRINTF("SCSI Command complete\n");
if (s->ti_size != 0) {
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 83084b6..96b19f9 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -660,7 +660,7 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
static void lsi_request_cancelled(SCSIRequest *req)
{
- LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->initiator);
lsi_request *p;
if (s->current && req == s->current->req) {
@@ -715,7 +715,7 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len)
/* 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);
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->initiator);
int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
@@ -789,8 +789,8 @@ 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);
-
+ s->current->req = scsi_req_new(dev, &s->dev.qdev, s->current->tag,
+ s->current_lun);
n = scsi_req_enqueue(s->current->req, buf);
if (n) {
if (n > 0) {
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 3b80541..24e91bf 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -143,14 +143,16 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
return res;
}
-SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun)
+SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, DeviceState *initiator,
+ uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
req = qemu_mallocz(size);
req->refcount = 1;
- req->bus = scsi_bus_from_device(d);
+ req->bus = scsi_bus_from_device(d, initiator);
req->dev = d;
+ req->initiator = initiator;
req->tag = tag;
req->lun = lun;
req->status = -1;
@@ -158,9 +160,10 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
return req;
}
-SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun)
+SCSIRequest *scsi_req_new(SCSIDevice *d, DeviceState *initiator,
+ uint32_t tag, uint32_t lun)
{
- return d->info->alloc_req(d, tag, lun);
+ return d->info->alloc_req(d, initiator, tag, lun);
}
uint8_t *scsi_req_get_buf(SCSIRequest *req)
@@ -724,8 +727,8 @@ void scsi_device_purge_requests(SCSIDevice *sdev)
static char *scsibus_get_fw_dev_path(DeviceState *dev)
{
- SCSIDevice *d = (SCSIDevice*)dev;
- SCSIBus *bus = scsi_bus_from_device(d);
+ SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
char path[100];
int i;
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 76c1748..87e5ac8 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -80,14 +80,14 @@ 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 SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
- uint32_t lun)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, DeviceState *initiator,
+ 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);
+ req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, initiator, tag, lun);
r = DO_UPCAST(SCSIDiskReq, req, req);
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
return req;
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index b67e154..94aca18 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -96,11 +96,12 @@ static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
return size;
}
-static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, DeviceState *initiator,
+ uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
- req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
+ req = scsi_req_alloc(sizeof(SCSIGenericReq), d, initiator, tag, lun);
return req;
}
diff --git a/hw/scsi.h b/hw/scsi.h
index 4ba1801..fa3ca9b 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -35,6 +35,7 @@ typedef struct SCSISense {
struct SCSIRequest {
SCSIBus *bus;
+ DeviceState *initiator;
SCSIDevice *dev;
uint32_t refcount;
uint32_t tag;
@@ -73,7 +74,8 @@ struct SCSIDeviceInfo {
int (*init)(SCSIDevice *dev);
void (*destroy)(SCSIDevice *s);
void (*reset)(SCSIDevice *s);
- SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun);
+ SCSIRequest *(*alloc_req)(SCSIDevice *s, DeviceState *initiator,
+ 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);
@@ -103,9 +105,16 @@ void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
const SCSIBusOps *ops);
void scsi_qdev_register(SCSIDeviceInfo *info);
-static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
+static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d, DeviceState *initiator)
{
- return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
+ SCSIBus *bus;
+ DeviceState *dev;
+ dev = &d->qdev;
+ do {
+ bus = DO_UPCAST(SCSIBus, qbus, dev->parent_bus);
+ dev = bus->qbus.parent;
+ } while (dev != initiator);
+ return bus;
}
SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
@@ -145,8 +154,10 @@ int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun);
-SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
-SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun);
+SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, DeviceState *initiator,
+ uint32_t tag, uint32_t lun);
+SCSIRequest *scsi_req_new(SCSIDevice *d, DeviceState *initiator, 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);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 038b9e4..5b5fa87 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -475,7 +475,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
/* Callback to indicate that the SCSI layer has completed a transfer. */
static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
{
- VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
+ VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->initiator);
vscsi_req *req = vscsi_find_req(s, sreq);
uint8_t *buf;
int rc = 0;
@@ -561,7 +561,7 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
static void vscsi_request_cancelled(SCSIRequest *sreq)
{
- VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
+ VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->initiator);
vscsi_req *req = vscsi_find_req(s, sreq);
vscsi_put_req(s, req);
@@ -649,7 +649,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
}
req->lun = lun;
- req->sreq = scsi_req_new(sdev, req->qtag, lun);
+ req->sreq = scsi_req_new(sdev, &s->vdev.qdev, 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",
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index c59797b..195089c 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -212,7 +212,7 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
{
- MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->initiator);
USBPacket *p = s->packet;
if (req->tag != s->tag) {
@@ -275,7 +275,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
static void usb_msd_request_cancelled(SCSIRequest *req)
{
- MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->initiator);
if (req == s->req) {
scsi_req_unref(s->req);
@@ -386,7 +386,7 @@ 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->req = scsi_req_new(s->scsi_dev, s->tag, 0);
+ s->req = scsi_req_new(s->scsi_dev, &s->dev.qdev, s->tag, 0);
scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 04/13] scsi: let a SCSIDevice have children devices
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (2 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 03/13] scsi: add initiator field to SCSIRequest Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 05/13] scsi: let the bus pick a LUN for the child device Paolo Bonzini
` (8 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This provides the infrastructure for simple devices to pick LUNs.
Of course, this will not do anything until there is a device that
can report the existence of those LUNs.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/esp.c | 4 +++-
hw/lsi53c895a.c | 2 +-
hw/scsi-bus.c | 15 +++++++++++++++
hw/scsi.h | 3 +++
4 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/hw/esp.c b/hw/esp.c
index e47dfec..a821a0a 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -239,12 +239,14 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
{
+ SCSIDevice *dev;
int32_t datalen;
int lun;
DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7;
- s->current_req = scsi_req_new(s->current_dev, &s->busdev.qdev, 0, lun);
+ dev = scsi_find_lun(s->current_dev, lun, buf);
+ s->current_req = scsi_req_new(dev, &s->busdev.qdev, 0, lun);
datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 96b19f9..a908324 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -780,7 +780,7 @@ static void lsi_do_command(LSIState *s)
s->command_complete = 0;
id = (s->select_tag >> 8) & 0xf;
- dev = s->bus.devs[id];
+ dev = scsi_find_lun(s->bus.devs[id], s->current_lun, buf);
if (!dev) {
lsi_bad_selection(s, id);
return;
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 24e91bf..b44f308 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -745,6 +745,21 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev)
return strdup(path);
}
+/* Simplified walk of the SCSI bus hierarchy, for devices that only support
+ one bus and only flat-space LUNs (typically 3-bit ones!). */
+SCSIDevice *scsi_find_lun(SCSIDevice *sdev, int lun, uint8_t *cdb)
+{
+ SCSIBus *sbus = sdev->children;
+ if (!sbus ||
+ lun < 0 ||
+ (lun == 0 && cdb[0] == REPORT_LUNS) ||
+ lun >= sbus->ndev || sbus->devs[lun] == NULL) {
+ return sdev;
+ } else {
+ return sbus->devs[lun];
+ }
+}
+
/* Decode the bus and level parts of a LUN, as defined in the SCSI architecture
model. If false is returned, the LUN could not be parsed. If true
is return, "*bus" and "*target" identify the next two steps in the
diff --git a/hw/scsi.h b/hw/scsi.h
index fa3ca9b..8d5737b 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -59,6 +59,7 @@ struct SCSIDevice
uint32_t id;
BlockConf conf;
SCSIDeviceInfo *info;
+ SCSIBus *children;
QTAILQ_HEAD(, SCSIRequest) requests;
int blocksize;
int type;
@@ -152,7 +153,9 @@ extern const struct SCSISense sense_code_LUN_FAILURE;
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
+
SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun);
+SCSIDevice *scsi_find_lun(SCSIDevice *sdev, int lun, uint8_t *cdb);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, DeviceState *initiator,
uint32_t tag, uint32_t lun);
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 05/13] scsi: let the bus pick a LUN for the child device
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (3 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 04/13] scsi: let a SCSIDevice have children devices Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 06/13] scsi-generic: fix passthrough of devices with LUN != 0 Paolo Bonzini
` (7 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
The scsi-id may be a LUN value or a target id. The bus knows,
so add a callback to that end. The default value in case there is
no implementation is to always returns zero.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-bus.c | 3 +++
hw/scsi-disk.c | 6 +++---
hw/scsi.h | 2 ++
3 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index b44f308..b64ed68 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -54,6 +54,9 @@ static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
}
bus->devs[dev->id] = dev;
+ if (bus->ops->get_child_lun) {
+ dev->lun = bus->ops->get_child_lun(dev);
+ }
dev->info = info;
QTAILQ_INIT(&dev->requests);
rc = dev->info->init(dev);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 87e5ac8..b2c8d6e 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -518,7 +518,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memset(outbuf, 0, buflen);
- if (req->lun) {
+ if (req->lun != s->qdev.lun) {
outbuf[0] = 0x7f; /* LUN not supported */
return buflen;
}
@@ -1024,8 +1024,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
}
#endif
- if (req->lun) {
- /* Only LUN 0 supported. */
+ if (req->lun != s->qdev.lun) {
+ /* Only one LUN supported. */
DPRINTF("Unimplemented LUN %d\n", req->lun);
if (command != REQUEST_SENSE && command != INQUIRY) {
scsi_command_complete(r, CHECK_CONDITION,
diff --git a/hw/scsi.h b/hw/scsi.h
index 8d5737b..67d18cc 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -57,6 +57,7 @@ struct SCSIDevice
{
DeviceState qdev;
uint32_t id;
+ uint32_t lun;
BlockConf conf;
SCSIDeviceInfo *info;
SCSIBus *children;
@@ -90,6 +91,7 @@ struct SCSIBusOps {
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
void (*complete)(SCSIRequest *req, uint32_t arg);
void (*cancel)(SCSIRequest *req);
+ int (*get_child_lun)(SCSIDevice *dev);
};
struct SCSIBus {
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 06/13] scsi-generic: fix passthrough of devices with LUN != 0
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (4 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 05/13] scsi: let the bus pick a LUN for the child device Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 07/13] scsi: add walking of hierarchical LUNs Paolo Bonzini
` (6 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This allows redirecting them to LUN0 in the emulated target and, after
the scsi-target device is added, will allow picking an arbitrary LUN
for passed-through devices.
This patch duplicates from scsi-disk the handling of REPORT LUNS and of
INQUIRY for invalid LUNs. This is temporary and is going away after
the necessary infrastructure is added to qdev.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-generic.c | 39 +++++++++++++++++++++++++++++++++------
1 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 94aca18..1611023 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -60,7 +60,6 @@ struct SCSIGenericState
{
SCSIDevice qdev;
BlockDriverState *bs;
- int lun;
int driver_status;
uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
uint8_t senselen;
@@ -232,8 +231,11 @@ static void scsi_read_data(SCSIRequest *req)
return;
}
- if (r->req.cmd.buf[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
- {
+ switch (r->req.cmd.buf[0]) {
+ case REQUEST_SENSE:
+ if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) {
+ break;
+ }
s->senselen = MIN(r->len, s->senselen);
memcpy(r->buf, s->sensebuf, s->senselen);
r->io_header.driver_status = 0;
@@ -248,6 +250,32 @@ static void scsi_read_data(SCSIRequest *req)
/* Clear sensebuf after REQUEST_SENSE */
scsi_clear_sense(s);
return;
+
+ case REPORT_LUNS:
+ assert(!s->qdev.lun);
+ if (r->req.cmd.xfer < 16) {
+ scsi_command_complete(r, -EINVAL);
+ return;
+ }
+ r->io_header.driver_status = 0;
+ r->io_header.status = 0;
+ r->io_header.dxfer_len = 16;
+ r->len = -1;
+ r->buf[3] = 8;
+ scsi_req_data(&r->req, 16);
+ scsi_command_complete(r, 0);
+ return;
+
+ case INQUIRY:
+ if (req->lun != s->qdev.lun) {
+ if (r->req.cmd.xfer < 1) {
+ scsi_command_complete(r, -EINVAL);
+ return;
+ }
+ r->buf[0] = 0x7f;
+ return;
+ }
+ break;
}
ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
@@ -338,7 +366,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
int ret;
- if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) {
+ if (cmd[0] != REQUEST_SENSE && cmd[0] != INQUIRY &&
+ req->lun != s->qdev.lun) {
DPRINTF("Unimplemented LUN %d\n", req->lun);
scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
r->req.status = CHECK_CONDITION;
@@ -505,8 +534,6 @@ static int scsi_generic_initfn(SCSIDevice *dev)
}
/* define device state */
- s->lun = scsiid.lun;
- DPRINTF("LUN %d\n", s->lun);
s->qdev.type = scsiid.scsi_type;
DPRINTF("device type %d\n", s->qdev.type);
if (s->qdev.type == TYPE_TAPE) {
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 07/13] scsi: add walking of hierarchical LUNs
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (5 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 06/13] scsi-generic: fix passthrough of devices with LUN != 0 Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 08/13] scsi: introduce the scsi-path device Paolo Bonzini
` (5 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This adds support for parsing a hierarchical LUN and mapping the
result to the qdev hierarchy.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-bus.c | 83 +++++++++++++++++++++++++++++++++++++++++++----------
hw/scsi.h | 9 +++++-
hw/spapr_vscsi.c | 6 ++-
3 files changed, 79 insertions(+), 19 deletions(-)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index b64ed68..54f308d 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -841,33 +841,84 @@ retry:
abort();
}
-/* Extract bus and target from the given LUN and use it to identify a
- SCSIDevice from a SCSIBus. Right now, only 1 target per bus is
- supported. In the future a SCSIDevice could host its own SCSIBus,
- in an alternation of devices that select a bus (target ports) and
- devices that select a target (initiator ports). */
-SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun)
+/* Reusable implementation of the decode_lun entry in SCSIBusOps. */
+SCSIDevice *scsi_decode_bus_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+ uint64_t *next_lun)
{
- int bus, target, decoded_lun;
- uint64_t next_lun;
+ int bus, target;
+ uint64_t my_next_lun;
+ SCSIDevice *sdev;
- if (!scsi_decode_level(sam_lun, &bus, &target, &next_lun)) {
+ if (!scsi_decode_level(sam_lun, &bus, &target, &my_next_lun)) {
/* Unsupported LUN format. */
return NULL;
}
- if (bus >= sbus->ndev || (bus == 0 && target > 0)) {
+ if (bus >= sbus->ndev) {
/* Out of range. */
return NULL;
}
- if (target != 0) {
- /* Only one target for now. */
+
+ sdev = sbus->devs[bus];
+ if (!sdev) {
+ return NULL;
+ } else if (!sdev->children || !sdev->children->ops->decode_lun) {
+ *next_lun = my_next_lun;
+ return target ? NULL : sdev;
+ } else {
+ /* Next we'll find the target, so pass down the same LUN we got. */
+ return sdev->children->ops->decode_lun(sdev->children, sam_lun,
+ next_lun);
+ }
+}
+
+SCSIDevice *scsi_decode_target_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+ uint64_t *next_lun)
+{
+ int bus, target;
+ SCSIDevice *sdev;
+
+ if (!scsi_decode_level(sam_lun, &bus, &target, next_lun)) {
+ /* Unsupported LUN format. */
+ return NULL;
+ }
+ if (target >= sbus->ndev) {
+ /* Out of range. */
return NULL;
}
+ sdev = sbus->devs[target];
+ if (!sdev || !sdev->children || !sdev->children->ops->decode_lun ||
+ (*next_lun >> 56) == ADDR_WELL_KNOWN_LUN) {
+ return sdev;
+ } else {
+ return sdev->children->ops->decode_lun(sdev->children, *next_lun,
+ next_lun);
+ }
+}
+
+/* Extract bus and target from the given LUN and use it to identify a
+ SCSIDevice from a SCSIBus. Right now, only 1 target per bus is
+ supported. In the future a SCSIDevice could host its own SCSIBus,
+ in an alternation of devices that select a bus (target ports) and
+ devices that select a target (initiator ports). */
+SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun,
+ uint8_t *cdb, int *lun)
+{
+ int decoded_lun;
+ uint64_t next_lun;
+ SCSIDevice *sdev;
+
+ sdev = sbus->ops->decode_lun(sbus, sam_lun, &next_lun);
+ if (!sdev) {
+ return NULL;
+ }
decoded_lun = scsi_get_lun(next_lun);
- if (decoded_lun != LUN_INVALID) {
- *lun = decoded_lun;
- return sbus->devs[bus];
+ if (decoded_lun == LUN_INVALID) {
+ return NULL;
+ }
+ if ((decoded_lun & ~LUN_WLUN_MASK) == LUN_WLUN_BASE) {
+ return sdev;
}
- return NULL;
+ *lun = decoded_lun;
+ return scsi_find_lun(sdev, decoded_lun, cdb);
}
diff --git a/hw/scsi.h b/hw/scsi.h
index 67d18cc..9f70771 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -92,6 +92,8 @@ struct SCSIBusOps {
void (*complete)(SCSIRequest *req, uint32_t arg);
void (*cancel)(SCSIRequest *req);
int (*get_child_lun)(SCSIDevice *dev);
+ SCSIDevice *(*decode_lun)(SCSIBus *sbus, uint64_t sam_lun,
+ uint64_t *next_lun);
};
struct SCSIBus {
@@ -156,7 +158,12 @@ extern const struct SCSISense sense_code_LUN_FAILURE;
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
-SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun);
+SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, uint8_t *cdb,
+ int *lun);
+SCSIDevice *scsi_decode_bus_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+ uint64_t *next_lun);
+SCSIDevice *scsi_decode_target_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+ uint64_t *next_lun);
SCSIDevice *scsi_find_lun(SCSIDevice *sdev, int lun, uint8_t *cdb);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, DeviceState *initiator,
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 5b5fa87..ace9dac 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -637,7 +637,8 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
SCSIDevice *sdev;
int n, lun;
- sdev = scsi_decode_lun(&s->bus, be64_to_cpu(srp->cmd.lun), &lun);
+ sdev = scsi_decode_lun(&s->bus, be64_to_cpu(srp->cmd.lun),
+ srp->cmd.cdb, &lun);
if (!sdev) {
if (srp->cmd.cdb[0] == INQUIRY) {
vscsi_inquiry_no_target(s, req);
@@ -915,7 +916,8 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
static const struct SCSIBusOps vscsi_scsi_ops = {
.transfer_data = vscsi_transfer_data,
.complete = vscsi_command_complete,
- .cancel = vscsi_request_cancelled
+ .cancel = vscsi_request_cancelled,
+ .decode_lun = scsi_decode_bus_from_lun
};
static int spapr_vscsi_init(VIOsPAPRDevice *dev)
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 08/13] scsi: introduce the scsi-path device
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (6 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 07/13] scsi: add walking of hierarchical LUNs Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 09/13] scsi: introduce the scsi-target device Paolo Bonzini
` (4 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This introduces a device that dispatches SCSI requests according to the
"target" field of a hierarchical LUN. The device does not have to do
any processing: requests are always forwarded to a target.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
Makefile.objs | 2 +-
hw/scsi-luns.c | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletions(-)
create mode 100644 hw/scsi-luns.c
diff --git a/Makefile.objs b/Makefile.objs
index 90838f6..90940d3 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -85,7 +85,7 @@ common-obj-$(CONFIG_MAX111X) += max111x.o
common-obj-$(CONFIG_DS1338) += ds1338.o
common-obj-y += i2c.o smbus.o smbus_eeprom.o
common-obj-y += eeprom93xx.o
-common-obj-y += scsi-disk.o cdrom.o
+common-obj-y += scsi-disk.o cdrom.o scsi-luns.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
diff --git a/hw/scsi-luns.c b/hw/scsi-luns.c
new file mode 100644
index 0000000..699e3c1
--- /dev/null
+++ b/hw/scsi-luns.c
@@ -0,0 +1,42 @@
+/*
+ * SCSI Device emulation
+ *
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>.
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "scsi.h"
+#include "sysemu.h"
+
+static struct SCSIBusOps path_scsi_ops = {
+ .decode_lun = scsi_decode_target_from_lun
+};
+
+static int scsi_path_initfn(SCSIDevice *s)
+{
+ s->children = qemu_mallocz(sizeof(SCSIBus));
+ scsi_bus_new(s->children, &s->qdev, 1, MAX_SCSI_DEVS,
+ &path_scsi_ops);
+ return 0;
+}
+
+static SCSIDeviceInfo scsi_path_info = {
+ .qdev.name = "scsi-path",
+ .qdev.desc = "SCSI initiator device connected to multiple targets",
+ .qdev.size = sizeof(SCSIDevice),
+ .init = scsi_path_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void scsi_luns_register_devices(void)
+{
+ scsi_qdev_register(&scsi_path_info);
+}
+device_init(scsi_luns_register_devices)
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 09/13] scsi: introduce the scsi-target device
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (7 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 08/13] scsi: introduce the scsi-path device Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-07 8:09 ` Stefan Hajnoczi
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 10/13] scsi: include bus and device levels Paolo Bonzini
` (3 subsequent siblings)
12 siblings, 1 reply; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This introduces a device that dispatches SCSI requests according to the
"LUN" field of a hierarchical LUN. In addition, the device always
processes REPORT LUNS commands, as well as commands for invalid and
well-known LUNs.
Finally, the device implements dummy INQUIRY emulation in case the
user sets up all devices on non-zero LUNs. This is not enough in Linux
to trigger a scan; I'll fix it before final submission.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-defs.h | 2 +
hw/scsi-luns.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 342 insertions(+), 0 deletions(-)
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index 66dfd4a..32fc82a 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -162,6 +162,9 @@
* - treated as TYPE_DISK */
#define TYPE_MEDIUM_CHANGER 0x08
#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define TYPE_WLUN 0x1e /* Well known LUN */
+#define TYPE_NOT_PRESENT 0x1f
+#define TYPE_INACTIVE 0x20
#define TYPE_NO_LUN 0x7f
/*
diff --git a/hw/scsi-luns.c b/hw/scsi-luns.c
index 699e3c1..4b158a9 100644
--- a/hw/scsi-luns.c
+++ b/hw/scsi-luns.c
@@ -8,11 +8,310 @@
* This code is licenced under the LGPL.
*/
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, ...) \
+do { printf("scsi-target: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "scsi-target: " fmt , ## __VA_ARGS__); } while (0)
+
#include "qemu-common.h"
#include "qemu-error.h"
#include "scsi.h"
+#include "scsi-defs.h"
#include "sysemu.h"
+#define SCSI_MAX_INQUIRY_LEN 256
+
+typedef struct SCSITargetState SCSITargetState;
+
+ typedef struct SCSITargetReq {
+ SCSIRequest req;
+ uint32_t buflen;
+ uint32_t status;
+ uint8_t buf[128];
+ uint8_t *p_buf;
+} SCSITargetReq;
+
+struct SCSITargetState
+{
+ SCSIDevice qdev;
+ char *version;
+ SCSISense sense;
+};
+
+static SCSIRequest *scsi_new_request(SCSIDevice *d, DeviceState *initiator,
+ uint32_t tag, uint32_t lun)
+{
+ SCSITargetState *s = DO_UPCAST(SCSITargetState, qdev, d);
+ SCSIRequest *req;
+ SCSITargetReq *r;
+
+ req = scsi_req_alloc(sizeof(SCSITargetReq), &s->qdev, initiator, tag, lun);
+ r = DO_UPCAST(SCSITargetReq, req, req);
+ return req;
+}
+
+static void scsi_free_request(SCSIRequest *req)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ if (r->p_buf) {
+ qemu_free(r->p_buf);
+ }
+}
+
+static void scsi_target_clear_sense(SCSITargetState *s)
+{
+ memset(&s->sense, 0, sizeof(s->sense));
+}
+
+static void scsi_req_set_status(SCSITargetReq *r, int status, SCSISense sense)
+{
+ SCSITargetState *s = DO_UPCAST(SCSITargetState, qdev, r->req.dev);
+
+ r->req.status = status;
+ s->sense = sense;
+}
+
+/* Helper function for command completion. */
+static void scsi_command_complete(SCSITargetReq *r, int status, SCSISense sense)
+{
+ scsi_req_set_status(r, status, sense);
+ scsi_req_complete(&r->req);
+}
+
+/* Read more data from scsi device into buffer. */
+static void scsi_read_data(SCSIRequest *req)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+ uint32_t n;
+
+ n = r->buflen;
+ if (n > 0) {
+ r->buflen = 0;
+ scsi_req_data(&r->req, n);
+ } else {
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
+ }
+}
+
+/* Return a pointer to the data buffer. */
+static uint8_t *scsi_get_buf(SCSIRequest *req)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ return r->p_buf ? r->p_buf : r->buf;
+}
+
+/* Copy sense information into the provided buffer */
+static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
+{
+ SCSITargetState *s = DO_UPCAST(SCSITargetState, qdev, req->dev);
+
+ return scsi_build_sense(s->sense, outbuf, len, len > 14);
+}
+
+static void store_lun(uint8_t *outbuf, int id)
+{
+ if (id < 256) {
+ outbuf[1] = id;
+ return;
+ }
+ if (id < 16384) {
+ outbuf[1] = (id & 255);
+ outbuf[0] = (id >> 8) | (ADDR_FLAT_SPACE << 6);
+ return;
+ }
+ outbuf[3] = (id & 255);
+ outbuf[2] = ((id >> 8) & 255);
+ outbuf[1] = ((id >> 16) & 255);
+ outbuf[0] = ADDR_FLAT_SPACE_EXT;
+}
+
+static int scsi_target_emulate_report_luns(SCSITargetReq *r)
+{
+ SCSIBus *bus = r->req.dev->children;
+ DeviceState *d;
+ uint32_t list_len;
+ uint8_t *outbuf;
+ int n, buflen;
+ if (r->req.cmd.xfer < 16) {
+ return -1;
+ }
+ if (r->req.cmd.buf[2] > 2) {
+ return -1;
+ }
+
+ n = 0;
+ QLIST_FOREACH(d, &bus->qbus.children, sibling) {
+ n++;
+ }
+
+ list_len = n * 8;
+ buflen = list_len + 8;
+ if (buflen > sizeof(r->buf)) {
+ r->p_buf = qemu_malloc(buflen);
+ outbuf = r->p_buf;
+ } else {
+ outbuf = r->buf;
+ }
+
+ buflen = MIN(buflen, r->req.cmd.xfer);
+ memset(outbuf, 0, buflen);
+ outbuf[3] = (list_len & 0xff);
+ outbuf[2] = ((list_len >> 8) & 0xff);
+ outbuf[1] = ((list_len >> 16) & 0xff);
+ outbuf[0] = ((list_len >> 24) & 0xff);
+ outbuf += 8;
+
+ QLIST_FOREACH(d, &bus->qbus.children, sibling) {
+ SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, d);
+ store_lun(outbuf, dev->id);
+ outbuf += 8;
+ }
+ return buflen;
+}
+
+static int scsi_target_emulate_inquiry(SCSITargetReq *r)
+{
+ SCSITargetState *s = DO_UPCAST(SCSITargetState, qdev, r->req.dev);
+ uint8_t *outbuf = r->buf;
+ int buflen = 0;
+
+ if (r->req.cmd.buf[1] & 0x2) {
+ /* Command support data - optional, not implemented */
+ return -1;
+ }
+
+ if (r->req.cmd.buf[1] & 0x1) {
+ /* Vital product data */
+ uint8_t page_code = r->req.cmd.buf[2];
+ if (r->req.cmd.xfer < 4) {
+ return -1;
+ }
+
+ outbuf[buflen++] = page_code ; // this page
+ outbuf[buflen++] = 0x00;
+
+ switch (page_code) {
+ case 0x00: /* Supported page codes, mandatory */
+ {
+ int pages;
+ pages = buflen++;
+ outbuf[buflen++] = 0x00; // list of supported pages (this page)
+ outbuf[pages] = buflen - pages - 1; // number of pages
+ break;
+ }
+ default:
+ return -1;
+ }
+ /* done with EVPD */
+ return buflen;
+ }
+
+ /* Standard INQUIRY data */
+ if (r->req.cmd.buf[2] != 0) {
+ return -1;
+ }
+
+ /* PAGE CODE == 0 */
+ if (r->req.cmd.xfer < 5) {
+ return -1;
+ }
+
+ buflen = MIN(36, r->req.cmd.xfer);
+ memset(outbuf, 0, buflen);
+
+ if (r->req.lun == 0) {
+ outbuf[0] = TYPE_INACTIVE | TYPE_NOT_PRESENT;
+ } else if (r->req.lun == (LUN_WLUN_BASE | WLUN_REPORT_LUNS)) {
+ outbuf[0] = TYPE_WLUN;
+ } else {
+ outbuf[0] = TYPE_NO_LUN; /* LUN not supported */
+ return buflen;
+ }
+
+ outbuf[2] = 5; /* Version */
+ outbuf[3] = 2 | 0x10; /* HiSup, response data format */
+ outbuf[4] = MAX(buflen, 36) - 5; /* Additional Length = (Len - 1) - 4 */
+ outbuf[7] = 0x10 | (r->req.bus->tcq ? 0x02 : 0); /* Sync, TCQ. */
+ memcpy(&outbuf[8], "QEMU ", 8);
+ memset(&outbuf[16], ' ', 16);
+ strncpy((char *) &outbuf[32], s->version, 4);
+ return buflen;
+}
+
+/* Execute a scsi command. Returns the length of the data expected by the
+ command. This will be Positive for data transfers from the device
+ (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(SCSIRequest *req, uint8_t *buf)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+ SCSITargetState *s = DO_UPCAST(SCSITargetState, qdev, req->dev);
+ int32_t len;
+ uint8_t command;
+
+ command = buf[0];
+
+ if (scsi_req_parse(&r->req, buf) != 0) {
+ BADF("Unsupported command length, command %x\n", command);
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ return 0;
+ }
+
+ if (command != REQUEST_SENSE && command != INQUIRY &&
+ req->lun != 0 && req->lun != (LUN_WLUN_BASE | WLUN_REPORT_LUNS)) {
+ /* Only one LUN supported. */
+ DPRINTF("Unimplemented LUN %d\n", req->lun);
+ scsi_command_complete(r, CHECK_CONDITION,
+ SENSE_CODE(LUN_NOT_SUPPORTED));
+ return 0;
+ }
+ switch (command) {
+ case TEST_UNIT_READY:
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
+ return 0;
+ case REQUEST_SENSE:
+ if (req->cmd.xfer < 4)
+ goto illegal_request;
+ r->buflen = scsi_build_sense(s->sense, r->buf, req->cmd.xfer,
+ req->cmd.xfer > 13);
+ scsi_target_clear_sense(s);
+ scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE));
+ break;
+ case INQUIRY:
+ len = scsi_target_emulate_inquiry(r);
+ if (len < 0)
+ goto illegal_request;
+ r->buflen = len;
+ scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE));
+ break;
+ case REPORT_LUNS:
+ len = scsi_target_emulate_report_luns(r);
+ if (len < 0)
+ goto illegal_request;
+ r->buflen = len;
+ scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE));
+ break;
+ default:
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ return 0;
+ illegal_request:
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
+ return 0;
+ }
+ assert (r->req.cmd.mode == SCSI_XFER_FROM_DEV);
+ return r->buflen;
+}
+
static struct SCSIBusOps path_scsi_ops = {
.decode_lun = scsi_decode_target_from_lun
};
@@ -35,8 +334,49 @@ static SCSIDeviceInfo scsi_path_info = {
},
};
+static int scsi_target_get_child_lun(SCSIDevice *dev)
+{
+ return dev->id;
+}
+
+static struct SCSIBusOps target_scsi_ops = {
+ .get_child_lun = scsi_target_get_child_lun
+};
+
+static int scsi_target_initfn(SCSIDevice *dev)
+{
+ SCSITargetState *s = DO_UPCAST(SCSITargetState, qdev, dev);
+
+ if (!s->version) {
+ s->version = qemu_strdup(QEMU_VERSION);
+ }
+
+ s->qdev.children = qemu_mallocz(sizeof(SCSIBus));
+ scsi_bus_new(s->qdev.children, &dev->qdev, 1, MAX_SCSI_DEVS,
+ &target_scsi_ops);
+ return 0;
+}
+
+static SCSIDeviceInfo scsi_target_info = {
+ .qdev.name = "scsi-target",
+ .qdev.desc = "SCSI target device connected to multiple logical units",
+ .qdev.size = sizeof(SCSITargetState),
+ .init = scsi_target_initfn,
+ .alloc_req = scsi_new_request,
+ .free_req = scsi_free_request,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .get_buf = scsi_get_buf,
+ .get_sense = scsi_get_sense,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_STRING("ver", SCSITargetState, version),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
static void scsi_luns_register_devices(void)
{
scsi_qdev_register(&scsi_path_info);
+ scsi_qdev_register(&scsi_target_info);
}
device_init(scsi_luns_register_devices)
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [Qemu-devel] [RFC PATCH v2 09/13] scsi: introduce the scsi-target device
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 09/13] scsi: introduce the scsi-target device Paolo Bonzini
@ 2011-06-07 8:09 ` Stefan Hajnoczi
0 siblings, 0 replies; 15+ messages in thread
From: Stefan Hajnoczi @ 2011-06-07 8:09 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel
On Mon, Jun 06, 2011 at 06:04:18PM +0200, Paolo Bonzini wrote:
> +static SCSIRequest *scsi_new_request(SCSIDevice *d, DeviceState *initiator,
> + uint32_t tag, uint32_t lun)
> +{
> + SCSITargetState *s = DO_UPCAST(SCSITargetState, qdev, d);
> + SCSIRequest *req;
> + SCSITargetReq *r;
> +
> + req = scsi_req_alloc(sizeof(SCSITargetReq), &s->qdev, initiator, tag, lun);
> + r = DO_UPCAST(SCSITargetReq, req, req);
r not needed
> + return req;
> +}
> +
> +static void scsi_free_request(SCSIRequest *req)
> +{
> + SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
> +
> + if (r->p_buf) {
> + qemu_free(r->p_buf);
> + }
qemu_free(NULL) is a nop, no need to check NULL
Stefan
^ permalink raw reply [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 10/13] scsi: include bus and device levels
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (8 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 09/13] scsi: introduce the scsi-target device Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 11/13] qdev: introduce automatic creation of buses Paolo Bonzini
` (2 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This gives each SCSIBus and SCSIDevice a position on the SCSI hierarchy:
- a SCSI_HOST dispatches on the "path" (aka "bus" aka "channel") axis.
- a SCSI_PATH dispatches on the target axis.
- a SCSI_TARGET dispatches on the LUN axis and handles requests for
REPORT LUNS commands, invalid LUNs, and well-known LUNs. It may
also have children that are paths, though this is not very much
supported by operating systems.
- a SCSI_LUN is at the bottom of the hierarchy.
This is used in the next patch to create missing layers in the
qdev representation.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/esp.c | 2 +-
hw/lsi53c895a.c | 2 +-
hw/scsi-bus.c | 3 ++-
hw/scsi-disk.c | 3 +++
hw/scsi-generic.c | 1 +
hw/scsi-luns.c | 6 ++++--
hw/scsi.h | 11 ++++++++++-
hw/spapr_vscsi.c | 2 +-
hw/usb-msd.c | 2 +-
9 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/hw/esp.c b/hw/esp.c
index a821a0a..0f16dcf 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -751,7 +751,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_scsi_ops);
+ scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, SCSI_PATH, &esp_scsi_ops);
return scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index a908324..2e5dd42 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -2287,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_scsi_ops);
+ scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, SCSI_PATH, &lsi_scsi_ops);
if (!dev->qdev.hotplugged) {
return scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 54f308d..43a1cd7 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -21,10 +21,11 @@ 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,
- const SCSIBusOps *ops)
+ enum SCSIDeviceLevel level, const SCSIBusOps *ops)
{
qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
bus->busnr = next_scsi_bus++;
+ bus->level = level;
bus->tcq = tcq;
bus->ndev = ndev;
bus->ops = ops;
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index b2c8d6e..0a4fbee 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1263,6 +1263,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.fw_name = "disk",
.qdev.desc = "virtual SCSI disk",
.qdev.size = sizeof(SCSIDiskState),
+ .level = SCSI_LUN,
.reset = scsi_disk_reset,
.init = scsi_hd_initfn,
.destroy = scsi_destroy,
@@ -1284,6 +1285,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.fw_name = "disk",
.qdev.desc = "virtual SCSI CD-ROM",
.qdev.size = sizeof(SCSIDiskState),
+ .level = SCSI_LUN,
.reset = scsi_disk_reset,
.init = scsi_cd_initfn,
.destroy = scsi_destroy,
@@ -1304,6 +1306,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.fw_name = "disk",
.qdev.desc = "virtual SCSI disk or CD-ROM (legacy)",
.qdev.size = sizeof(SCSIDiskState),
+ .level = SCSI_LUN,
.reset = scsi_disk_reset,
.init = scsi_disk_initfn,
.destroy = scsi_destroy,
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 1611023..e1f9d0e 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -561,6 +561,7 @@ static SCSIDeviceInfo scsi_generic_info = {
.qdev.name = "scsi-generic",
.qdev.desc = "pass through generic scsi device (/dev/sg*)",
.qdev.size = sizeof(SCSIGenericState),
+ .level = SCSI_LUN,
.init = scsi_generic_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
diff --git a/hw/scsi-luns.c b/hw/scsi-luns.c
index 4b158a9..2cf36f0 100644
--- a/hw/scsi-luns.c
+++ b/hw/scsi-luns.c
@@ -319,7 +319,7 @@ static struct SCSIBusOps path_scsi_ops = {
static int scsi_path_initfn(SCSIDevice *s)
{
s->children = qemu_mallocz(sizeof(SCSIBus));
- scsi_bus_new(s->children, &s->qdev, 1, MAX_SCSI_DEVS,
+ scsi_bus_new(s->children, &s->qdev, 1, MAX_SCSI_DEVS, SCSI_PATH,
&path_scsi_ops);
return 0;
}
@@ -329,6 +329,7 @@ static SCSIDeviceInfo scsi_path_info = {
.qdev.desc = "SCSI initiator device connected to multiple targets",
.qdev.size = sizeof(SCSIDevice),
.init = scsi_path_initfn,
+ .level = SCSI_PATH,
.qdev.props = (Property[]) {
DEFINE_PROP_END_OF_LIST(),
},
@@ -352,7 +353,7 @@ static int scsi_target_initfn(SCSIDevice *dev)
}
s->qdev.children = qemu_mallocz(sizeof(SCSIBus));
- scsi_bus_new(s->qdev.children, &dev->qdev, 1, MAX_SCSI_DEVS,
+ scsi_bus_new(s->qdev.children, &dev->qdev, 1, MAX_SCSI_DEVS, SCSI_TARGET,
&target_scsi_ops);
return 0;
}
@@ -361,6 +362,7 @@ static SCSIDeviceInfo scsi_target_info = {
.qdev.name = "scsi-target",
.qdev.desc = "SCSI target device connected to multiple logical units",
.qdev.size = sizeof(SCSITargetState),
+ .level = SCSI_TARGET,
.init = scsi_target_initfn,
.alloc_req = scsi_new_request,
.free_req = scsi_free_request,
diff --git a/hw/scsi.h b/hw/scsi.h
index 9f70771..f4fec61 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -27,6 +27,13 @@ enum SCSILun {
LUN_WLUN_MASK = 255
};
+enum SCSIDeviceLevel {
+ SCSI_HOST = 0,
+ SCSI_PATH = 1,
+ SCSI_TARGET = 2,
+ SCSI_LUN = 3
+};
+
typedef struct SCSISense {
uint8_t key;
uint8_t asc;
@@ -73,6 +80,7 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
/* scsi-bus.c */
struct SCSIDeviceInfo {
DeviceInfo qdev;
+ enum SCSIDeviceLevel level;
int (*init)(SCSIDevice *dev);
void (*destroy)(SCSIDevice *s);
void (*reset)(SCSIDevice *s);
@@ -100,6 +108,7 @@ struct SCSIBus {
BusState qbus;
int busnr;
+ enum SCSIDeviceLevel level;
int tcq, ndev;
const SCSIBusOps *ops;
@@ -107,7 +116,7 @@ struct SCSIBus {
};
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
- const SCSIBusOps *ops);
+ enum SCSIDeviceLevel level, const SCSIBusOps *ops);
void scsi_qdev_register(SCSIDeviceInfo *info);
static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d, DeviceState *initiator)
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index ace9dac..36dba40 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -935,7 +935,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,
+ scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT, SCSI_HOST,
&vscsi_scsi_ops);
if (!dev->qdev.hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus);
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 195089c..5b34b7b 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -537,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_scsi_ops);
+ scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, SCSI_PATH, &usb_msd_scsi_ops);
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
if (!s->scsi_dev) {
return -1;
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 11/13] qdev: introduce automatic creation of buses
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (9 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 10/13] scsi: include bus and device levels Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 12/13] scsi: create scsi-path and scsi-target devices automatically Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 13/13] scsi: delete handling of REPORT LUNS and unknown LUNs outside scsi-target Paolo Bonzini
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Markus Armbruster
When a logical unit is hung directly below an HBA, we want to introduce
the "missing levels" to avoid duplicating generic target code across
all devices. To do this, I introduce a callback in qdev that is given
the bus specified by the user, and returns the actual bus to use.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
---
hw/qdev.c | 13 +++++++++++++
hw/qdev.h | 3 +++
2 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/hw/qdev.c b/hw/qdev.c
index 9519f5d..847bcda 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -204,6 +204,17 @@ int qdev_device_help(QemuOpts *opts)
return 1;
}
+static BusState *qbus_realize_topology(BusState *bus, DeviceInfo *info,
+ QemuOpts *opts)
+{
+ BusState *old = NULL;
+ while (old != bus && bus->info->realize_topology) {
+ old = bus;
+ bus = bus->info->realize_topology(bus, info, opts);
+ }
+ return bus;
+}
+
DeviceState *qdev_device_add(QemuOpts *opts)
{
const char *driver, *path, *id;
@@ -245,6 +256,8 @@ DeviceState *qdev_device_add(QemuOpts *opts)
return NULL;
}
}
+
+ bus = qbus_realize_topology(bus, info, opts);
if (qdev_hotplug && !bus->allow_hotplug) {
qerror_report(QERR_BUS_NO_HOTPLUG, bus->name);
return NULL;
diff --git a/hw/qdev.h b/hw/qdev.h
index 8a13ec9..ae18406 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -56,6 +56,8 @@ typedef char *(*bus_get_dev_path)(DeviceState *dev);
*/
typedef char *(*bus_get_fw_dev_path)(DeviceState *dev);
typedef int (qbus_resetfn)(BusState *bus);
+typedef BusState *(*bus_realize_topology)(BusState *bus, DeviceInfo *dev,
+ QemuOpts *opts);
struct BusInfo {
const char *name;
@@ -63,6 +65,7 @@ struct BusInfo {
bus_dev_printfn print_dev;
bus_get_dev_path get_dev_path;
bus_get_fw_dev_path get_fw_dev_path;
+ bus_realize_topology realize_topology;
qbus_resetfn *reset;
Property *props;
};
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 12/13] scsi: create scsi-path and scsi-target devices automatically
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (10 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 11/13] qdev: introduce automatic creation of buses Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 13/13] scsi: delete handling of REPORT LUNS and unknown LUNs outside scsi-target Paolo Bonzini
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This allows to specify a bus or target ID as the scsi-id, and
automatically creates all the missing chains down to the LUN level.
The given scsi-id is used for the first level; subsequent levels
use id 0 as it should be for backward compatibility.
Having to parse the number manually kind of sucks. :( Unfortunately
qdev properties will not be parsed until after the device has been
created. The right way to fix it is to wait for the QCFG fairies,
or to otherwise make qdev use QObject rather than QemuOpts as the
basis for its parsing. Not something I am going to do soon anyway.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-bus.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 44 insertions(+), 1 deletions(-)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 43a1cd7..277bcc0 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -7,11 +7,14 @@
#include "trace.h"
static char *scsibus_get_fw_dev_path(DeviceState *dev);
+static BusState *scsibus_realize_topology(BusState *qbus, DeviceInfo *base,
+ QemuOpts *opts);
static struct BusInfo scsi_bus_info = {
.name = "SCSI",
.size = sizeof(SCSIBus),
- .get_fw_dev_path = scsibus_get_fw_dev_path,
+ .get_fw_dev_path = scsibus_get_fw_dev_path,
+ .realize_topology = scsibus_realize_topology,
.props = (Property[]) {
DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
DEFINE_PROP_END_OF_LIST(),
@@ -32,6 +35,46 @@ void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
bus->qbus.allow_hotplug = 1;
}
+static BusState *scsibus_realize_topology(BusState *qbus, DeviceInfo *base,
+ QemuOpts *opts)
+{
+ SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base);
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, qbus);
+ const char *scsi_id_str;
+ int scsi_id;
+ DeviceState *qdev;
+ SCSIDevice *dev;
+
+ scsi_id_str = qemu_opt_get(opts, "scsi-id");
+ if (scsi_id_str) {
+ char *end;
+ scsi_id = strtoul(scsi_id_str, &end, 0);
+ if ((*end != '\0') || (end == scsi_id_str)) {
+ /* We'll fail when parsing the property. */
+ return &bus->qbus;
+ }
+ } else {
+ scsi_id = -1;
+ }
+
+ if (bus->level >= info->level - 1) {
+ return &bus->qbus;
+ }
+ if (bus->level == SCSI_PATH) {
+ qdev = qdev_create(&bus->qbus, "scsi-target");
+ } else {
+ qdev = qdev_create(&bus->qbus, "scsi-path");
+ }
+ if (scsi_id != -1) {
+ qdev_prop_set_uint32(qdev, "scsi-id", scsi_id);
+ qemu_opt_set(opts, "scsi-id", "0");
+ }
+
+ qdev_init_nofail(qdev);
+ dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+ return &dev->children->qbus;
+}
+
static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
{
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Qemu-devel] [RFC PATCH v2 13/13] scsi: delete handling of REPORT LUNS and unknown LUNs outside scsi-target
2011-06-06 16:04 [Qemu-devel] [RFC PATCH v2 00/13] support hierarchical LUNs Paolo Bonzini
` (11 preceding siblings ...)
2011-06-06 16:04 ` [Qemu-devel] [RFC PATCH v2 12/13] scsi: create scsi-path and scsi-target devices automatically Paolo Bonzini
@ 2011-06-06 16:04 ` Paolo Bonzini
12 siblings, 0 replies; 15+ messages in thread
From: Paolo Bonzini @ 2011-06-06 16:04 UTC (permalink / raw)
To: qemu-devel
This removes scsi-disk and scsi-generic's handling of requests
that should be entirely within the realm of the target.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi-disk.c | 23 +----------------------
hw/scsi-generic.c | 34 +++-------------------------------
2 files changed, 4 insertions(+), 53 deletions(-)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 0a4fbee..60e96a0 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -517,12 +517,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
buflen = SCSI_MAX_INQUIRY_LEN;
memset(outbuf, 0, buflen);
-
- if (req->lun != s->qdev.lun) {
- outbuf[0] = 0x7f; /* LUN not supported */
- return buflen;
- }
-
if (s->drive_kind == SCSI_CD) {
outbuf[0] = 5;
outbuf[1] = 0x80;
@@ -956,13 +950,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
}
DPRINTF("Unsupported Service Action In\n");
goto illegal_request;
- case REPORT_LUNS:
- if (req->cmd.xfer < 16)
- goto illegal_request;
- memset(outbuf, 0, 16);
- outbuf[3] = 8;
- buflen = 16;
- break;
case VERIFY:
break;
case REZERO_UNIT:
@@ -1024,15 +1011,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
}
#endif
- if (req->lun != s->qdev.lun) {
- /* Only one LUN supported. */
- 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;
- }
- }
+ assert(req->lun == s->qdev.lun);
switch (command) {
case TEST_UNIT_READY:
case REQUEST_SENSE:
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index e1f9d0e..5912a3d 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -231,11 +231,9 @@ static void scsi_read_data(SCSIRequest *req)
return;
}
- switch (r->req.cmd.buf[0]) {
- case REQUEST_SENSE:
- if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) {
- break;
- }
+ assert(req->lun == s->qdev.lun);
+ if (r->req.cmd.buf[0] == REQUEST_SENSE &&
+ !(s->driver_status & SG_ERR_DRIVER_SENSE)) {
s->senselen = MIN(r->len, s->senselen);
memcpy(r->buf, s->sensebuf, s->senselen);
r->io_header.driver_status = 0;
@@ -250,32 +248,6 @@ static void scsi_read_data(SCSIRequest *req)
/* Clear sensebuf after REQUEST_SENSE */
scsi_clear_sense(s);
return;
-
- case REPORT_LUNS:
- assert(!s->qdev.lun);
- if (r->req.cmd.xfer < 16) {
- scsi_command_complete(r, -EINVAL);
- return;
- }
- r->io_header.driver_status = 0;
- r->io_header.status = 0;
- r->io_header.dxfer_len = 16;
- r->len = -1;
- r->buf[3] = 8;
- scsi_req_data(&r->req, 16);
- scsi_command_complete(r, 0);
- return;
-
- case INQUIRY:
- if (req->lun != s->qdev.lun) {
- if (r->req.cmd.xfer < 1) {
- scsi_command_complete(r, -EINVAL);
- return;
- }
- r->buf[0] = 0x7f;
- return;
- }
- break;
}
ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
--
1.7.4.4
^ permalink raw reply related [flat|nested] 15+ messages in thread