@@ -3837,6 +3837,21 @@ int bdrv_get_shared(BlockDriverState *bs)
return bs->shared_no;
}
+void bdrv_shared_mask(BlockDriverState *bs, unsigned long *shared_mask)
+{
+ BlockDriverState *tmp_bs;
+
+ if (!bs->filename || !shared_mask)
+ return;
+ QTAILQ_FOREACH(tmp_bs, &graph_bdrv_states, node_list) {
+ if (!strcmp(bs->filename, tmp_bs->filename)) {
+ if (tmp_bs->shared_no > 0) {
+ set_bit(tmp_bs->shared_no - 1, shared_mask);
+ }
+ }
+ }
+}
+
/* Put this QMP function here so it can access the static graph_bdrv_states. */
BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp)
{
@@ -676,6 +676,13 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
buflen += 8;
}
+ if (bdrv_get_shared(blk_bs(s->qdev.conf.blk))) {
+ outbuf[buflen++] = 0x61; // SAS / Binary
+ outbuf[buflen++] = 0x95; // PIV / Target port / target port group
+ outbuf[buflen++] = 0; // reserved
+ outbuf[buflen++] = 4;
+ buflen += 4;
+ }
if (s->port_index) {
outbuf[buflen++] = 0x61; // SAS / Binary
outbuf[buflen++] = 0x94; // PIV / Target port / relative target port
@@ -819,6 +826,11 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
outbuf[4] = 36 - 5;
}
+ /* Enable TGPS bit */
+ if (bdrv_get_shared(blk_bs(s->qdev.conf.blk))) {
+ outbuf[5] = 0x10;
+ }
+
/* Sync data transfer and TCQ. */
outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0);
return buflen;
@@ -1869,6 +1881,47 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req)
}
}
+static int scsi_emulate_report_target_port_groups(SCSIDiskState *s,
+ uint8_t *inbuf)
+{
+ uint8_t *p, *pg;
+ int buflen = 0, i, count = 0;
+ unsigned long shared_mask = 0;
+
+ if (!bdrv_get_shared(blk_bs(s->qdev.conf.blk))) {
+ return -1;
+ }
+
+ bdrv_shared_mask(blk_bs(s->qdev.conf.blk), &shared_mask);
+ if (!shared_mask) {
+ return -1;
+ }
+
+ pg = &inbuf[4];
+ pg[0] = 0; /* Active/Optimized */
+ pg[1] = 0x1; /* Only Active/Optimized is supported */
+
+ p = &pg[8];
+ buflen += 8;
+ for (i = 0; i < 32; i++) {
+ if (!test_bit(i, &shared_mask))
+ continue;
+ p[2] = (i + 1) >> 8;
+ p[3] = (i + 1) & 0xFF;
+ p += 4;
+ buflen += 4;
+ count++;
+ }
+ pg[7] = count;
+
+ inbuf[0] = (buflen >> 24) & 0xff;
+ inbuf[1] = (buflen >> 16) & 0xff;
+ inbuf[2] = (buflen >> 8) & 0xff;
+ inbuf[3] = buflen & 0xff;
+
+ return buflen + 4;
+}
+
static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
@@ -2010,6 +2063,19 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
goto illegal_request;
}
break;
+ case MAINTENANCE_IN:
+ if ((req->cmd.buf[1] & 31) == MI_REPORT_TARGET_PORT_GROUPS) {
+ DPRINTF("MI REPORT TARGET PORT GROUPS\n");
+ memset(outbuf, 0, req->cmd.xfer);
+ buflen = scsi_emulate_report_target_port_groups(s, outbuf);
+ if (buflen < 0) {
+ goto illegal_request;
+ }
+ break;
+ }
+ DPRINTF("Unsupported Maintenance In\n");
+ goto illegal_request;
+ break;
case MECHANISM_STATUS:
buflen = scsi_emulate_mechanism_status(s, outbuf);
if (buflen < 0) {
@@ -445,6 +445,7 @@ const char *bdrv_get_format_name(BlockDriverState *bs);
BlockDriverState *bdrv_find_node(const char *node_name);
void bdrv_find_shared(BlockDriverState *bs);
int bdrv_get_shared(BlockDriverState *bs);
+void bdrv_shared_mask(BlockDriverState *bs, unsigned long *shared_mask);
BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp);
BlockDriverState *bdrv_lookup_bs(const char *device,
const char *node_name,
@@ -156,6 +156,11 @@
#define SAI_READ_CAPACITY_16 0x10
/*
+ * MAINTENANCE IN subcodes
+ */
+#define MI_REPORT_TARGET_PORT_GROUPS 0xa
+
+/*
* READ POSITION service action codes
*/
#define SHORT_FORM_BLOCK_ID 0x00
Implement simple multipath support based on the shared block device feature. Whenever a shared device is detected the scsi-disk driver will report a simple ALUA setup with all paths in active/optimized. Signed-off-by: Hannes Reinecke <hare@suse.com> --- block.c | 15 +++++++++++ hw/scsi/scsi-disk.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ include/block/block.h | 1 + include/scsi/constants.h | 5 ++++ 4 files changed, 87 insertions(+)