From patchwork Fri Nov 27 14:59:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hannes Reinecke X-Patchwork-Id: 549487 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 2B82A14031D for ; Sat, 28 Nov 2015 01:59:57 +1100 (AEDT) Received: from localhost ([::1]:57133 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a2KV9-0000ev-45 for incoming@patchwork.ozlabs.org; Fri, 27 Nov 2015 09:59:55 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:34116) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a2KUT-0007ky-Jv for qemu-devel@nongnu.org; Fri, 27 Nov 2015 09:59:15 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1a2KUP-0001rP-KJ for qemu-devel@nongnu.org; Fri, 27 Nov 2015 09:59:13 -0500 Received: from mx2.suse.de ([195.135.220.15]:48995) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a2KUP-0001qw-Ag for qemu-devel@nongnu.org; Fri, 27 Nov 2015 09:59:09 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 4A04AACA5; Fri, 27 Nov 2015 14:57:28 +0000 (UTC) From: Hannes Reinecke To: Paolo Bonzini Date: Fri, 27 Nov 2015 15:59:00 +0100 Message-Id: <1448636346-24641-3-git-send-email-hare@suse.de> X-Mailer: git-send-email 1.8.4.5 In-Reply-To: <1448636346-24641-1-git-send-email-hare@suse.de> References: <1448636346-24641-1-git-send-email-hare@suse.de> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] X-Received-From: 195.135.220.15 Cc: Johannes Thumshirn , Stefan Hajnoczi , Hannes Reinecke , qemu-devel@nongnu.org, Alexander Graf Subject: [Qemu-devel] [PATCH 2/8] scsi-disk: Add 'alua_state' property X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org To support asymmetric logical unit access (ALUA) we need to store the ALUA state in the device structure. Signed-off-by: Hannes Reinecke --- hw/scsi/scsi-bus.c | 20 +++++++++ hw/scsi/scsi-disk.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ include/block/scsi.h | 13 ++++++ include/hw/scsi/scsi.h | 8 ++++ 4 files changed, 158 insertions(+) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index fd1171e..56a4d33 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1294,6 +1294,21 @@ const struct SCSISense sense_code_LUN_NOT_READY = { .key = NOT_READY, .asc = 0x04, .ascq = 0x03 }; +/* LUN not ready, asymmetric access state transition */ +const struct SCSISense sense_code_STATE_TRANSITION = { + .key = NOT_READY, .asc = 0x04, .ascq = 0x0a +}; + +/* LUN not ready, target port in standby state */ +const struct SCSISense sense_code_STATE_STANDBY = { + .key = NOT_READY, .asc = 0x04, .ascq = 0x0b +}; + +/* LUN not ready, target port in unavailable state */ +const struct SCSISense sense_code_STATE_UNAVAILABLE = { + .key = NOT_READY, .asc = 0x04, .ascq = 0x0c +}; + /* LUN not ready, Medium not present */ const struct SCSISense sense_code_NO_MEDIUM = { .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 @@ -1409,6 +1424,11 @@ const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 }; +/* Unit attention, Asymmetric Access State changed */ +const struct SCSISense sense_code_ASYMMETRIC_ACCESS_STATE_CHANGED = { + .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x06 +}; + /* Data Protection, Write Protected */ const struct SCSISense sense_code_WRITE_PROTECTED = { .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index f544f43..583cacd 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -33,6 +33,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "hw/scsi/scsi.h" #include "block/scsi.h" #include "sysemu/sysemu.h" +#include "qapi/visitor.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/block/block.h" @@ -80,6 +81,7 @@ struct SCSIDiskState uint64_t port_wwn; uint16_t port_index; uint16_t port_group; + uint8_t alua_state; uint64_t max_unmap_size; uint64_t max_io_size; QEMUBH *bh; @@ -1890,6 +1892,63 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) break; } + if ((s->alua_state & 0x0f) != ALUA_STATE_ACTIVE_OPTIMIZED && + (s->alua_state & 0x0f) != ALUA_STATE_ACTIVE_NON_OPTIMIZED) { + bool standby_allowed = true; + bool unavailable_allowed = true; + bool transition_allowed = true; + + switch(req->cmd.buf[0]) { + case MAINTENANCE_IN: + if ((req->cmd.buf[1] & 31) != MI_REPORT_TARGET_PORT_GROUPS) { + transition_allowed = false; + unavailable_allowed = false; + standby_allowed = false; + } + /* Fallthrough */ + case INQUIRY: + case REPORT_LUNS: + case REQUEST_SENSE: + break; + case MAINTENANCE_OUT: + transition_allowed = false; + break; + case PERSISTENT_RESERVE_IN: + case PERSISTENT_RESERVE_OUT: + case LOG_SENSE: + case LOG_SELECT: + case MODE_SENSE: + case MODE_SENSE_10: + case MODE_SELECT: + case MODE_SELECT_10: + case RECEIVE_DIAGNOSTIC: + case SEND_DIAGNOSTIC: + transition_allowed = false; + unavailable_allowed = false; + break; + default: + transition_allowed = false; + unavailable_allowed = false; + standby_allowed = false; + break; + } + if ((s->alua_state & 0x0f) == ALUA_STATE_STANDBY && + !standby_allowed) { + scsi_check_condition(r, SENSE_CODE(STATE_STANDBY)); + return 0; + } + if ((s->alua_state & 0x0f) == ALUA_STATE_UNAVAILABLE && + !unavailable_allowed) { + scsi_check_condition(r, SENSE_CODE(STATE_UNAVAILABLE)); + return 0; + } + if ((s->alua_state & 0x0f) == ALUA_STATE_TRANSITION && + !transition_allowed) { + scsi_check_condition(r, SENSE_CODE(STATE_TRANSITION)); + return 0; + } + } + /* * FIXME: we shouldn't return anything bigger than 4k, but the code * requires the buffer to be as big as req->cmd.xfer in several @@ -2156,6 +2215,21 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) return 0; } + switch ((s->alua_state & 0x0f)) { + case ALUA_STATE_ACTIVE_OPTIMIZED: + case ALUA_STATE_ACTIVE_NON_OPTIMIZED: + break; + case ALUA_STATE_STANDBY: + scsi_check_condition(r, SENSE_CODE(STATE_STANDBY)); + return 0; + case ALUA_STATE_UNAVAILABLE: + scsi_check_condition(r, SENSE_CODE(STATE_UNAVAILABLE)); + return 0; + case ALUA_STATE_TRANSITION: + scsi_check_condition(r, SENSE_CODE(STATE_TRANSITION)); + return 0; + } + len = scsi_data_cdb_xfer(r->req.cmd.buf); switch (command) { case READ_6: @@ -2819,11 +2893,54 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_scsi_disk_state; } +static void scsi_disk_get_alua_state(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + SCSIDiskState *s = OBJECT_CHECK(SCSIDiskState, obj, "scsi-disk"); + uint8_t alua_state = s->alua_state & 0x0f; + + visit_type_uint8(v, &alua_state, name, errp); +} + +static void scsi_disk_set_alua_state(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + SCSIDiskState *s = OBJECT_CHECK(SCSIDiskState, obj, "scsi-disk"); + uint8_t alua_state; + Error *local_err = NULL; + + visit_type_uint8(v, &alua_state, name, &local_err); + if (local_err) { + goto out; + } + if (alua_state > 3) { + error_setg(&local_err, "Invalid ALUA state %d\n", alua_state); + goto out; + } + + s->alua_state = alua_state; + scsi_device_set_ua(&s->qdev, SENSE_CODE(ASYMMETRIC_ACCESS_STATE_CHANGED)); + +out: + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void scsi_disk_instance_initfn(Object *obj) +{ + object_property_add(obj, "alua_state", "uint8", + scsi_disk_get_alua_state, + scsi_disk_set_alua_state, NULL, NULL, NULL); + object_property_set_int(obj, ALUA_STATE_ACTIVE_OPTIMIZED, "alua_state", NULL); +} + static const TypeInfo scsi_disk_info = { .name = "scsi-disk", .parent = TYPE_SCSI_DEVICE, .instance_size = sizeof(SCSIDiskState), .class_init = scsi_disk_class_initfn, + .instance_init = scsi_disk_instance_initfn, }; static void scsi_disk_register_types(void) diff --git a/include/block/scsi.h b/include/block/scsi.h index a311341..a9d0f64 100644 --- a/include/block/scsi.h +++ b/include/block/scsi.h @@ -151,6 +151,11 @@ const char *scsi_command_name(uint8_t cmd); #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 @@ -306,4 +311,12 @@ const char *scsi_command_name(uint8_t cmd); #define MMC_PROFILE_HDDVD_RW_DL 0x005A #define MMC_PROFILE_INVALID 0xFFFF +#define ALUA_STATE_ACTIVE_OPTIMIZED 0x0 +#define ALUA_STATE_ACTIVE_NON_OPTIMIZED 0x1 +#define ALUA_STATE_STANDBY 0x2 +#define ALUA_STATE_UNAVAILABLE 0x3 +#define ALUA_STATE_LBA_DEPENDENT 0x4 +#define ALUA_STATE_OFFLINE 0xE +#define ALUA_STATE_TRANSITION 0xF + #endif diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index cdaf0f8..76f3e49 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -185,6 +185,12 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp); extern const struct SCSISense sense_code_NO_SENSE; /* LUN not ready, Manual intervention required */ extern const struct SCSISense sense_code_LUN_NOT_READY; +/* LUN not ready, asymmetric access state transition */ +extern const struct SCSISense sense_code_STATE_TRANSITION; +/* LUN not ready, Target Port in standby state */ +extern const struct SCSISense sense_code_STATE_STANDBY; +/* LUN not ready, Target Port in unavailable state */ +extern const struct SCSISense sense_code_STATE_UNAVAILABLE; /* LUN not ready, Medium not present */ extern const struct SCSISense sense_code_NO_MEDIUM; /* LUN not ready, medium removal prevented */ @@ -231,6 +237,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; +/* Unit attention, Asymmetric Access State changed */ +extern const struct SCSISense sense_code_ASYMMETRIC_ACCESS_STATE_CHANGED; /* Data Protection, Write Protected */ extern const struct SCSISense sense_code_WRITE_PROTECTED; /* Data Protection, Space Allocation Failed Write Protect */