[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 4/8] scsi-disk: Implement 'SET TARGET PORT GROUPS'
From: |
Hannes Reinecke |
Subject: |
[Qemu-devel] [PATCH 4/8] scsi-disk: Implement 'SET TARGET PORT GROUPS' |
Date: |
Fri, 27 Nov 2015 15:59:02 +0100 |
Implement 'SET TARGET PORT GROUPS' handling. The ports states are
switched as indicated in the command; no strategy is implemented.
This might cause issues with standard Linux behaviour, which will
only switch the passive path to 'active' and leave the former
active path alone.
Signed-off-by: Hannes Reinecke <address@hidden>
---
hw/scsi/scsi-disk.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++
include/block/scsi.h | 5 +++
2 files changed, 123 insertions(+)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 8dabed3..52c73be 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1915,6 +1915,111 @@ static void qbus_enumerate_port_desc(PortDescEnumerate
*pd, BusState *bus)
}
}
+typedef struct PortGroupSetEnumerate {
+ uint64_t wwn;
+ uint16_t port_group;
+ uint8_t alua_state;
+} PortGroupSetEnumerate;
+
+static void qbus_enumerate_set_port(PortGroupSetEnumerate *, BusState *);
+
+static void qdev_enumerate_set_port(PortGroupSetEnumerate *ps, DeviceState
*dev)
+{
+ BusState *child;
+
+ if (!strcmp(object_get_typename(OBJECT(dev->parent_bus)), TYPE_SCSI_BUS)) {
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
+ if (s->wwn == ps->wwn &&
+ s->port_group == ps->port_group) {
+ printf("pg %x: switch ALUA state %x -> %x\n",
+ s->port_group, (s->alua_state & 0x0f), ps->alua_state);
+ s->alua_state = (s->alua_state & 0xf0) | ps->alua_state;
+ scsi_device_set_ua(&s->qdev,
+ SENSE_CODE(ASYMMETRIC_ACCESS_STATE_CHANGED));
+ }
+ }
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ qbus_enumerate_set_port(ps, child);
+ }
+}
+
+static void qbus_enumerate_set_port(PortGroupSetEnumerate *ps, BusState *bus)
+{
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ qdev_enumerate_set_port(ps, dev);
+ }
+}
+
+static void scsi_emulate_set_target_port_groups(SCSIDiskReq *r, uint8_t *inbuf)
+{
+ SCSIRequest *req = &r->req;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ uint32_t buflen = scsi_data_cdb_xfer(r->req.cmd.buf);
+ uint8_t *p = inbuf;
+ PortGroupEnumerate pg;
+ PortGroupSetEnumerate ps;
+ int i, pg_found = 0;
+
+ if (!s->wwn) {
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+ }
+
+ pg.numgrp = 0;
+ pg.wwn = s->wwn;
+ qbus_enumerate_port_group(&pg, sysbus_get_default());
+
+ p = &inbuf[4];
+ /* Validate input before continuing */
+ while (p < inbuf + buflen) {
+ uint16_t port_group;
+ uint8_t alua_state;
+
+ port_group = ((uint16_t)p[2] << 8) + p[3];
+ alua_state = p[0] & 0x7;
+
+ for (i = 0; i < pg.numgrp; i++) {
+ if ((port_group == pg.grp[i]) &&
+ (alua_state == (pg.alua_state[i] & 0x0f))) {
+ printf("pg %x: port already in state %d\n",
+ pg.grp[i], (pg.alua_state[i] & 0x0f));
+ pg_found++;
+ }
+ }
+ p += 4;
+ }
+ if (pg_found == pg.numgrp) {
+ printf("all ports in requested state\n");
+ scsi_req_complete(&r->req, GOOD);
+ return;
+ }
+
+ p = &inbuf[4];
+ while (p < inbuf + buflen) {
+ uint16_t port_group;
+ uint8_t alua_state;
+
+ port_group = ((uint16_t)p[2] << 8) + p[3];
+ alua_state = p[0] & 0x7;
+
+ if (port_group == s->port_group) {
+ printf("pg %x: explicit switch current ALUA state "
+ "%x -> %x\n",
+ port_group, (s->alua_state & 0x0f), alua_state);
+ s->alua_state = (s->alua_state & 0xf0) | alua_state;
+ } else {
+ ps.wwn = s->wwn;
+ ps.port_group = port_group;
+ ps.alua_state = alua_state;
+ qbus_enumerate_set_port(&ps, sysbus_get_default());
+ }
+ p += 4;
+ }
+ scsi_req_complete(&r->req, GOOD);
+}
+
static void scsi_disk_emulate_write_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
@@ -1951,6 +2056,16 @@ static void scsi_disk_emulate_write_data(SCSIRequest
*req)
scsi_disk_emulate_write_same(r, r->iov.iov_base);
break;
+ case MAINTENANCE_OUT:
+ if ((req->cmd.buf[1] & 31) == MO_SET_TARGET_PORT_GROUPS) {
+ DPRINTF("MO SET TARGET PORT GROUPS\n");
+ scsi_emulate_set_target_port_groups(r, r->iov.iov_base);
+ break;
+ }
+ DPRINTF("Unsupported Maintenance Out\n");
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+ break;
+
default:
abort();
}
@@ -2215,6 +2330,9 @@ static int32_t scsi_disk_emulate_command(SCSIRequest
*req, uint8_t *buf)
DPRINTF("Unsupported Maintenance In\n");
goto illegal_request;
break;
+ case MAINTENANCE_OUT:
+ DPRINTF("Maintenance Out (len %lu)\n", (long)r->req.cmd.xfer);
+ break;
case MECHANISM_STATUS:
buflen = scsi_emulate_mechanism_status(s, outbuf);
if (buflen < 0) {
diff --git a/include/block/scsi.h b/include/block/scsi.h
index a9d0f64..47a25b8 100644
--- a/include/block/scsi.h
+++ b/include/block/scsi.h
@@ -156,6 +156,11 @@ const char *scsi_command_name(uint8_t cmd);
#define MI_REPORT_TARGET_PORT_GROUPS 0xa
/*
+ * MAINTENANCE OUT subcodes
+ */
+#define MO_SET_TARGET_PORT_GROUPS 0xa
+
+/*
* READ POSITION service action codes
*/
#define SHORT_FORM_BLOCK_ID 0x00
--
1.8.4.5
- [Qemu-devel] [PATCH RFC 0/8] scsi-disk: Active/passive ALUA support, Hannes Reinecke, 2015/11/27
- [Qemu-devel] [PATCH 5/8] scsi-disk: implement ALUA policy, Hannes Reinecke, 2015/11/27
- [Qemu-devel] [PATCH 4/8] scsi-disk: Implement 'SET TARGET PORT GROUPS',
Hannes Reinecke <=
- [Qemu-devel] [PATCH 8/8] block: Implement 'block_disconnect' HMP command, Hannes Reinecke, 2015/11/27
- [Qemu-devel] [PATCH 3/8] scsi-disk: Implement 'REPORT TARGET PORT GROUPS', Hannes Reinecke, 2015/11/27
- [Qemu-devel] [PATCH 1/8] scsi-disk: Add 'port_group' property, Hannes Reinecke, 2015/11/27
- [Qemu-devel] [PATCH 6/8] scsi-disk: Allow READ CAPACITY in standby, Hannes Reinecke, 2015/11/27
- [Qemu-devel] [PATCH 7/8] scsi-disk: Implement 'alua_preferred' option, Hannes Reinecke, 2015/11/27
- [Qemu-devel] [PATCH 2/8] scsi-disk: Add 'alua_state' property, Hannes Reinecke, 2015/11/27