From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 150340806146454.07821163188248; Tue, 22 Aug 2017 06:21:01 -0700 (PDT) Received: from localhost ([::1]:45933 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk976-0004Ip-2t for importer@patchew.org; Tue, 22 Aug 2017 09:21:00 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49677) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk950-0002dy-Pm for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:51 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk94u-0005m5-SJ for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:50 -0400 Received: from mx1.redhat.com ([209.132.183.28]:53082) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk94u-0005dc-Hz for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:44 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CBC5F6146B; Tue, 22 Aug 2017 13:18:35 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id AAB3F81B5A; Tue, 22 Aug 2017 13:18:34 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com CBC5F6146B Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:23 +0200 Message-Id: <20170822131832.20191-2-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Tue, 22 Aug 2017 13:18:35 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 01/10] scsi: rename scsi_convert_sense X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" After introducing the scsi/ subdirectory, there will be a scsi_build_sense function that is the same as scsi_req_build_sense but without needing a SCSIRequest. The existing scsi_build_sense function gets in the way, remove it. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daud=C3=A9 --- hw/scsi/scsi-bus.c | 10 +++++----- hw/scsi/scsi-disk.c | 4 ++-- include/hw/scsi/scsi.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index e364410a23..890f8fcc83 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -769,7 +769,7 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, = int len) return 0; } =20 - ret =3D scsi_build_sense(req->sense, req->sense_len, buf, len, true); + ret =3D scsi_convert_sense(req->sense, req->sense_len, buf, len, true); =20 /* * FIXME: clearing unit attention conditions upon autosense should be = done @@ -790,7 +790,7 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, = int len) =20 int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fix= ed) { - return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed); + return scsi_convert_sense(dev->sense, dev->sense_len, buf, len, fixed); } =20 void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) @@ -1510,12 +1510,12 @@ const struct SCSISense sense_code_SPACE_ALLOC_FAILE= D =3D { }; =20 /* - * scsi_build_sense + * scsi_convert_sense * * Convert between fixed and descriptor sense buffers */ -int scsi_build_sense(uint8_t *in_buf, int in_len, - uint8_t *buf, int len, bool fixed) +int scsi_convert_sense(uint8_t *in_buf, int in_len, + uint8_t *buf, int len, bool fixed) { bool fixed_in; SCSISense sense; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 5f1e5e8070..0a1f4ef0c7 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1978,8 +1978,8 @@ static int32_t scsi_disk_emulate_command(SCSIRequest = *req, uint8_t *buf) break; case REQUEST_SENSE: /* Just return "NO SENSE". */ - buflen =3D scsi_build_sense(NULL, 0, outbuf, r->buflen, - (req->cmd.buf[1] & 1) =3D=3D 0); + buflen =3D scsi_convert_sense(NULL, 0, outbuf, r->buflen, + (req->cmd.buf[1] & 1) =3D=3D 0); if (buflen < 0) { goto illegal_request; } diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 6b85786dbf..6ef67fb504 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -244,8 +244,8 @@ extern const struct SCSISense sense_code_SPACE_ALLOC_FA= ILED; uint32_t scsi_data_cdb_xfer(uint8_t *buf); uint32_t scsi_cdb_xfer(uint8_t *buf); int scsi_cdb_length(uint8_t *buf); -int scsi_build_sense(uint8_t *in_buf, int in_len, - uint8_t *buf, int len, bool fixed); +int scsi_convert_sense(uint8_t *in_buf, int in_len, + uint8_t *buf, int len, bool fixed); =20 SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, uint32_t lun, void *hba_private); --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1503408234141694.3782474046961; Tue, 22 Aug 2017 06:23:54 -0700 (PDT) Received: from localhost ([::1]:45959 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk99s-0007cP-Ix for importer@patchew.org; Tue, 22 Aug 2017 09:23:52 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49646) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk94y-0002dE-V6 for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk94v-0005mR-2G for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:48 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34268) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk94u-0005fz-I9 for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:44 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EEB67806C3; Tue, 22 Aug 2017 13:18:37 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2B37F81B49; Tue, 22 Aug 2017 13:18:35 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EEB67806C3 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:24 +0200 Message-Id: <20170822131832.20191-3-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Tue, 22 Aug 2017 13:18:38 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 02/10] scsi: move non-emulation specific code to scsi/ X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" There is a bunch of SCSI code that is shared by block/iscsi.c and hw/scsi, and the introduction of the persistent reservation helper will add more instances of this. There is also include/block/scsi.h, which actually is not part of the core block layer. Create a directory for this kind of shared code. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daud=C3=A9 --- MAINTAINERS | 7 + Makefile.objs | 2 +- hw/scsi/scsi-bus.c | 397 ---------------------------------------------= --- hw/scsi/scsi-generic.c | 8 - include/block/scsi.h | 2 - include/hw/scsi/scsi.h | 94 +----------- include/scsi/utils.h | 116 ++++++++++++++ scsi/Makefile.objs | 1 + scsi/utils.c | 403 +++++++++++++++++++++++++++++++++++++++++++++= ++++ 9 files changed, 529 insertions(+), 501 deletions(-) create mode 100644 include/scsi/utils.h create mode 100644 scsi/Makefile.objs create mode 100644 scsi/utils.c diff --git a/MAINTAINERS b/MAINTAINERS index ccee28b12d..fa6e21cd38 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1213,6 +1213,13 @@ F: migration/block* F: include/block/aio.h T: git git://github.com/stefanha/qemu.git block =20 +Block SCSI subsystem +M: Paolo Bonzini +L: qemu-block@nongnu.org +S: Supported +F: include/scsi/* +F: scsi/* + Block Jobs M: Jeff Cody L: qemu-block@nongnu.org diff --git a/Makefile.objs b/Makefile.objs index 24a4ea08b8..f68aa3b60d 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -11,7 +11,7 @@ chardev-obj-y =3D chardev/ =20 block-obj-y +=3D nbd/ block-obj-y +=3D block.o blockjob.o -block-obj-y +=3D block/ +block-obj-y +=3D block/ scsi/ block-obj-y +=3D qemu-io-cmds.o block-obj-$(CONFIG_REPLICATION) +=3D replication.o =20 diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 890f8fcc83..300912d213 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -935,36 +935,6 @@ static int ata_passthrough_16_xfer(SCSIDevice *dev, ui= nt8_t *buf) return xfer * unit; } =20 -uint32_t scsi_data_cdb_xfer(uint8_t *buf) -{ - if ((buf[0] >> 5) =3D=3D 0 && buf[4] =3D=3D 0) { - return 256; - } else { - return scsi_cdb_xfer(buf); - } -} - -uint32_t scsi_cdb_xfer(uint8_t *buf) -{ - switch (buf[0] >> 5) { - case 0: - return buf[4]; - break; - case 1: - case 2: - return lduw_be_p(&buf[7]); - break; - case 4: - return ldl_be_p(&buf[10]) & 0xffffffffULL; - break; - case 5: - return ldl_be_p(&buf[6]) & 0xffffffffULL; - break; - default: - return -1; - } -} - static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) { cmd->xfer =3D scsi_cdb_xfer(buf); @@ -1277,53 +1247,6 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) } } =20 -static uint64_t scsi_cmd_lba(SCSICommand *cmd) -{ - uint8_t *buf =3D cmd->buf; - uint64_t lba; - - switch (buf[0] >> 5) { - case 0: - lba =3D ldl_be_p(&buf[0]) & 0x1fffff; - break; - case 1: - case 2: - case 5: - lba =3D ldl_be_p(&buf[2]) & 0xffffffffULL; - break; - case 4: - lba =3D ldq_be_p(&buf[2]); - break; - default: - lba =3D -1; - - } - return lba; -} - -int scsi_cdb_length(uint8_t *buf) { - int cdb_len; - - switch (buf[0] >> 5) { - case 0: - cdb_len =3D 6; - break; - case 1: - case 2: - cdb_len =3D 10; - break; - case 4: - cdb_len =3D 16; - break; - case 5: - cdb_len =3D 12; - break; - default: - cdb_len =3D -1; - } - return cdb_len; -} - int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) { int rc; @@ -1370,326 +1293,6 @@ void scsi_device_report_change(SCSIDevice *dev, SCS= ISense sense) } } =20 -/* - * Predefined sense codes - */ - -/* No sense data available */ -const struct SCSISense sense_code_NO_SENSE =3D { - .key =3D NO_SENSE , .asc =3D 0x00 , .ascq =3D 0x00 -}; - -/* LUN not ready, Manual intervention required */ -const struct SCSISense sense_code_LUN_NOT_READY =3D { - .key =3D NOT_READY, .asc =3D 0x04, .ascq =3D 0x03 -}; - -/* LUN not ready, Medium not present */ -const struct SCSISense sense_code_NO_MEDIUM =3D { - .key =3D NOT_READY, .asc =3D 0x3a, .ascq =3D 0x00 -}; - -/* LUN not ready, medium removal prevented */ -const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED =3D { - .key =3D NOT_READY, .asc =3D 0x53, .ascq =3D 0x02 -}; - -/* Hardware error, internal target failure */ -const struct SCSISense sense_code_TARGET_FAILURE =3D { - .key =3D HARDWARE_ERROR, .asc =3D 0x44, .ascq =3D 0x00 -}; - -/* Illegal request, invalid command operation code */ -const struct SCSISense sense_code_INVALID_OPCODE =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x20, .ascq =3D 0x00 -}; - -/* Illegal request, LBA out of range */ -const struct SCSISense sense_code_LBA_OUT_OF_RANGE =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x21, .ascq =3D 0x00 -}; - -/* Illegal request, Invalid field in CDB */ -const struct SCSISense sense_code_INVALID_FIELD =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x24, .ascq =3D 0x00 -}; - -/* Illegal request, Invalid field in parameter list */ -const struct SCSISense sense_code_INVALID_PARAM =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x26, .ascq =3D 0x00 -}; - -/* Illegal request, Parameter list length error */ -const struct SCSISense sense_code_INVALID_PARAM_LEN =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x1a, .ascq =3D 0x00 -}; - -/* Illegal request, LUN not supported */ -const struct SCSISense sense_code_LUN_NOT_SUPPORTED =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x25, .ascq =3D 0x00 -}; - -/* Illegal request, Saving parameters not supported */ -const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x39, .ascq =3D 0x00 -}; - -/* Illegal request, Incompatible medium installed */ -const struct SCSISense sense_code_INCOMPATIBLE_FORMAT =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x30, .ascq =3D 0x00 -}; - -/* Illegal request, medium removal prevented */ -const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x53, .ascq =3D 0x02 -}; - -/* Illegal request, Invalid Transfer Tag */ -const struct SCSISense sense_code_INVALID_TAG =3D { - .key =3D ILLEGAL_REQUEST, .asc =3D 0x4b, .ascq =3D 0x01 -}; - -/* Command aborted, I/O process terminated */ -const struct SCSISense sense_code_IO_ERROR =3D { - .key =3D ABORTED_COMMAND, .asc =3D 0x00, .ascq =3D 0x06 -}; - -/* Command aborted, I_T Nexus loss occurred */ -const struct SCSISense sense_code_I_T_NEXUS_LOSS =3D { - .key =3D ABORTED_COMMAND, .asc =3D 0x29, .ascq =3D 0x07 -}; - -/* Command aborted, Logical Unit failure */ -const struct SCSISense sense_code_LUN_FAILURE =3D { - .key =3D ABORTED_COMMAND, .asc =3D 0x3e, .ascq =3D 0x01 -}; - -/* Command aborted, Overlapped Commands Attempted */ -const struct SCSISense sense_code_OVERLAPPED_COMMANDS =3D { - .key =3D ABORTED_COMMAND, .asc =3D 0x4e, .ascq =3D 0x00 -}; - -/* Unit attention, Capacity data has changed */ -const struct SCSISense sense_code_CAPACITY_CHANGED =3D { - .key =3D UNIT_ATTENTION, .asc =3D 0x2a, .ascq =3D 0x09 -}; - -/* Unit attention, Power on, reset or bus device reset occurred */ -const struct SCSISense sense_code_RESET =3D { - .key =3D UNIT_ATTENTION, .asc =3D 0x29, .ascq =3D 0x00 -}; - -/* Unit attention, No medium */ -const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM =3D { - .key =3D UNIT_ATTENTION, .asc =3D 0x3a, .ascq =3D 0x00 -}; - -/* Unit attention, Medium may have changed */ -const struct SCSISense sense_code_MEDIUM_CHANGED =3D { - .key =3D UNIT_ATTENTION, .asc =3D 0x28, .ascq =3D 0x00 -}; - -/* Unit attention, Reported LUNs data has changed */ -const struct SCSISense sense_code_REPORTED_LUNS_CHANGED =3D { - .key =3D UNIT_ATTENTION, .asc =3D 0x3f, .ascq =3D 0x0e -}; - -/* Unit attention, Device internal reset */ -const struct SCSISense sense_code_DEVICE_INTERNAL_RESET =3D { - .key =3D UNIT_ATTENTION, .asc =3D 0x29, .ascq =3D 0x04 -}; - -/* Data Protection, Write Protected */ -const struct SCSISense sense_code_WRITE_PROTECTED =3D { - .key =3D DATA_PROTECT, .asc =3D 0x27, .ascq =3D 0x00 -}; - -/* Data Protection, Space Allocation Failed Write Protect */ -const struct SCSISense sense_code_SPACE_ALLOC_FAILED =3D { - .key =3D DATA_PROTECT, .asc =3D 0x27, .ascq =3D 0x07 -}; - -/* - * scsi_convert_sense - * - * Convert between fixed and descriptor sense buffers - */ -int scsi_convert_sense(uint8_t *in_buf, int in_len, - uint8_t *buf, int len, bool fixed) -{ - bool fixed_in; - SCSISense sense; - if (!fixed && len < 8) { - return 0; - } - - if (in_len =3D=3D 0) { - sense.key =3D NO_SENSE; - sense.asc =3D 0; - sense.ascq =3D 0; - } else { - fixed_in =3D (in_buf[0] & 2) =3D=3D 0; - - if (fixed =3D=3D fixed_in) { - memcpy(buf, in_buf, MIN(len, in_len)); - return MIN(len, in_len); - } - - if (fixed_in) { - sense.key =3D in_buf[2]; - sense.asc =3D in_buf[12]; - sense.ascq =3D in_buf[13]; - } else { - sense.key =3D in_buf[1]; - sense.asc =3D in_buf[2]; - sense.ascq =3D in_buf[3]; - } - } - - memset(buf, 0, len); - if (fixed) { - /* Return fixed format sense buffer */ - buf[0] =3D 0x70; - buf[2] =3D sense.key; - buf[7] =3D 10; - buf[12] =3D sense.asc; - buf[13] =3D sense.ascq; - return MIN(len, SCSI_SENSE_LEN); - } else { - /* Return descriptor format sense buffer */ - buf[0] =3D 0x72; - buf[1] =3D sense.key; - buf[2] =3D sense.asc; - buf[3] =3D sense.ascq; - return 8; - } -} - -const char *scsi_command_name(uint8_t cmd) -{ - static const char *names[] =3D { - [ TEST_UNIT_READY ] =3D "TEST_UNIT_READY", - [ REWIND ] =3D "REWIND", - [ REQUEST_SENSE ] =3D "REQUEST_SENSE", - [ FORMAT_UNIT ] =3D "FORMAT_UNIT", - [ READ_BLOCK_LIMITS ] =3D "READ_BLOCK_LIMITS", - [ REASSIGN_BLOCKS ] =3D "REASSIGN_BLOCKS/INITIALIZE ELEME= NT STATUS", - /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operatio= n code */ - [ READ_6 ] =3D "READ_6", - [ WRITE_6 ] =3D "WRITE_6", - [ SET_CAPACITY ] =3D "SET_CAPACITY", - [ READ_REVERSE ] =3D "READ_REVERSE", - [ WRITE_FILEMARKS ] =3D "WRITE_FILEMARKS", - [ SPACE ] =3D "SPACE", - [ INQUIRY ] =3D "INQUIRY", - [ RECOVER_BUFFERED_DATA ] =3D "RECOVER_BUFFERED_DATA", - [ MAINTENANCE_IN ] =3D "MAINTENANCE_IN", - [ MAINTENANCE_OUT ] =3D "MAINTENANCE_OUT", - [ MODE_SELECT ] =3D "MODE_SELECT", - [ RESERVE ] =3D "RESERVE", - [ RELEASE ] =3D "RELEASE", - [ COPY ] =3D "COPY", - [ ERASE ] =3D "ERASE", - [ MODE_SENSE ] =3D "MODE_SENSE", - [ START_STOP ] =3D "START_STOP/LOAD_UNLOAD", - /* LOAD_UNLOAD and START_STOP use the same operation code */ - [ RECEIVE_DIAGNOSTIC ] =3D "RECEIVE_DIAGNOSTIC", - [ SEND_DIAGNOSTIC ] =3D "SEND_DIAGNOSTIC", - [ ALLOW_MEDIUM_REMOVAL ] =3D "ALLOW_MEDIUM_REMOVAL", - [ READ_CAPACITY_10 ] =3D "READ_CAPACITY_10", - [ READ_10 ] =3D "READ_10", - [ WRITE_10 ] =3D "WRITE_10", - [ SEEK_10 ] =3D "SEEK_10/POSITION_TO_ELEMENT", - /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ - [ WRITE_VERIFY_10 ] =3D "WRITE_VERIFY_10", - [ VERIFY_10 ] =3D "VERIFY_10", - [ SEARCH_HIGH ] =3D "SEARCH_HIGH", - [ SEARCH_EQUAL ] =3D "SEARCH_EQUAL", - [ SEARCH_LOW ] =3D "SEARCH_LOW", - [ SET_LIMITS ] =3D "SET_LIMITS", - [ PRE_FETCH ] =3D "PRE_FETCH/READ_POSITION", - /* READ_POSITION and PRE_FETCH use the same operation code */ - [ SYNCHRONIZE_CACHE ] =3D "SYNCHRONIZE_CACHE", - [ LOCK_UNLOCK_CACHE ] =3D "LOCK_UNLOCK_CACHE", - [ READ_DEFECT_DATA ] =3D "READ_DEFECT_DATA/INITIALIZE_ELEM= ENT_STATUS_WITH_RANGE", - /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use t= he same operation code */ - [ MEDIUM_SCAN ] =3D "MEDIUM_SCAN", - [ COMPARE ] =3D "COMPARE", - [ COPY_VERIFY ] =3D "COPY_VERIFY", - [ WRITE_BUFFER ] =3D "WRITE_BUFFER", - [ READ_BUFFER ] =3D "READ_BUFFER", - [ UPDATE_BLOCK ] =3D "UPDATE_BLOCK", - [ READ_LONG_10 ] =3D "READ_LONG_10", - [ WRITE_LONG_10 ] =3D "WRITE_LONG_10", - [ CHANGE_DEFINITION ] =3D "CHANGE_DEFINITION", - [ WRITE_SAME_10 ] =3D "WRITE_SAME_10", - [ UNMAP ] =3D "UNMAP", - [ READ_TOC ] =3D "READ_TOC", - [ REPORT_DENSITY_SUPPORT ] =3D "REPORT_DENSITY_SUPPORT", - [ SANITIZE ] =3D "SANITIZE", - [ GET_CONFIGURATION ] =3D "GET_CONFIGURATION", - [ LOG_SELECT ] =3D "LOG_SELECT", - [ LOG_SENSE ] =3D "LOG_SENSE", - [ MODE_SELECT_10 ] =3D "MODE_SELECT_10", - [ RESERVE_10 ] =3D "RESERVE_10", - [ RELEASE_10 ] =3D "RELEASE_10", - [ MODE_SENSE_10 ] =3D "MODE_SENSE_10", - [ PERSISTENT_RESERVE_IN ] =3D "PERSISTENT_RESERVE_IN", - [ PERSISTENT_RESERVE_OUT ] =3D "PERSISTENT_RESERVE_OUT", - [ WRITE_FILEMARKS_16 ] =3D "WRITE_FILEMARKS_16", - [ EXTENDED_COPY ] =3D "EXTENDED_COPY", - [ ATA_PASSTHROUGH_16 ] =3D "ATA_PASSTHROUGH_16", - [ ACCESS_CONTROL_IN ] =3D "ACCESS_CONTROL_IN", - [ ACCESS_CONTROL_OUT ] =3D "ACCESS_CONTROL_OUT", - [ READ_16 ] =3D "READ_16", - [ COMPARE_AND_WRITE ] =3D "COMPARE_AND_WRITE", - [ WRITE_16 ] =3D "WRITE_16", - [ WRITE_VERIFY_16 ] =3D "WRITE_VERIFY_16", - [ VERIFY_16 ] =3D "VERIFY_16", - [ PRE_FETCH_16 ] =3D "PRE_FETCH_16", - [ SYNCHRONIZE_CACHE_16 ] =3D "SPACE_16/SYNCHRONIZE_CACHE_16", - /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ - [ LOCATE_16 ] =3D "LOCATE_16", - [ WRITE_SAME_16 ] =3D "ERASE_16/WRITE_SAME_16", - /* ERASE_16 and WRITE_SAME_16 use the same operation code */ - [ SERVICE_ACTION_IN_16 ] =3D "SERVICE_ACTION_IN_16", - [ WRITE_LONG_16 ] =3D "WRITE_LONG_16", - [ REPORT_LUNS ] =3D "REPORT_LUNS", - [ ATA_PASSTHROUGH_12 ] =3D "BLANK/ATA_PASSTHROUGH_12", - [ MOVE_MEDIUM ] =3D "MOVE_MEDIUM", - [ EXCHANGE_MEDIUM ] =3D "EXCHANGE MEDIUM", - [ READ_12 ] =3D "READ_12", - [ WRITE_12 ] =3D "WRITE_12", - [ ERASE_12 ] =3D "ERASE_12/GET_PERFORMANCE", - /* ERASE_12 and GET_PERFORMANCE use the same operation code */ - [ SERVICE_ACTION_IN_12 ] =3D "SERVICE_ACTION_IN_12", - [ WRITE_VERIFY_12 ] =3D "WRITE_VERIFY_12", - [ VERIFY_12 ] =3D "VERIFY_12", - [ SEARCH_HIGH_12 ] =3D "SEARCH_HIGH_12", - [ SEARCH_EQUAL_12 ] =3D "SEARCH_EQUAL_12", - [ SEARCH_LOW_12 ] =3D "SEARCH_LOW_12", - [ READ_ELEMENT_STATUS ] =3D "READ_ELEMENT_STATUS", - [ SEND_VOLUME_TAG ] =3D "SEND_VOLUME_TAG/SET_STREAMING", - /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ - [ READ_CD ] =3D "READ_CD", - [ READ_DEFECT_DATA_12 ] =3D "READ_DEFECT_DATA_12", - [ READ_DVD_STRUCTURE ] =3D "READ_DVD_STRUCTURE", - [ RESERVE_TRACK ] =3D "RESERVE_TRACK", - [ SEND_CUE_SHEET ] =3D "SEND_CUE_SHEET", - [ SEND_DVD_STRUCTURE ] =3D "SEND_DVD_STRUCTURE", - [ SET_CD_SPEED ] =3D "SET_CD_SPEED", - [ SET_READ_AHEAD ] =3D "SET_READ_AHEAD", - [ ALLOW_OVERWRITE ] =3D "ALLOW_OVERWRITE", - [ MECHANISM_STATUS ] =3D "MECHANISM_STATUS", - [ GET_EVENT_STATUS_NOTIFICATION ] =3D "GET_EVENT_STATUS_NOTIFICATI= ON", - [ READ_DISC_INFORMATION ] =3D "READ_DISC_INFORMATION", - }; - - if (cmd >=3D ARRAY_SIZE(names) || names[cmd] =3D=3D NULL) - return "*UNKNOWN*"; - return names[cmd]; -} - SCSIRequest *scsi_req_ref(SCSIRequest *req) { assert(req->refcount > 0); diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 7e1cbab77e..7a8f500934 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -36,14 +36,6 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS= __); } while (0) #include #include "block/scsi.h" =20 -#define SG_ERR_DRIVER_TIMEOUT 0x06 -#define SG_ERR_DRIVER_SENSE 0x08 - -#define SG_ERR_DID_OK 0x00 -#define SG_ERR_DID_NO_CONNECT 0x01 -#define SG_ERR_DID_BUS_BUSY 0x02 -#define SG_ERR_DID_TIME_OUT 0x03 - #ifndef MAX_UINT #define MAX_UINT ((unsigned int)-1) #endif diff --git a/include/block/scsi.h b/include/block/scsi.h index cdf0a58a07..a141dd71f8 100644 --- a/include/block/scsi.h +++ b/include/block/scsi.h @@ -150,8 +150,6 @@ #define READ_CD 0xbe #define SEND_DVD_STRUCTURE 0xbf =20 -const char *scsi_command_name(uint8_t cmd); - /* * SERVICE ACTION IN subcodes */ diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 6ef67fb504..23a8ee6a7d 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -4,45 +4,20 @@ #include "hw/qdev.h" #include "hw/block/block.h" #include "sysemu/sysemu.h" +#include "scsi/utils.h" #include "qemu/notify.h" =20 #define MAX_SCSI_DEVS 255 =20 -#define SCSI_CMD_BUF_SIZE 16 -#define SCSI_SENSE_LEN 18 -#define SCSI_SENSE_LEN_SCANNER 32 -#define SCSI_INQUIRY_LEN 36 - typedef struct SCSIBus SCSIBus; typedef struct SCSIBusInfo SCSIBusInfo; -typedef struct SCSICommand SCSICommand; typedef struct SCSIDevice SCSIDevice; typedef struct SCSIRequest SCSIRequest; typedef struct SCSIReqOps SCSIReqOps; =20 -enum SCSIXferMode { - SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ - SCSI_XFER_FROM_DEV, /* READ, INQUIRY, MODE_SENSE, ... */ - SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ -}; - -typedef struct SCSISense { - uint8_t key; - uint8_t asc; - uint8_t ascq; -} SCSISense; - #define SCSI_SENSE_BUF_SIZE_OLD 96 #define SCSI_SENSE_BUF_SIZE 252 =20 -struct SCSICommand { - uint8_t buf[SCSI_CMD_BUF_SIZE]; - int len; - size_t xfer; - uint64_t lba; - enum SCSIXferMode mode; -}; - struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; @@ -180,73 +155,6 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, Bl= ockBackend *blk, void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated); void scsi_legacy_handle_cmdline(void); =20 -/* - * Predefined sense codes - */ - -/* No sense data available */ -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, Medium not present */ -extern const struct SCSISense sense_code_NO_MEDIUM; -/* LUN not ready, medium removal prevented */ -extern const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED; -/* Hardware error, internal target failure */ -extern const struct SCSISense sense_code_TARGET_FAILURE; -/* Illegal request, invalid command operation code */ -extern const struct SCSISense sense_code_INVALID_OPCODE; -/* Illegal request, LBA out of range */ -extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE; -/* Illegal request, Invalid field in CDB */ -extern const struct SCSISense sense_code_INVALID_FIELD; -/* Illegal request, Invalid field in parameter list */ -extern const struct SCSISense sense_code_INVALID_PARAM; -/* Illegal request, Parameter list length error */ -extern const struct SCSISense sense_code_INVALID_PARAM_LEN; -/* Illegal request, LUN not supported */ -extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED; -/* Illegal request, Saving parameters not supported */ -extern const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED; -/* Illegal request, Incompatible format */ -extern const struct SCSISense sense_code_INCOMPATIBLE_FORMAT; -/* Illegal request, medium removal prevented */ -extern const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED; -/* Illegal request, Invalid Transfer Tag */ -extern const struct SCSISense sense_code_INVALID_TAG; -/* Command aborted, I/O process terminated */ -extern const struct SCSISense sense_code_IO_ERROR; -/* Command aborted, I_T Nexus loss occurred */ -extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; -/* Command aborted, Logical Unit failure */ -extern const struct SCSISense sense_code_LUN_FAILURE; -/* Command aborted, Overlapped Commands Attempted */ -extern const struct SCSISense sense_code_OVERLAPPED_COMMANDS; -/* LUN not ready, Capacity data has changed */ -extern const struct SCSISense sense_code_CAPACITY_CHANGED; -/* LUN not ready, Medium not present */ -extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM; -/* Unit attention, Power on, reset or bus device reset occurred */ -extern const struct SCSISense sense_code_RESET; -/* Unit attention, Medium may have changed*/ -extern const struct SCSISense sense_code_MEDIUM_CHANGED; -/* Unit attention, Reported LUNs data has changed */ -extern const struct SCSISense sense_code_REPORTED_LUNS_CHANGED; -/* Unit attention, Device internal reset */ -extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET; -/* Data Protection, Write Protected */ -extern const struct SCSISense sense_code_WRITE_PROTECTED; -/* Data Protection, Space Allocation Failed Write Protect */ -extern const struct SCSISense sense_code_SPACE_ALLOC_FAILED; - -#define SENSE_CODE(x) sense_code_ ## x - -uint32_t scsi_data_cdb_xfer(uint8_t *buf); -uint32_t scsi_cdb_xfer(uint8_t *buf); -int scsi_cdb_length(uint8_t *buf); -int scsi_convert_sense(uint8_t *in_buf, int in_len, - uint8_t *buf, int len, bool fixed); - SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, uint32_t lun, void *hba_private); SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, diff --git a/include/scsi/utils.h b/include/scsi/utils.h new file mode 100644 index 0000000000..35a74436bf --- /dev/null +++ b/include/scsi/utils.h @@ -0,0 +1,116 @@ +#ifndef SCSI_UTILS_H +#define SCSI_UTILS_H 1 + +#ifdef CONFIG_LINUX +#include +#endif + +#define SCSI_CMD_BUF_SIZE 16 +#define SCSI_SENSE_LEN 18 +#define SCSI_SENSE_LEN_SCANNER 32 +#define SCSI_INQUIRY_LEN 36 + +enum SCSIXferMode { + SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ + SCSI_XFER_FROM_DEV, /* READ, INQUIRY, MODE_SENSE, ... */ + SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ +}; + +typedef struct SCSICommand { + uint8_t buf[SCSI_CMD_BUF_SIZE]; + int len; + size_t xfer; + uint64_t lba; + enum SCSIXferMode mode; +} SCSICommand; + +typedef struct SCSISense { + uint8_t key; + uint8_t asc; + uint8_t ascq; +} SCSISense; + +/* + * Predefined sense codes + */ + +/* No sense data available */ +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, Medium not present */ +extern const struct SCSISense sense_code_NO_MEDIUM; +/* LUN not ready, medium removal prevented */ +extern const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED; +/* Hardware error, internal target failure */ +extern const struct SCSISense sense_code_TARGET_FAILURE; +/* Illegal request, invalid command operation code */ +extern const struct SCSISense sense_code_INVALID_OPCODE; +/* Illegal request, LBA out of range */ +extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE; +/* Illegal request, Invalid field in CDB */ +extern const struct SCSISense sense_code_INVALID_FIELD; +/* Illegal request, Invalid field in parameter list */ +extern const struct SCSISense sense_code_INVALID_PARAM; +/* Illegal request, Parameter list length error */ +extern const struct SCSISense sense_code_INVALID_PARAM_LEN; +/* Illegal request, LUN not supported */ +extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED; +/* Illegal request, Saving parameters not supported */ +extern const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED; +/* Illegal request, Incompatible format */ +extern const struct SCSISense sense_code_INCOMPATIBLE_FORMAT; +/* Illegal request, medium removal prevented */ +extern const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED; +/* Illegal request, Invalid Transfer Tag */ +extern const struct SCSISense sense_code_INVALID_TAG; +/* Command aborted, I/O process terminated */ +extern const struct SCSISense sense_code_IO_ERROR; +/* Command aborted, I_T Nexus loss occurred */ +extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; +/* Command aborted, Logical Unit failure */ +extern const struct SCSISense sense_code_LUN_FAILURE; +/* Command aborted, Overlapped Commands Attempted */ +extern const struct SCSISense sense_code_OVERLAPPED_COMMANDS; +/* LUN not ready, Capacity data has changed */ +extern const struct SCSISense sense_code_CAPACITY_CHANGED; +/* LUN not ready, Medium not present */ +extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM; +/* Unit attention, Power on, reset or bus device reset occurred */ +extern const struct SCSISense sense_code_RESET; +/* Unit attention, Medium may have changed*/ +extern const struct SCSISense sense_code_MEDIUM_CHANGED; +/* Unit attention, Reported LUNs data has changed */ +extern const struct SCSISense sense_code_REPORTED_LUNS_CHANGED; +/* Unit attention, Device internal reset */ +extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET; +/* Data Protection, Write Protected */ +extern const struct SCSISense sense_code_WRITE_PROTECTED; +/* Data Protection, Space Allocation Failed Write Protect */ +extern const struct SCSISense sense_code_SPACE_ALLOC_FAILED; + +#define SENSE_CODE(x) sense_code_ ## x + +int scsi_convert_sense(uint8_t *in_buf, int in_len, + uint8_t *buf, int len, bool fixed); +const char *scsi_command_name(uint8_t cmd); + +uint64_t scsi_cmd_lba(SCSICommand *cmd); +uint32_t scsi_data_cdb_xfer(uint8_t *buf); +uint32_t scsi_cdb_xfer(uint8_t *buf); +int scsi_cdb_length(uint8_t *buf); + +/* Linux SG_IO interface. */ +#ifdef CONFIG_LINUX +#define SG_ERR_DRIVER_TIMEOUT 0x06 +#define SG_ERR_DRIVER_SENSE 0x08 + +#define SG_ERR_DID_OK 0x00 +#define SG_ERR_DID_NO_CONNECT 0x01 +#define SG_ERR_DID_BUS_BUSY 0x02 +#define SG_ERR_DID_TIME_OUT 0x03 + +#define SG_ERR_DRIVER_SENSE 0x08 +#endif + +#endif diff --git a/scsi/Makefile.objs b/scsi/Makefile.objs new file mode 100644 index 0000000000..31b82a5a36 --- /dev/null +++ b/scsi/Makefile.objs @@ -0,0 +1 @@ +block-obj-y +=3D utils.o diff --git a/scsi/utils.c b/scsi/utils.c new file mode 100644 index 0000000000..0db727591f --- /dev/null +++ b/scsi/utils.c @@ -0,0 +1,403 @@ +#include "qemu/osdep.h" +#include "block/scsi.h" +#include "scsi/utils.h" +#include "qemu/bswap.h" + +uint32_t scsi_data_cdb_xfer(uint8_t *buf) +{ + if ((buf[0] >> 5) =3D=3D 0 && buf[4] =3D=3D 0) { + return 256; + } else { + return scsi_cdb_xfer(buf); + } +} + +uint32_t scsi_cdb_xfer(uint8_t *buf) +{ + switch (buf[0] >> 5) { + case 0: + return buf[4]; + break; + case 1: + case 2: + return lduw_be_p(&buf[7]); + break; + case 4: + return ldl_be_p(&buf[10]) & 0xffffffffULL; + break; + case 5: + return ldl_be_p(&buf[6]) & 0xffffffffULL; + break; + default: + return -1; + } +} + +uint64_t scsi_cmd_lba(SCSICommand *cmd) +{ + uint8_t *buf =3D cmd->buf; + uint64_t lba; + + switch (buf[0] >> 5) { + case 0: + lba =3D ldl_be_p(&buf[0]) & 0x1fffff; + break; + case 1: + case 2: + case 5: + lba =3D ldl_be_p(&buf[2]) & 0xffffffffULL; + break; + case 4: + lba =3D ldq_be_p(&buf[2]); + break; + default: + lba =3D -1; + + } + return lba; +} + +int scsi_cdb_length(uint8_t *buf) +{ + int cdb_len; + + switch (buf[0] >> 5) { + case 0: + cdb_len =3D 6; + break; + case 1: + case 2: + cdb_len =3D 10; + break; + case 4: + cdb_len =3D 16; + break; + case 5: + cdb_len =3D 12; + break; + default: + cdb_len =3D -1; + } + return cdb_len; +} + +/* + * Predefined sense codes + */ + +/* No sense data available */ +const struct SCSISense sense_code_NO_SENSE =3D { + .key =3D NO_SENSE , .asc =3D 0x00 , .ascq =3D 0x00 +}; + +/* LUN not ready, Manual intervention required */ +const struct SCSISense sense_code_LUN_NOT_READY =3D { + .key =3D NOT_READY, .asc =3D 0x04, .ascq =3D 0x03 +}; + +/* LUN not ready, Medium not present */ +const struct SCSISense sense_code_NO_MEDIUM =3D { + .key =3D NOT_READY, .asc =3D 0x3a, .ascq =3D 0x00 +}; + +/* LUN not ready, medium removal prevented */ +const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED =3D { + .key =3D NOT_READY, .asc =3D 0x53, .ascq =3D 0x02 +}; + +/* Hardware error, internal target failure */ +const struct SCSISense sense_code_TARGET_FAILURE =3D { + .key =3D HARDWARE_ERROR, .asc =3D 0x44, .ascq =3D 0x00 +}; + +/* Illegal request, invalid command operation code */ +const struct SCSISense sense_code_INVALID_OPCODE =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x20, .ascq =3D 0x00 +}; + +/* Illegal request, LBA out of range */ +const struct SCSISense sense_code_LBA_OUT_OF_RANGE =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x21, .ascq =3D 0x00 +}; + +/* Illegal request, Invalid field in CDB */ +const struct SCSISense sense_code_INVALID_FIELD =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x24, .ascq =3D 0x00 +}; + +/* Illegal request, Invalid field in parameter list */ +const struct SCSISense sense_code_INVALID_PARAM =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x26, .ascq =3D 0x00 +}; + +/* Illegal request, Parameter list length error */ +const struct SCSISense sense_code_INVALID_PARAM_LEN =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x1a, .ascq =3D 0x00 +}; + +/* Illegal request, LUN not supported */ +const struct SCSISense sense_code_LUN_NOT_SUPPORTED =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x25, .ascq =3D 0x00 +}; + +/* Illegal request, Saving parameters not supported */ +const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x39, .ascq =3D 0x00 +}; + +/* Illegal request, Incompatible medium installed */ +const struct SCSISense sense_code_INCOMPATIBLE_FORMAT =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x30, .ascq =3D 0x00 +}; + +/* Illegal request, medium removal prevented */ +const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x53, .ascq =3D 0x02 +}; + +/* Illegal request, Invalid Transfer Tag */ +const struct SCSISense sense_code_INVALID_TAG =3D { + .key =3D ILLEGAL_REQUEST, .asc =3D 0x4b, .ascq =3D 0x01 +}; + +/* Command aborted, I/O process terminated */ +const struct SCSISense sense_code_IO_ERROR =3D { + .key =3D ABORTED_COMMAND, .asc =3D 0x00, .ascq =3D 0x06 +}; + +/* Command aborted, I_T Nexus loss occurred */ +const struct SCSISense sense_code_I_T_NEXUS_LOSS =3D { + .key =3D ABORTED_COMMAND, .asc =3D 0x29, .ascq =3D 0x07 +}; + +/* Command aborted, Logical Unit failure */ +const struct SCSISense sense_code_LUN_FAILURE =3D { + .key =3D ABORTED_COMMAND, .asc =3D 0x3e, .ascq =3D 0x01 +}; + +/* Command aborted, Overlapped Commands Attempted */ +const struct SCSISense sense_code_OVERLAPPED_COMMANDS =3D { + .key =3D ABORTED_COMMAND, .asc =3D 0x4e, .ascq =3D 0x00 +}; + +/* Unit attention, Capacity data has changed */ +const struct SCSISense sense_code_CAPACITY_CHANGED =3D { + .key =3D UNIT_ATTENTION, .asc =3D 0x2a, .ascq =3D 0x09 +}; + +/* Unit attention, Power on, reset or bus device reset occurred */ +const struct SCSISense sense_code_RESET =3D { + .key =3D UNIT_ATTENTION, .asc =3D 0x29, .ascq =3D 0x00 +}; + +/* Unit attention, No medium */ +const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM =3D { + .key =3D UNIT_ATTENTION, .asc =3D 0x3a, .ascq =3D 0x00 +}; + +/* Unit attention, Medium may have changed */ +const struct SCSISense sense_code_MEDIUM_CHANGED =3D { + .key =3D UNIT_ATTENTION, .asc =3D 0x28, .ascq =3D 0x00 +}; + +/* Unit attention, Reported LUNs data has changed */ +const struct SCSISense sense_code_REPORTED_LUNS_CHANGED =3D { + .key =3D UNIT_ATTENTION, .asc =3D 0x3f, .ascq =3D 0x0e +}; + +/* Unit attention, Device internal reset */ +const struct SCSISense sense_code_DEVICE_INTERNAL_RESET =3D { + .key =3D UNIT_ATTENTION, .asc =3D 0x29, .ascq =3D 0x04 +}; + +/* Data Protection, Write Protected */ +const struct SCSISense sense_code_WRITE_PROTECTED =3D { + .key =3D DATA_PROTECT, .asc =3D 0x27, .ascq =3D 0x00 +}; + +/* Data Protection, Space Allocation Failed Write Protect */ +const struct SCSISense sense_code_SPACE_ALLOC_FAILED =3D { + .key =3D DATA_PROTECT, .asc =3D 0x27, .ascq =3D 0x07 +}; + +/* + * scsi_convert_sense + * + * Convert between fixed and descriptor sense buffers + */ +int scsi_convert_sense(uint8_t *in_buf, int in_len, + uint8_t *buf, int len, bool fixed) +{ + bool fixed_in; + SCSISense sense; + if (!fixed && len < 8) { + return 0; + } + + if (in_len =3D=3D 0) { + sense.key =3D NO_SENSE; + sense.asc =3D 0; + sense.ascq =3D 0; + } else { + fixed_in =3D (in_buf[0] & 2) =3D=3D 0; + + if (fixed =3D=3D fixed_in) { + memcpy(buf, in_buf, MIN(len, in_len)); + return MIN(len, in_len); + } + + if (fixed_in) { + sense.key =3D in_buf[2]; + sense.asc =3D in_buf[12]; + sense.ascq =3D in_buf[13]; + } else { + sense.key =3D in_buf[1]; + sense.asc =3D in_buf[2]; + sense.ascq =3D in_buf[3]; + } + } + + memset(buf, 0, len); + if (fixed) { + /* Return fixed format sense buffer */ + buf[0] =3D 0x70; + buf[2] =3D sense.key; + buf[7] =3D 10; + buf[12] =3D sense.asc; + buf[13] =3D sense.ascq; + return MIN(len, SCSI_SENSE_LEN); + } else { + /* Return descriptor format sense buffer */ + buf[0] =3D 0x72; + buf[1] =3D sense.key; + buf[2] =3D sense.asc; + buf[3] =3D sense.ascq; + return 8; + } +} + +const char *scsi_command_name(uint8_t cmd) +{ + static const char *names[] =3D { + [ TEST_UNIT_READY ] =3D "TEST_UNIT_READY", + [ REWIND ] =3D "REWIND", + [ REQUEST_SENSE ] =3D "REQUEST_SENSE", + [ FORMAT_UNIT ] =3D "FORMAT_UNIT", + [ READ_BLOCK_LIMITS ] =3D "READ_BLOCK_LIMITS", + [ REASSIGN_BLOCKS ] =3D "REASSIGN_BLOCKS/INITIALIZE ELEME= NT STATUS", + /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operatio= n code */ + [ READ_6 ] =3D "READ_6", + [ WRITE_6 ] =3D "WRITE_6", + [ SET_CAPACITY ] =3D "SET_CAPACITY", + [ READ_REVERSE ] =3D "READ_REVERSE", + [ WRITE_FILEMARKS ] =3D "WRITE_FILEMARKS", + [ SPACE ] =3D "SPACE", + [ INQUIRY ] =3D "INQUIRY", + [ RECOVER_BUFFERED_DATA ] =3D "RECOVER_BUFFERED_DATA", + [ MAINTENANCE_IN ] =3D "MAINTENANCE_IN", + [ MAINTENANCE_OUT ] =3D "MAINTENANCE_OUT", + [ MODE_SELECT ] =3D "MODE_SELECT", + [ RESERVE ] =3D "RESERVE", + [ RELEASE ] =3D "RELEASE", + [ COPY ] =3D "COPY", + [ ERASE ] =3D "ERASE", + [ MODE_SENSE ] =3D "MODE_SENSE", + [ START_STOP ] =3D "START_STOP/LOAD_UNLOAD", + /* LOAD_UNLOAD and START_STOP use the same operation code */ + [ RECEIVE_DIAGNOSTIC ] =3D "RECEIVE_DIAGNOSTIC", + [ SEND_DIAGNOSTIC ] =3D "SEND_DIAGNOSTIC", + [ ALLOW_MEDIUM_REMOVAL ] =3D "ALLOW_MEDIUM_REMOVAL", + [ READ_CAPACITY_10 ] =3D "READ_CAPACITY_10", + [ READ_10 ] =3D "READ_10", + [ WRITE_10 ] =3D "WRITE_10", + [ SEEK_10 ] =3D "SEEK_10/POSITION_TO_ELEMENT", + /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ + [ WRITE_VERIFY_10 ] =3D "WRITE_VERIFY_10", + [ VERIFY_10 ] =3D "VERIFY_10", + [ SEARCH_HIGH ] =3D "SEARCH_HIGH", + [ SEARCH_EQUAL ] =3D "SEARCH_EQUAL", + [ SEARCH_LOW ] =3D "SEARCH_LOW", + [ SET_LIMITS ] =3D "SET_LIMITS", + [ PRE_FETCH ] =3D "PRE_FETCH/READ_POSITION", + /* READ_POSITION and PRE_FETCH use the same operation code */ + [ SYNCHRONIZE_CACHE ] =3D "SYNCHRONIZE_CACHE", + [ LOCK_UNLOCK_CACHE ] =3D "LOCK_UNLOCK_CACHE", + [ READ_DEFECT_DATA ] =3D "READ_DEFECT_DATA/INITIALIZE_ELEM= ENT_STATUS_WITH_RANGE", + /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use t= he same operation code */ + [ MEDIUM_SCAN ] =3D "MEDIUM_SCAN", + [ COMPARE ] =3D "COMPARE", + [ COPY_VERIFY ] =3D "COPY_VERIFY", + [ WRITE_BUFFER ] =3D "WRITE_BUFFER", + [ READ_BUFFER ] =3D "READ_BUFFER", + [ UPDATE_BLOCK ] =3D "UPDATE_BLOCK", + [ READ_LONG_10 ] =3D "READ_LONG_10", + [ WRITE_LONG_10 ] =3D "WRITE_LONG_10", + [ CHANGE_DEFINITION ] =3D "CHANGE_DEFINITION", + [ WRITE_SAME_10 ] =3D "WRITE_SAME_10", + [ UNMAP ] =3D "UNMAP", + [ READ_TOC ] =3D "READ_TOC", + [ REPORT_DENSITY_SUPPORT ] =3D "REPORT_DENSITY_SUPPORT", + [ SANITIZE ] =3D "SANITIZE", + [ GET_CONFIGURATION ] =3D "GET_CONFIGURATION", + [ LOG_SELECT ] =3D "LOG_SELECT", + [ LOG_SENSE ] =3D "LOG_SENSE", + [ MODE_SELECT_10 ] =3D "MODE_SELECT_10", + [ RESERVE_10 ] =3D "RESERVE_10", + [ RELEASE_10 ] =3D "RELEASE_10", + [ MODE_SENSE_10 ] =3D "MODE_SENSE_10", + [ PERSISTENT_RESERVE_IN ] =3D "PERSISTENT_RESERVE_IN", + [ PERSISTENT_RESERVE_OUT ] =3D "PERSISTENT_RESERVE_OUT", + [ WRITE_FILEMARKS_16 ] =3D "WRITE_FILEMARKS_16", + [ EXTENDED_COPY ] =3D "EXTENDED_COPY", + [ ATA_PASSTHROUGH_16 ] =3D "ATA_PASSTHROUGH_16", + [ ACCESS_CONTROL_IN ] =3D "ACCESS_CONTROL_IN", + [ ACCESS_CONTROL_OUT ] =3D "ACCESS_CONTROL_OUT", + [ READ_16 ] =3D "READ_16", + [ COMPARE_AND_WRITE ] =3D "COMPARE_AND_WRITE", + [ WRITE_16 ] =3D "WRITE_16", + [ WRITE_VERIFY_16 ] =3D "WRITE_VERIFY_16", + [ VERIFY_16 ] =3D "VERIFY_16", + [ PRE_FETCH_16 ] =3D "PRE_FETCH_16", + [ SYNCHRONIZE_CACHE_16 ] =3D "SPACE_16/SYNCHRONIZE_CACHE_16", + /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ + [ LOCATE_16 ] =3D "LOCATE_16", + [ WRITE_SAME_16 ] =3D "ERASE_16/WRITE_SAME_16", + /* ERASE_16 and WRITE_SAME_16 use the same operation code */ + [ SERVICE_ACTION_IN_16 ] =3D "SERVICE_ACTION_IN_16", + [ WRITE_LONG_16 ] =3D "WRITE_LONG_16", + [ REPORT_LUNS ] =3D "REPORT_LUNS", + [ ATA_PASSTHROUGH_12 ] =3D "BLANK/ATA_PASSTHROUGH_12", + [ MOVE_MEDIUM ] =3D "MOVE_MEDIUM", + [ EXCHANGE_MEDIUM ] =3D "EXCHANGE MEDIUM", + [ READ_12 ] =3D "READ_12", + [ WRITE_12 ] =3D "WRITE_12", + [ ERASE_12 ] =3D "ERASE_12/GET_PERFORMANCE", + /* ERASE_12 and GET_PERFORMANCE use the same operation code */ + [ SERVICE_ACTION_IN_12 ] =3D "SERVICE_ACTION_IN_12", + [ WRITE_VERIFY_12 ] =3D "WRITE_VERIFY_12", + [ VERIFY_12 ] =3D "VERIFY_12", + [ SEARCH_HIGH_12 ] =3D "SEARCH_HIGH_12", + [ SEARCH_EQUAL_12 ] =3D "SEARCH_EQUAL_12", + [ SEARCH_LOW_12 ] =3D "SEARCH_LOW_12", + [ READ_ELEMENT_STATUS ] =3D "READ_ELEMENT_STATUS", + [ SEND_VOLUME_TAG ] =3D "SEND_VOLUME_TAG/SET_STREAMING", + /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ + [ READ_CD ] =3D "READ_CD", + [ READ_DEFECT_DATA_12 ] =3D "READ_DEFECT_DATA_12", + [ READ_DVD_STRUCTURE ] =3D "READ_DVD_STRUCTURE", + [ RESERVE_TRACK ] =3D "RESERVE_TRACK", + [ SEND_CUE_SHEET ] =3D "SEND_CUE_SHEET", + [ SEND_DVD_STRUCTURE ] =3D "SEND_DVD_STRUCTURE", + [ SET_CD_SPEED ] =3D "SET_CD_SPEED", + [ SET_READ_AHEAD ] =3D "SET_READ_AHEAD", + [ ALLOW_OVERWRITE ] =3D "ALLOW_OVERWRITE", + [ MECHANISM_STATUS ] =3D "MECHANISM_STATUS", + [ GET_EVENT_STATUS_NOTIFICATION ] =3D "GET_EVENT_STATUS_NOTIFICATI= ON", + [ READ_DISC_INFORMATION ] =3D "READ_DISC_INFORMATION", + }; + + if (cmd >=3D ARRAY_SIZE(names) || names[cmd] =3D=3D NULL) { + return "*UNKNOWN*"; + } + return names[cmd]; +} --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 15034080589470.12682024144021398; Tue, 22 Aug 2017 06:20:58 -0700 (PDT) Received: from localhost ([::1]:45932 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk973-0004GT-Ev for importer@patchew.org; Tue, 22 Aug 2017 09:20:57 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49652) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk94y-0002dF-WB for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:50 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk94v-0005mM-1Q for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:48 -0400 Received: from mx1.redhat.com ([209.132.183.28]:30175) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk94u-0005hU-Qz for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:44 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7F6E481E0F; Tue, 22 Aug 2017 13:18:39 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4E98081B49; Tue, 22 Aug 2017 13:18:38 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 7F6E481E0F Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:25 +0200 Message-Id: <20170822131832.20191-4-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Tue, 22 Aug 2017 13:18:39 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 03/10] scsi: introduce scsi_build_sense X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Move more knowledge of sense data format out of hw/scsi/scsi-bus.c for reusability. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daud=C3=A9 Reviewed-by: Stefan Hajnoczi --- hw/scsi/scsi-bus.c | 8 +------- include/scsi/utils.h | 2 ++ scsi/utils.c | 11 +++++++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 300912d213..f71218f02a 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -797,13 +797,7 @@ void scsi_req_build_sense(SCSIRequest *req, SCSISense = sense) { trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag, sense.key, sense.asc, sense.ascq); - memset(req->sense, 0, 18); - req->sense[0] =3D 0x70; - req->sense[2] =3D sense.key; - req->sense[7] =3D 10; - req->sense[12] =3D sense.asc; - req->sense[13] =3D sense.ascq; - req->sense_len =3D 18; + req->sense_len =3D scsi_build_sense(req->sense, sense); } =20 static void scsi_req_enqueue_internal(SCSIRequest *req) diff --git a/include/scsi/utils.h b/include/scsi/utils.h index 35a74436bf..76c3db98c0 100644 --- a/include/scsi/utils.h +++ b/include/scsi/utils.h @@ -30,6 +30,8 @@ typedef struct SCSISense { uint8_t ascq; } SCSISense; =20 +int scsi_build_sense(uint8_t *buf, SCSISense sense); + /* * Predefined sense codes */ diff --git a/scsi/utils.c b/scsi/utils.c index 0db727591f..54d6d4486a 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -81,6 +81,17 @@ int scsi_cdb_length(uint8_t *buf) return cdb_len; } =20 +int scsi_build_sense(uint8_t *buf, SCSISense sense) +{ + memset(buf, 0, 18); + buf[0] =3D 0x70; + buf[2] =3D sense.key; + buf[7] =3D 10; + buf[12] =3D sense.asc; + buf[13] =3D sense.ascq; + return 18; +} + /* * Predefined sense codes */ --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1503408067240813.4101704488886; Tue, 22 Aug 2017 06:21:07 -0700 (PDT) Received: from localhost ([::1]:45934 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk97C-0004SJ-22 for importer@patchew.org; Tue, 22 Aug 2017 09:21:06 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49643) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk94y-0002dC-UG for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:54 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk94v-0005n7-KJ for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:48 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34486) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk94v-0005jR-9T for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:45 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CB187806BF; Tue, 22 Aug 2017 13:18:41 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id D186F81B49; Tue, 22 Aug 2017 13:18:39 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com CB187806BF Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:26 +0200 Message-Id: <20170822131832.20191-5-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Tue, 22 Aug 2017 13:18:42 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 04/10] scsi: introduce sg_io_sense_from_errno X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Move more knowledge of SG_IO out of hw/scsi/scsi-generic.c, for reusability. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daud=C3=A9 Reviewed-by: Stefan Hajnoczi --- hw/scsi/scsi-generic.c | 40 +++++++--------------------------------- include/scsi/utils.h | 3 +++ scsi/utils.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 7a8f500934..04c687ee76 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -81,6 +81,7 @@ static void scsi_free_request(SCSIRequest *req) static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) { int status; + SCSISense sense; =20 assert(r->req.aiocb =3D=3D NULL); =20 @@ -88,42 +89,15 @@ static void scsi_command_complete_noio(SCSIGenericReq *= r, int ret) scsi_req_cancel_complete(&r->req); goto done; } - if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { - r->req.sense_len =3D r->io_header.sb_len_wr; - } - - if (ret !=3D 0) { - switch (ret) { - case -EDOM: - status =3D TASK_SET_FULL; - break; - case -ENOMEM: - status =3D CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); - break; - default: - status =3D CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); - break; - } - } else { - if (r->io_header.host_status =3D=3D SG_ERR_DID_NO_CONNECT || - r->io_header.host_status =3D=3D SG_ERR_DID_BUS_BUSY || - r->io_header.host_status =3D=3D SG_ERR_DID_TIME_OUT || - (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { - status =3D BUSY; - BADF("Driver Timeout\n"); - } else if (r->io_header.host_status) { - status =3D CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); - } else if (r->io_header.status) { - status =3D r->io_header.status; - } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { - status =3D CHECK_CONDITION; + status =3D sg_io_sense_from_errno(-ret, &r->io_header, &sense); + if (status =3D=3D CHECK_CONDITION) { + if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { + r->req.sense_len =3D r->io_header.sb_len_wr; } else { - status =3D GOOD; + scsi_req_build_sense(&r->req, sense); } } + DPRINTF("Command complete 0x%p tag=3D0x%x status=3D%d\n", r, r->req.tag, status); =20 diff --git a/include/scsi/utils.h b/include/scsi/utils.h index 76c3db98c0..c12b34f2e5 100644 --- a/include/scsi/utils.h +++ b/include/scsi/utils.h @@ -113,6 +113,9 @@ int scsi_cdb_length(uint8_t *buf); #define SG_ERR_DID_TIME_OUT 0x03 =20 #define SG_ERR_DRIVER_SENSE 0x08 + +int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr, + SCSISense *sense); #endif =20 #endif diff --git a/scsi/utils.c b/scsi/utils.c index 54d6d4486a..ca5b058a73 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -412,3 +412,38 @@ const char *scsi_command_name(uint8_t cmd) } return names[cmd]; } + +#ifdef CONFIG_LINUX +int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr, + SCSISense *sense) +{ + if (errno_value !=3D 0) { + switch (errno_value) { + case EDOM: + return TASK_SET_FULL; + case ENOMEM: + *sense =3D SENSE_CODE(TARGET_FAILURE); + return CHECK_CONDITION; + default: + *sense =3D SENSE_CODE(IO_ERROR); + return CHECK_CONDITION; + } + } else { + if (io_hdr->host_status =3D=3D SG_ERR_DID_NO_CONNECT || + io_hdr->host_status =3D=3D SG_ERR_DID_BUS_BUSY || + io_hdr->host_status =3D=3D SG_ERR_DID_TIME_OUT || + (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT)) { + return BUSY; + } else if (io_hdr->host_status) { + *sense =3D SENSE_CODE(I_T_NEXUS_LOSS); + return CHECK_CONDITION; + } else if (io_hdr->status) { + return io_hdr->status; + } else if (io_hdr->driver_status & SG_ERR_DRIVER_SENSE) { + return CHECK_CONDITION; + } else { + return GOOD; + } + } +} +#endif --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1503408235573202.4072444472232; Tue, 22 Aug 2017 06:23:55 -0700 (PDT) Received: from localhost ([::1]:45960 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk99u-0007fR-6L for importer@patchew.org; Tue, 22 Aug 2017 09:23:54 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49644) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk94y-0002dD-Us for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk94x-0005ow-4i for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:48 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34560) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk94w-0005kt-PZ for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:47 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 59B4C806C6; Tue, 22 Aug 2017 13:18:43 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id 25CCF81B49; Tue, 22 Aug 2017 13:18:41 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 59B4C806C6 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:27 +0200 Message-Id: <20170822131832.20191-6-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Tue, 22 Aug 2017 13:18:43 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 05/10] scsi: move block/scsi.h to include/scsi/constants.h X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Complete the transition by renaming this header, which was shared by block/iscsi.c and the SCSI emulation code. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daud=C3=A9 Reviewed-by: Stefan Hajnoczi --- block/iscsi.c | 2 +- hw/block/virtio-blk.c | 2 +- hw/scsi/megasas.c | 2 +- hw/scsi/mptendian.c | 2 +- hw/scsi/mptsas.c | 2 +- hw/scsi/scsi-bus.c | 2 +- hw/scsi/scsi-disk.c | 2 +- hw/scsi/scsi-generic.c | 2 +- hw/scsi/spapr_vscsi.c | 2 +- hw/scsi/virtio-scsi-dataplane.c | 2 +- hw/scsi/virtio-scsi.c | 2 +- hw/scsi/vmw_pvscsi.c | 2 +- hw/usb/dev-uas.c | 2 +- include/hw/ide/internal.h | 2 +- include/{block/scsi.h =3D> scsi/constants.h} | 0 scsi/utils.c | 2 +- tests/virtio-scsi-test.c | 2 +- 17 files changed, 16 insertions(+), 16 deletions(-) rename include/{block/scsi.h =3D> scsi/constants.h} (100%) diff --git a/block/iscsi.c b/block/iscsi.c index d557c99668..c43c0953e1 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -34,7 +34,7 @@ #include "qemu/bitops.h" #include "qemu/bitmap.h" #include "block/block_int.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "qemu/iov.h" #include "qemu/uuid.h" #include "qmp-commands.h" diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index a16ac75090..05d1440786 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -22,7 +22,7 @@ #include "sysemu/blockdev.h" #include "hw/virtio/virtio-blk.h" #include "dataplane/virtio-blk.h" -#include "block/scsi.h" +#include "scsi/constants.h" #ifdef __linux__ # include #endif diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 734fdaef90..0db68aacee 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -27,7 +27,7 @@ #include "hw/pci/msix.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "trace.h" #include "qapi/error.h" #include "mfi.h" diff --git a/hw/scsi/mptendian.c b/hw/scsi/mptendian.c index b7fe2a2a36..3415229b5e 100644 --- a/hw/scsi/mptendian.c +++ b/hw/scsi/mptendian.c @@ -28,7 +28,7 @@ #include "hw/pci/msi.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "trace.h" =20 #include "mptsas.h" diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 765ab53c34..8bae8f543e 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -30,7 +30,7 @@ #include "hw/pci/msi.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "trace.h" #include "qapi/error.h" #include "mptsas.h" diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index f71218f02a..b9244606f8 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -3,7 +3,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "hw/qdev.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 0a1f4ef0c7..5faf6682c5 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -32,7 +32,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while = (0) #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 04c687ee76..bd0d9ff355 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -34,7 +34,7 @@ do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } whi= le (0) do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) =20 #include -#include "block/scsi.h" +#include "scsi/constants.h" =20 #ifndef MAX_UINT #define MAX_UINT ((unsigned int)-1) diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 55ee48c4da..360db53ac8 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -36,7 +36,7 @@ #include "cpu.h" #include "hw/hw.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "srp.h" #include "hw/qdev.h" #include "hw/ppc/spapr.h" diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplan= e.c index 944ea4eb53..add4b3f4a4 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -17,7 +17,7 @@ #include "qemu/error-report.h" #include "sysemu/block-backend.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" =20 diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index eb639442d1..823a1e9a42 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -21,7 +21,7 @@ #include "qemu/iov.h" #include "sysemu/block-backend.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" =20 diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 77d8b6f9e2..6d3f0bf11d 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "hw/pci/msi.h" #include "vmw_pvscsi.h" #include "trace.h" diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index fffc424396..c218b53f09 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -19,7 +19,7 @@ #include "hw/usb.h" #include "hw/usb/desc.h" #include "hw/scsi/scsi.h" -#include "block/scsi.h" +#include "scsi/constants.h" =20 /* --------------------------------------------------------------------- */ =20 diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 482a9512be..63a99e0366 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -11,7 +11,7 @@ #include "sysemu/dma.h" #include "sysemu/sysemu.h" #include "hw/block/block.h" -#include "block/scsi.h" +#include "scsi/constants.h" =20 /* debug IDE devices */ //#define DEBUG_IDE diff --git a/include/block/scsi.h b/include/scsi/constants.h similarity index 100% rename from include/block/scsi.h rename to include/scsi/constants.h diff --git a/scsi/utils.c b/scsi/utils.c index ca5b058a73..eedd5f45b4 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "scsi/utils.h" #include "qemu/bswap.h" =20 diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 87a3b6e81a..082d323541 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -10,7 +10,7 @@ =20 #include "qemu/osdep.h" #include "libqtest.h" -#include "block/scsi.h" +#include "scsi/constants.h" #include "libqos/libqos-pc.h" #include "libqos/libqos-spapr.h" #include "libqos/virtio.h" --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 150340850119251.823437808688254; Tue, 22 Aug 2017 06:28:21 -0700 (PDT) Received: from localhost ([::1]:46132 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk9EB-0002qg-Dc for importer@patchew.org; Tue, 22 Aug 2017 09:28:19 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49694) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk953-0002gI-6P for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:00 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk951-0005ta-4M for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:18:53 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34706) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk94v-0005mh-VL; Tue, 22 Aug 2017 09:18:46 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id ECC47806CB; Tue, 22 Aug 2017 13:18:44 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id AC1A281B49; Tue, 22 Aug 2017 13:18:43 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com ECC47806CB Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:28 +0200 Message-Id: <20170822131832.20191-7-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Tue, 22 Aug 2017 13:18:45 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 06/10] scsi, file-posix: add support for persistent reservation management X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" It is a common requirement for virtual machine to send persistent reservations, but this currently requires either running QEMU with CAP_SYS_RAWIO, or using out-of-tree patches that let an unprivileged QEMU bypass Linux's filter on SG_IO commands. As an alternative mechanism, the next patches will introduce a privileged helper to run persistent reservation commands without expanding QEMU's attack surface unnecessarily. The helper is invoked through a "pr-manager" QOM object, to which file-posix.c passes SG_IO requests for PERSISTENT RESERVE OUT and PERSISTENT RESERVE IN commands. For example: $ qemu-system-x86_64 -device virtio-scsi \ -object pr-manager-helper,id=3Dhelper0,path=3D/var/run/qemu-pr-helper= .sock -drive if=3Dnone,id=3Dhd,driver=3Draw,file.filename=3D/dev/sdb,file.p= r-manager=3Dhelper0 -device scsi-block,drive=3Dhd or: $ qemu-system-x86_64 -device virtio-scsi \ -object pr-manager-helper,id=3Dhelper0,path=3D/var/run/qemu-pr-helper= .sock -blockdev node-name=3Dhd,driver=3Draw,file.driver=3Dhost_device,file.= filename=3D/dev/sdb,file.pr-manager=3Dhelper0 -device scsi-block,drive=3Dhd Multiple pr-manager implementations are conceivable and possible, though only one is implemented right now. For example, a pr-manager could: - talk directly to the multipath daemon from a privileged QEMU (i.e. QEMU links to libmpathpersist); this makes reservation work properly with multipath, but still requires CAP_SYS_RAWIO - use the Linux IOC_PR_* ioctls (they require CAP_SYS_ADMIN though) - more interestingly, implement reservations directly in QEMU through file system locks or a shared database (e.g. sqlite) Signed-off-by: Paolo Bonzini --- Makefile.objs | 1 + block/file-posix.c | 30 +++++++++++++ docs/pr-manager.rst | 51 ++++++++++++++++++++++ include/scsi/pr-manager.h | 57 ++++++++++++++++++++++++ qapi/block-core.json | 4 ++ scsi/Makefile.objs | 2 + scsi/pr-manager.c | 109 ++++++++++++++++++++++++++++++++++++++++++= ++++ vl.c | 3 +- 8 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 docs/pr-manager.rst create mode 100644 include/scsi/pr-manager.h create mode 100644 scsi/pr-manager.c diff --git a/Makefile.objs b/Makefile.objs index f68aa3b60d..64bebd05db 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -168,6 +168,7 @@ trace-events-subdirs +=3D qapi trace-events-subdirs +=3D accel/tcg trace-events-subdirs +=3D accel/kvm trace-events-subdirs +=3D nbd +trace-events-subdirs +=3D scsi =20 trace-events-files =3D $(SRC_PATH)/trace-events $(trace-events-subdirs:%= =3D$(SRC_PATH)/%/trace-events) =20 diff --git a/block/file-posix.c b/block/file-posix.c index f4de022ae0..47aadbf45d 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -34,6 +34,9 @@ #include "qapi/util.h" #include "qapi/qmp/qstring.h" =20 +#include "scsi/pr-manager.h" +#include "scsi/constants.h" + #if defined(__APPLE__) && (__MACH__) #include #include @@ -156,6 +159,8 @@ typedef struct BDRVRawState { bool page_cache_inconsistent:1; bool has_fallocate; bool needs_alignment; + + PRManager *pr_mgr; } BDRVRawState; =20 typedef struct BDRVRawReopenState { @@ -403,6 +408,11 @@ static QemuOptsList raw_runtime_opts =3D { .type =3D QEMU_OPT_STRING, .help =3D "file locking mode (on/off/auto, default: auto)", }, + { + .name =3D "pr-manager", + .type =3D QEMU_OPT_STRING, + .help =3D "id of persistent reservation manager object (defaul= t: none)", + }, { /* end of list */ } }, }; @@ -414,6 +424,7 @@ static int raw_open_common(BlockDriverState *bs, QDict = *options, QemuOpts *opts; Error *local_err =3D NULL; const char *filename =3D NULL; + const char *str; BlockdevAioOptions aio, aio_default; int fd, ret; struct stat st; @@ -478,6 +489,16 @@ static int raw_open_common(BlockDriverState *bs, QDict= *options, abort(); } =20 + str =3D qemu_opt_get(opts, "pr-manager"); + if (str) { + s->pr_mgr =3D pr_manager_lookup(str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret =3D -EINVAL; + goto fail; + } + } + s->open_flags =3D open_flags; raw_parse_flags(bdrv_flags, &s->open_flags); =20 @@ -2600,6 +2621,15 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *= bs, if (fd_open(bs) < 0) return NULL; =20 + if (req =3D=3D SG_IO && s->pr_mgr) { + struct sg_io_hdr *io_hdr =3D buf; + if (io_hdr->cmdp[0] =3D=3D PERSISTENT_RESERVE_OUT || + io_hdr->cmdp[0] =3D=3D PERSISTENT_RESERVE_IN) { + return pr_manager_execute(s->pr_mgr, bdrv_get_aio_context(bs), + s->fd, io_hdr, cb, opaque); + } + } + acb =3D g_new(RawPosixAIOData, 1); acb->bs =3D bs; acb->aio_type =3D QEMU_AIO_IOCTL; diff --git a/docs/pr-manager.rst b/docs/pr-manager.rst new file mode 100644 index 0000000000..b6089fb57c --- /dev/null +++ b/docs/pr-manager.rst @@ -0,0 +1,51 @@ +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Persistent reservation managers +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +SCSI persistent Reservations allow restricting access to block devices +to specific initiators in a shared storage setup. When implementing +clustering of virtual machines, it is a common requirement for virtual +machines to send persistent reservation SCSI commands. However, +the operating system restricts sending these commands to unprivileged +programs because incorrect usage can disrupt regular operation of the +storage fabric. + +For this reason, QEMU's SCSI passthrough devices, ``scsi-block`` +and ``scsi-generic`` (both are only available on Linux) can delegate +implementation of persistent reservations to a separate object, +the "persistent reservation manager". Only PERSISTENT RESERVE OUT and +PERSISTENT RESERVE IN commands are passed to the persistent reservation +manager object; other commands are processed by QEMU as usual. + +----------------------------------------- +Defining a persistent reservation manager +----------------------------------------- + +A persistent reservation manager is an instance of a subclass of the +"pr-manager" QOM class. + +Right now only one subclass is defined, ``pr-manager-helper``, which +forwards the commands to an external privileged helper program +over Unix sockets. The helper program only allows sending persistent +reservation commands to devices for which QEMU has a file descriptor, +so that QEMU will not be able to effect persistent reservations +unless it has access to both the socket and the device. + +``pr-manager-helper`` has a single string property, ``path``, which +accepts the path to the helper program's Unix socket. For example, +the following command line defines a ``pr-manager-helper`` object and +attaches it to a SCSI passthrough device:: + + $ qemu-system-x86_64 + -device virtio-scsi \ + -object pr-manager-helper,id=3Dhelper0,path=3D/var/run/qemu-pr-h= elper.sock + -drive if=3Dnone,id=3Dhd,driver=3Draw,file.filename=3D/dev/sdb,f= ile.pr-manager=3Dhelper0 + -device scsi-block,drive=3Dhd + +Alternatively, using ``-blockdev``:: + + $ qemu-system-x86_64 + -device virtio-scsi \ + -object pr-manager-helper,id=3Dhelper0,path=3D/var/run/qemu-pr-h= elper.sock + -blockdev node-name=3Dhd,driver=3Draw,file.driver=3Dhost_device,= file.filename=3D/dev/sdb,file.pr-manager=3Dhelper0 + -device scsi-block,drive=3Dhd diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h new file mode 100644 index 0000000000..d523be218f --- /dev/null +++ b/include/scsi/pr-manager.h @@ -0,0 +1,57 @@ +#ifndef PR_MANAGER_H +#define PR_MANAGER_H + +#include "qom/object.h" +#include "qapi/qmp/qdict.h" +#include "qapi/visitor.h" +#include "qom/object_interfaces.h" +#include "block/aio.h" + +#define TYPE_PR_MANAGER "pr-manager" + +#define PR_MANAGER_CLASS(klass) \ + OBJECT_CLASS_CHECK(PRManagerClass, (klass), TYPE_PR_MANAGER) +#define PR_MANAGER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PRManagerClass, (obj), TYPE_PR_MANAGER) +#define PR_MANAGER(obj) \ + OBJECT_CHECK(PRManager, (obj), TYPE_PR_MANAGER) + +struct sg_io_hdr; + +typedef struct PRManager { + /* */ + Object parent; +} PRManager; + +/** + * PRManagerClass: + * @parent_class: the base class + * @run: callback invoked in thread pool context + */ +typedef struct PRManagerClass { + /* */ + ObjectClass parent_class; + + /* */ + int (*run)(PRManager *pr_mgr, int fd, struct sg_io_hdr *hdr); +} PRManagerClass; + +BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, + AioContext *ctx, int fd, + struct sg_io_hdr *hdr, + BlockCompletionFunc *complete, + void *opaque); + +#ifdef CONFIG_LINUX +PRManager *pr_manager_lookup(const char *id, Error **errp); +#else +static inline PRManager *pr_manager_lookup(const char *id, + Error **errp) +{ + /* The classes do not exist at all! */ + error_setg(errp, "No persistent reservation manager with id '%s'", id); + return NULL; +} +#endif + +#endif diff --git a/qapi/block-core.json b/qapi/block-core.json index 833c602150..5ec663ab0d 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2191,6 +2191,9 @@ # Driver specific block device options for the file backend. # # @filename: path to the image file +# @pr-manager: the if for the object that will handle persistent reservat= ions +# for this device (default: forward the commands via SG_IO, +# since 2.11) # @aio: AIO backend (default: threads) (since: 2.8) # @locking: whether to enable file locking. If set to 'auto', only ena= ble # when Open File Descriptor (OFD) locking API is available @@ -2200,6 +2203,7 @@ ## { 'struct': 'BlockdevOptionsFile', 'data': { 'filename': 'str', + '*pr-manager': 'str', '*locking': 'OnOffAuto', '*aio': 'BlockdevAioOptions' } } =20 diff --git a/scsi/Makefile.objs b/scsi/Makefile.objs index 31b82a5a36..5496d2ae6a 100644 --- a/scsi/Makefile.objs +++ b/scsi/Makefile.objs @@ -1 +1,3 @@ block-obj-y +=3D utils.o + +block-obj-$(CONFIG_LINUX) +=3D pr-manager.o diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c new file mode 100644 index 0000000000..e80f8d9b31 --- /dev/null +++ b/scsi/pr-manager.c @@ -0,0 +1,109 @@ +/* + * Persistent reservation manager abstract class + * + * Copyright (c) 2017 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This code is licensed under the LGPL. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "block/aio.h" +#include "block/thread-pool.h" +#include "scsi/pr-manager.h" +#include "scsi/trace.h" + +#include + +typedef struct PRManagerData { + PRManager *pr_mgr; + struct sg_io_hdr *hdr; + int fd; +} PRManagerData; + +static int pr_manager_worker(void *opaque) +{ + PRManagerData *data =3D opaque; + PRManager *pr_mgr =3D data->pr_mgr; + PRManagerClass *pr_mgr_class =3D + PR_MANAGER_GET_CLASS(pr_mgr); + struct sg_io_hdr *hdr =3D data->hdr; + int fd =3D data->fd; + int r; + + g_free(data); + trace_pr_manager_run(fd, hdr->cmdp[0], hdr->cmdp[1]); + + /* The is was taken in pr_manager_execute. */ + r =3D pr_mgr_class->run(pr_mgr, fd, hdr); + object_unref(OBJECT(pr_mgr)); + return r; +} + + +BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, + AioContext *ctx, int fd, + struct sg_io_hdr *hdr, + BlockCompletionFunc *complete, + void *opaque) +{ + PRManagerData *data =3D g_new(PRManagerData, 1); + ThreadPool *pool =3D aio_get_thread_pool(ctx); + + trace_pr_manager_execute(fd, hdr->cmdp[0], hdr->cmdp[1], opaque); + data->pr_mgr =3D pr_mgr; + data->fd =3D fd; + data->hdr =3D hdr; + + /* The matching object_unref is in pr_manager_worker. */ + object_ref(OBJECT(pr_mgr)); + return thread_pool_submit_aio(pool, pr_manager_worker, + data, complete, opaque); +} + +static const TypeInfo pr_manager_info =3D { + .parent =3D TYPE_OBJECT, + .name =3D TYPE_PR_MANAGER, + .class_size =3D sizeof(PRManagerClass), + .abstract =3D true, + .interfaces =3D (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +PRManager *pr_manager_lookup(const char *id, Error **errp) +{ + Object *obj; + PRManager *pr_mgr; + + obj =3D object_resolve_path_component(object_get_objects_root(), id); + if (!obj) { + error_setg(errp, "No persistent reservation manager with id '%s'",= id); + return NULL; + } + + pr_mgr =3D (PRManager *) + object_dynamic_cast(obj, + TYPE_PR_MANAGER); + if (!pr_mgr) { + error_setg(errp, + "Object with id '%s' is not a persistent reservation ma= nager", + id); + return NULL; + } + + return pr_mgr; +} + +static void +pr_manager_register_types(void) +{ + type_register_static(&pr_manager_info); +} + + +type_init(pr_manager_register_types); diff --git a/vl.c b/vl.c index 8e247cc2a2..af0e6576ab 100644 --- a/vl.c +++ b/vl.c @@ -2811,7 +2811,8 @@ static int machine_set_property(void *opaque, */ static bool object_create_initial(const char *type) { - if (g_str_equal(type, "rng-egd")) { + if (g_str_equal(type, "rng-egd") || + g_str_has_prefix(type, "pr-manager-")) { return false; } =20 --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1503408419138839.8305965953024; Tue, 22 Aug 2017 06:26:59 -0700 (PDT) Received: from localhost ([::1]:46044 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk9Cr-0001ic-OO for importer@patchew.org; Tue, 22 Aug 2017 09:26:57 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49768) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk95B-0002pe-C1 for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:02 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk95A-00062U-7R for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:01 -0400 Received: from mx1.redhat.com ([209.132.183.28]:41852) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk94x-0005of-KI; Tue, 22 Aug 2017 09:18:47 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 9ED7180E74; Tue, 22 Aug 2017 13:18:46 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4C71181B49; Tue, 22 Aug 2017 13:18:45 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 9ED7180E74 Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:29 +0200 Message-Id: <20170822131832.20191-8-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Tue, 22 Aug 2017 13:18:46 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 07/10] io: add qio_channel_read/write_all X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" It is pretty common to read a fixed-size buffer from a socket. Add a function that does this, either with multiple reads (on blocking sockets) or by yielding if called from a coroutine. Cc: Daniel P. Berrange Signed-off-by: Paolo Bonzini --- include/io/channel.h | 36 ++++++++++++++++++++++++++++++++++- io/channel.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/include/io/channel.h b/include/io/channel.h index db9bb022a1..9cfb4d081f 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -299,7 +299,7 @@ ssize_t qio_channel_writev(QIOChannel *ioc, Error **errp); =20 /** - * qio_channel_readv: + * qio_channel_read: * @ioc: the channel object * @buf: the memory region to read data into * @buflen: the length of @buf @@ -315,6 +315,23 @@ ssize_t qio_channel_read(QIOChannel *ioc, Error **errp); =20 /** + * qio_channel_read_all: + * @ioc: the channel object + * @buf: the memory region to read data into + * @buflen: the number of bytes to @buf + * @errp: pointer to a NULL-initialized error object + * + * Reads @buflen bytes into @buf, possibly blocking or (if the + * channel is non-blocking) yielding from the current coroutine + * multiple times until the entire content is read. Otherwise + * behaves as qio_channel_read(). + */ +ssize_t coroutine_fn qio_channel_read_all(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp); + +/** * qio_channel_write: * @ioc: the channel object * @buf: the memory regions to send data from @@ -331,6 +348,23 @@ ssize_t qio_channel_write(QIOChannel *ioc, Error **errp); =20 /** + * qio_channel_write_all: + * @ioc: the channel object + * @buf: the memory region to write data into + * @buflen: the number of bytes to @buf + * @errp: pointer to a NULL-initialized error object + * + * Writes @buflen bytes from @buf, possibly blocking or (if the + * channel is non-blocking) yielding from the current coroutine + * multiple times until the entire content is written. Otherwise + * behaves as qio_channel_write(). + */ +ssize_t coroutine_fn qio_channel_write_all(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp); + +/** * qio_channel_set_blocking: * @ioc: the channel object * @enabled: the blocking flag state diff --git a/io/channel.c b/io/channel.c index 1cfb8b33a2..7ab3f4eede 100644 --- a/io/channel.c +++ b/io/channel.c @@ -113,6 +113,60 @@ ssize_t qio_channel_read(QIOChannel *ioc, } =20 =20 +ssize_t qio_channel_read_all(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp) +{ + ssize_t total =3D 0; + while (buflen > 0) { + ssize_t n_read =3D qio_channel_read(ioc, buf, buflen, errp); + + if (n_read =3D=3D QIO_CHANNEL_ERR_BLOCK) { + assert(ioc->ctx); + qio_channel_yield(ioc, G_IO_IN); + continue; + } + if (n_read < 0) { + return n_read; + } + + buf +=3D n_read; + total +=3D n_read; + buflen -=3D n_read; + } + + return total; +} + + +ssize_t qio_channel_write_all(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp) +{ + ssize_t total =3D 0; + while (buflen > 0) { + ssize_t n_written =3D qio_channel_write(ioc, buf, buflen, errp); + + if (n_written =3D=3D QIO_CHANNEL_ERR_BLOCK) { + assert(ioc->ctx); + qio_channel_yield(ioc, G_IO_OUT); + continue; + } + if (n_written < 0) { + return n_written; + } + + buf +=3D n_written; + total +=3D n_written; + buflen -=3D n_written; + } + + return total; +} + + ssize_t qio_channel_write(QIOChannel *ioc, const char *buf, size_t buflen, --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1503408243413469.5120330163637; Tue, 22 Aug 2017 06:24:03 -0700 (PDT) Received: from localhost ([::1]:45961 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk9A1-0007i2-VS for importer@patchew.org; Tue, 22 Aug 2017 09:24:02 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49798) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk95E-0002uB-Te for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk95C-00064E-1j for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:04 -0400 Received: from mx1.redhat.com ([209.132.183.28]:53880) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk950-0005s1-4W; Tue, 22 Aug 2017 09:18:50 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 323FD5F7B2; Tue, 22 Aug 2017 13:18:49 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id F0F6B81B49; Tue, 22 Aug 2017 13:18:46 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 323FD5F7B2 Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:30 +0200 Message-Id: <20170822131832.20191-9-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Tue, 22 Aug 2017 13:18:49 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 08/10] scsi: build qemu-pr-helper X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Introduce a privileged helper to run persistent reservation commands. This lets virtual machines send persistent reservations without using CAP_SYS_RAWIO or out-of-tree patches. The helper uses Unix permissions and SCM_RIGHTS to restrict access to processes that can access its socket and prove that they have an open file descriptor for a raw SCSI device. The next patch will also correct the usage of persistent reservations with multipath devices. It would also be possible to support for Linux's IOC_PR_* ioctls in the future, to support NVMe devices. For now, however, only SCSI is supported. Signed-off-by: Paolo Bonzini --- Makefile | 9 +- configure | 2 +- docs/interop/pr-helper.rst | 78 ++++++ docs/pr-manager.rst | 33 +++ scsi/pr-helper.h | 13 + scsi/qemu-pr-helper.c | 657 +++++++++++++++++++++++++++++++++++++++++= ++++ 6 files changed, 790 insertions(+), 2 deletions(-) create mode 100644 docs/interop/pr-helper.rst create mode 100644 scsi/pr-helper.h create mode 100644 scsi/qemu-pr-helper.c diff --git a/Makefile b/Makefile index 97a58a0f4e..bfd4f69ecd 100644 --- a/Makefile +++ b/Makefile @@ -209,6 +209,9 @@ ifdef BUILD_DOCS DOCS=3Dqemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 DOCS+=3Ddocs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/= interop/qemu-qmp-ref.7 DOCS+=3Ddocs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/in= terop/qemu-ga-ref.7 +ifdef CONFIG_LINUX +DOCS+=3Dscsi/qemu-pr-helper.8 +endif ifdef CONFIG_VIRTFS DOCS+=3Dfsdev/virtfs-proxy-helper.1 endif @@ -384,6 +387,8 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COM= MON_LDADDS) fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-m= arshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) fsdev/virtfs-proxy-helper$(EXESUF): LIBS +=3D -lcap =20 +scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-= obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) + qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@= ") =20 @@ -491,7 +496,7 @@ clean: rm -f *.msi find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]= ' \) -type f -exec rm {} + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.p= od *~ */*~ - rm -f fsdev/*.pod + rm -f fsdev/*.pod scsi/*.pod rm -f qemu-img-cmds.h rm -f ui/shader/*-vert.h ui/shader/*-frag.h @# May not be present in GENERATED_FILES @@ -590,6 +595,7 @@ endif ifdef CONFIG_VIRTFS $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" $(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DATA) scsi/qemu-pr-helper.8 "$(DESTDIR)$(mandir)/man8" endif =20 install-datadir: @@ -718,6 +724,7 @@ qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.te= xi qemu-monitor-info.texi qemu.1: qemu-option-trace.texi qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi +scsi/qemu-pr-helper.8: scsi/qemu-pr-helper.texi qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi qemu-ga.8: qemu-ga.texi =20 diff --git a/configure b/configure index dd73cce62f..772aff18d6 100755 --- a/configure +++ b/configure @@ -6545,7 +6545,7 @@ fi =20 # build tree in object directory in case the source is not in the current = directory DIRS=3D"tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/q= api-schema tests/tcg/xtensa tests/qemu-iotests" -DIRS=3D"$DIRS docs docs/interop fsdev" +DIRS=3D"$DIRS docs docs/interop fsdev scsi" DIRS=3D"$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" DIRS=3D"$DIRS roms/seabios roms/vgabios" DIRS=3D"$DIRS qapi-generated" diff --git a/docs/interop/pr-helper.rst b/docs/interop/pr-helper.rst new file mode 100644 index 0000000000..765174c31f --- /dev/null +++ b/docs/interop/pr-helper.rst @@ -0,0 +1,78 @@ +.. + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Persistent reservation helper protocol +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +QEMU's SCSI passthrough devices, ``scsi-block`` and ``scsi-generic``, +can delegate implementation of persistent reservations to an external +(and typically privilege) program. Persistent Reservations allow +restricting access to block devices to specific initiators in a shared +storage setup. + +For a more detailed reference please refer the the SCSI Primary +Commands standard, specifically the section on Reservations and the +"PERSISTENT RESERVE IN" and "PERSISTENT RESERVE OUT" commands. + +This document describes the socket protocol used between QEMU's +``pr-manager-helper`` object and the external program. + +.. contents:: + +Connection and initialization +----------------------------- + +After connecting to the helper program's socket, the helper starts a simple +feature negotiation process by writing four bytes corresponding to +the features it exposes (``supported_features``). QEMU also writes +four bytes corresponding to the desired features of the helper program +(``requested_features``). + +If a bit is 1 in ``requested_features`` and 0 in ``supported_features``, +the corresponding feature is not supported by the helper and the connection +is closed. On the other hand, it is acceptable for a bit to be 0 in +``requested_features`` and 1 in ``supported_features``; in this case, +he helper will not enable the feature. + +Right now no feature is defined, so the two parts always write four +zero bytes. + +All data transmitted on the socket is big-endian. + +Command format +-------------- + +A command consists of a request and a response. A request consists +of a 16-byte SCSI CDB. A file descriptor must be passed to the helper +together with the SCSI CDB using ancillary data. + +The CDB has the following limitations: + +- the command (stored in the first byte) must be one of 0x5E + (PERSISTENT RESERVE IN) or 0x5F (PERSISTENT RESERVE OUT). + +- the allocation length (stored in bytes 7-8 of the CDB for PERSISTENT + RESERVE IN) or parameter list length (stored in bytes 5-8 of the CDB + for PERSISTENT RESERVE OUT) is limited to 8 KiB. + +For PERSISTENT RESERVE OUT, the parameter list is sent right after the +CDB. + +The helper's reply has the following fixed structure: + +- 4 bytes for the SCSI status + +- 96 bytes for the SCSI sense data + +The sense data is always sent to keep the protocol simple, even though +it is only valid if the SCSI status is CHECK CONDITION (0x02). + +For PERSISTENT RESERVE IN, the data is sent right after the sense +data if the SCSI status is GOOD (0x00). + +It is invalid to send multiple commands concurrently on the same +socket. It is however possible to connect multiple sockets to the +helper and send multiple commands to the helper for one or more +file descriptors. + +If the protocol is violated, the helper closes the socket. diff --git a/docs/pr-manager.rst b/docs/pr-manager.rst index b6089fb57c..7107e59fb8 100644 --- a/docs/pr-manager.rst +++ b/docs/pr-manager.rst @@ -49,3 +49,36 @@ Alternatively, using ``-blockdev``:: -object pr-manager-helper,id=3Dhelper0,path=3D/var/run/qemu-pr-h= elper.sock -blockdev node-name=3Dhd,driver=3Draw,file.driver=3Dhost_device,= file.filename=3D/dev/sdb,file.pr-manager=3Dhelper0 -device scsi-block,drive=3Dhd + +---------------------------------- +Invoking :program:`qemu-pr-helper` +---------------------------------- + +QEMU provides an implementation of the persistent reservation helper, +called :program:`qemu-pr-helper`. The helper should be started as a +system service and supports the following option: + +-d, --daemon run in the background +-q, --quiet decrease verbosity +-f, --pidfile=3Dpath PID file when running as a daemon +-k, --socket=3Dpath path to the socket +-T, --trace=3Dtrace-opts tracing options + +By default, the socket and PID file are placed in the runtime state +directory, for example :file:`/var/run/qemu-pr-helper.sock` and +:file:`/var/run/qemu-pr-helper.pid`. The PID file is not created +unless :option:`-d` is passed too. + +:program:`qemu-pr-helper` can also use the systemd socket activation +protocol. In this case, the systemd socket unit should specify a +Unix stream socket, like this:: + + [Socket] + ListenStream=3D/var/run/qemu-pr-helper.sock + +After connecting to the socket, :program:`qemu-pr-helper`` can optionally = drop +root privileges, except for those capabilities that are needed for +its operation. To do this, add the following options: + +-u, --user=3Duser user to drop privileges to +-g, --group=3Dgroup group to drop privileges to diff --git a/scsi/pr-helper.h b/scsi/pr-helper.h new file mode 100644 index 0000000000..2c7ccc9928 --- /dev/null +++ b/scsi/pr-helper.h @@ -0,0 +1,13 @@ +#ifndef QEMU_PR_HELPER_H +#define QEMU_PR_HELPER_H 1 + +#define PR_HELPER_CDB_SIZE 16 +#define PR_HELPER_SENSE_SIZE 96 +#define PR_HELPER_DATA_SIZE 8192 + +typedef struct PRHelperResponse { + int32_t result; + uint8_t sense[PR_HELPER_SENSE_SIZE]; +} PRHelperResponse; + +#endif diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c new file mode 100644 index 0000000000..d52234af0f --- /dev/null +++ b/scsi/qemu-pr-helper.c @@ -0,0 +1,657 @@ +/* + * Helper to forward persistent reservation commands to libmpathpersist + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include +#include "qapi/error.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "qemu/config-file.h" +#include "qemu/bswap.h" +#include "qemu/log.h" +#include "qemu/systemd.h" +#include "qapi/util.h" +#include "qapi/qmp/qstring.h" +#include "io/channel-socket.h" +#include "trace/control.h" +#include "qemu-version.h" + +#include "block/aio.h" +#include "block/thread-pool.h" + +#include "scsi/constants.h" +#include "scsi/utils.h" +#include "pr-helper.h" +#include +#include +#include + +#ifdef CONFIG_LIBCAP +#include +#endif +#include +#include + + +#define PR_OUT_FIXED_PARAM_SIZE 24 + +static char *socket_path; +static char *pidfile; +static enum { RUNNING, TERMINATE, TERMINATING } state; +static QIOChannelSocket *server_ioc; +static int server_watch; +static int num_active_sockets =3D 1; +static int verbose; + +#ifdef CONFIG_LIBCAP +static int uid =3D -1; +static int gid =3D -1; +#endif + +static void usage(const char *name) +{ + (printf) ( +"Usage: %s [OPTIONS] FILE\n" +"Persistent Reservation helper program for QEMU\n" +"\n" +" -h, --help display this help and exit\n" +" -V, --version output version information and exit\n" +"\n" +" -d, --daemon run in the background\n" +" -f, --pidfile=3DPATH PID file when running as a daemon\n" +" (default '%s')\n" +" -k, --socket=3DPATH path to the unix socket\n" +" (default '%s')\n" +" -T, --trace [[enable=3D]][,events=3D][,file=3D]\n" +" specify tracing options\n" +#ifdef CONFIG_LIBCAP +" -u, --user=3DUSER user to drop privileges to\n" +" -g, --group=3DGROUP group to drop privileges to\n" +#endif +"\n" +QEMU_HELP_BOTTOM "\n" + , name, pidfile, socket_path); +} + +static void version(const char *name) +{ + printf( +"%s " QEMU_VERSION QEMU_PKGVERSION "\n" +"Written by Paolo Bonzini.\n" +"\n" +QEMU_COPYRIGHT "\n" +"This is free software; see the source for copying conditions. There is N= O\n" +"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOS= E.\n" + , name); +} + +/* SG_IO support */ + +typedef struct PRHelperSGIOData { + int fd; + const uint8_t *cdb; + uint8_t *sense; + uint8_t *buf; + int sz; + int dir; +} PRHelperSGIOData; + +static int do_sgio_worker(void *opaque) +{ + PRHelperSGIOData *data =3D opaque; + struct sg_io_hdr io_hdr; + int ret; + int status; + SCSISense sense_code; + + memset(data->sense, 0, PR_HELPER_SENSE_SIZE); + memset(&io_hdr, 0, sizeof(io_hdr)); + io_hdr.interface_id =3D 'S'; + io_hdr.cmd_len =3D PR_HELPER_CDB_SIZE; + io_hdr.cmdp =3D (uint8_t *)data->cdb; + io_hdr.sbp =3D data->sense; + io_hdr.mx_sb_len =3D PR_HELPER_SENSE_SIZE; + io_hdr.timeout =3D 1; + io_hdr.dxfer_direction =3D data->dir; + io_hdr.dxferp =3D (char *)data->buf; + io_hdr.dxfer_len =3D data->sz; + ret =3D ioctl(data->fd, SG_IO, &io_hdr); + status =3D sg_io_sense_from_errno(ret < 0 ? errno : 0, &io_hdr, + &sense_code); + if (status =3D=3D CHECK_CONDITION && + !(io_hdr.driver_status & SG_ERR_DRIVER_SENSE)) { + scsi_build_sense(data->sense, sense_code); + } + + return status; +} + +static int do_sgio(int fd, const uint8_t *cdb, uint8_t *sense, + uint8_t *buf, int sz, int dir) +{ + ThreadPool *pool =3D aio_get_thread_pool(qemu_get_aio_context()); + + PRHelperSGIOData data =3D { + .fd =3D fd, + .cdb =3D cdb, + .sense =3D sense, + .buf =3D buf, + .sz =3D sz, + .dir =3D dir, + }; + + return thread_pool_submit_co(pool, do_sgio_worker, &data); +} + +static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, + uint8_t *data, int sz) +{ + return do_sgio(fd, cdb, sense, data, sz, + SG_DXFER_FROM_DEV); +} + +static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, + const uint8_t *param, int sz) +{ + return do_sgio(fd, cdb, sense, (uint8_t *)param, sz, + SG_DXFER_TO_DEV); +} + +/* Client */ + +typedef struct PRHelperClient { + QIOChannelSocket *ioc; + Coroutine *co; + int fd; + uint8_t data[PR_HELPER_DATA_SIZE]; +} PRHelperClient; + +typedef struct PRHelperRequest { + int fd; + size_t sz; + uint8_t cdb[PR_HELPER_CDB_SIZE]; +} PRHelperRequest; + +static int prh_read(PRHelperClient *client, void *buf, int sz, Error **err= p) +{ + while (sz > 0) { + int *fds =3D NULL; + size_t nfds =3D 0; + int i; + struct iovec iov; + ssize_t n_read; + + iov.iov_base =3D buf; + iov.iov_len =3D sz; + n_read =3D qio_channel_readv_full(QIO_CHANNEL(client->ioc), &iov, = 1, + &fds, &nfds, errp); + + if (n_read =3D=3D QIO_CHANNEL_ERR_BLOCK) { + qio_channel_yield(QIO_CHANNEL(client->ioc), G_IO_IN); + continue; + } + if (n_read <=3D 0) { + return n_read ? n_read : -1; + } + + /* Stash one file descriptor per request. */ + if (nfds) { + for (i =3D 0; i < nfds; i++) { + if (client->fd =3D=3D -1) { + client->fd =3D fds[i++]; + } else { + close(fds[i]); + } + } + g_free(fds); + } + + buf +=3D n_read; + sz -=3D n_read; + } + + return 0; +} + +static int prh_read_request(PRHelperClient *client, PRHelperRequest *req, + PRHelperResponse *resp, Error **errp) +{ + uint32_t sz; + + if (prh_read(client, req->cdb, sizeof(req->cdb), NULL) < 0) { + return -1; + } + + if (client->fd =3D=3D -1) { + error_setg(errp, "No file descriptor in request."); + return -1; + } + + if (req->cdb[0] !=3D PERSISTENT_RESERVE_OUT && + req->cdb[0] !=3D PERSISTENT_RESERVE_IN) { + error_setg(errp, "Invalid CDB, closing socket."); + goto out_close; + } + + sz =3D scsi_cdb_xfer(req->cdb); + if (sz > sizeof(client->data)) { + goto out_close; + } + + if (req->cdb[0] =3D=3D PERSISTENT_RESERVE_OUT) { + if (qio_channel_read_all(QIO_CHANNEL(client->ioc), + (char *)client->data, sz, + errp) < 0) { + goto out_close; + } + if (sz < PR_OUT_FIXED_PARAM_SIZE) { + /* Illegal request, Parameter list length error. Not a fatal + * error, so read the data and do not close the socket. + */ + scsi_build_sense(resp->sense, SENSE_CODE(INVALID_PARAM_LEN)); + resp->result =3D CHECK_CONDITION; + sz =3D 0; + close(client->fd); + client->fd =3D -1; + } + } + + req->fd =3D client->fd; + req->sz =3D sz; + client->fd =3D -1; + return sz; + +out_close: + close(client->fd); + client->fd =3D -1; + return -1; +} + +static int prh_write_response(PRHelperClient *client, PRHelperRequest *req, + PRHelperResponse *resp, Error **errp) +{ + ssize_t r; + + resp->result =3D cpu_to_be32(resp->result); + r =3D qio_channel_write_all(QIO_CHANNEL(client->ioc), + (char *) resp, sizeof(*resp), errp); + if (r < 0) { + return r; + } + + if (req->cdb[0] =3D=3D PERSISTENT_RESERVE_IN && resp->result =3D=3D GO= OD) { + assert(req->sz <=3D sizeof(client->data)); + r =3D qio_channel_write_all(QIO_CHANNEL(client->ioc), + (char *) client->data, + req->sz, errp); + } + return r < 0 ? r : 0; +} + +static void prh_co_entry(void *opaque) +{ + PRHelperClient *client =3D opaque; + Error *local_err =3D NULL; + uint32_t flags; + int r; + + qio_channel_set_blocking(QIO_CHANNEL(client->ioc), + false, NULL); + qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), + qemu_get_aio_context()); + + /* A very simple negotiation for future extensibility. No features + * are defined so write 0. + */ + flags =3D cpu_to_be32(0); + r =3D qio_channel_write_all(QIO_CHANNEL(client->ioc), + (char *) &flags, sizeof(flags), NULL); + if (r < 0) { + goto out; + } + + r =3D qio_channel_read_all(QIO_CHANNEL(client->ioc), + (char *) &flags, sizeof(flags), NULL); + if (be32_to_cpu(flags) !=3D 0 || r < 0) { + goto out; + } + + while (atomic_read(&state) =3D=3D RUNNING) { + PRHelperRequest req; + PRHelperResponse resp; + int sz; + + sz =3D prh_read_request(client, &req, &resp, &local_err); + if (sz < 0) { + break; + } + + if (sz > 0) { + num_active_sockets++; + if (req.cdb[0] =3D=3D PERSISTENT_RESERVE_OUT) { + r =3D do_pr_out(req.fd, req.cdb, resp.sense, client->data,= sz); + } else { + r =3D do_pr_in(req.fd, req.cdb, resp.sense, client->data, = sz); + } + num_active_sockets--; + close(req.fd); + if (r =3D=3D -1) { + break; + } + resp.result =3D r; + } + + if (prh_write_response(client, &req, &resp, &local_err) < 0) { + break; + } + } + + if (local_err) { + if (verbose =3D=3D 0) { + error_free(local_err); + } else { + error_report_err(local_err); + } + } + +out: + qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc)); + object_unref(OBJECT(client->ioc)); + g_free(client); +} + +static gboolean accept_client(QIOChannel *ioc, GIOCondition cond, gpointer= opaque) +{ + QIOChannelSocket *cioc; + PRHelperClient *prh; + + cioc =3D qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; + } + + prh =3D g_new(PRHelperClient, 1); + prh->ioc =3D cioc; + prh->fd =3D -1; + prh->co =3D qemu_coroutine_create(prh_co_entry, prh); + qemu_coroutine_enter(prh->co); + + return TRUE; +} + + +/* + * Check socket parameters compatibility when socket activation is used. + */ +static const char *socket_activation_validate_opts(void) +{ + if (socket_path !=3D NULL) { + return "Unix socket can't be set when using socket activation"; + } + + return NULL; +} + +static void compute_default_paths(void) +{ + socket_path =3D qemu_get_local_state_pathname("run/qemu-pr-helper.sock= "); + pidfile =3D qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); +} + +static void termsig_handler(int signum) +{ + atomic_cmpxchg(&state, RUNNING, TERMINATE); + qemu_notify_event(); +} + +static void close_server_socket(void) +{ + g_assert(server_ioc); + + g_source_remove(server_watch); + server_watch =3D -1; + object_unref(OBJECT(server_ioc)); + num_active_sockets--; +} + +#ifdef CONFIG_LIBCAP +static int drop_privileges(void) +{ + /* clear all capabilities */ + capng_clear(CAPNG_SELECT_BOTH); + + if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, + CAP_SYS_RAWIO) < 0) { + return -1; + } + + /* Change user/group id, retaining the capabilities. Because file des= criptors + * are passed via SCM_RIGHTS, we don't need supplementary groups (and = in + * fact the helper can run as "nobody"). + */ + if (capng_change_id(uid !=3D -1 ? uid : getuid(), + gid !=3D -1 ? gid : getgid(), + CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING)) { + return -1; + } + + return 0; +} +#endif + +int main(int argc, char **argv) +{ + const char *sopt =3D "hVk:fdT:u:g:q"; + struct option lopt[] =3D { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "socket", required_argument, NULL, 'k' }, + { "pidfile", no_argument, NULL, 'f' }, + { "daemon", no_argument, NULL, 'd' }, + { "trace", required_argument, NULL, 'T' }, + { "user", required_argument, NULL, 'u' }, + { "group", required_argument, NULL, 'g' }, + { "quiet", no_argument, NULL, 'q' }, + { NULL, 0, NULL, 0 } + }; + int opt_ind =3D 0; + int quiet =3D 0; + char ch; + Error *local_err =3D NULL; + char *trace_file =3D NULL; + bool daemonize =3D false; + unsigned socket_activation; + + struct sigaction sa_sigterm; + memset(&sa_sigterm, 0, sizeof(sa_sigterm)); + sa_sigterm.sa_handler =3D termsig_handler; + sigaction(SIGTERM, &sa_sigterm, NULL); + sigaction(SIGINT, &sa_sigterm, NULL); + sigaction(SIGHUP, &sa_sigterm, NULL); + + signal(SIGPIPE, SIG_IGN); + + compute_default_paths(); + + module_call_init(MODULE_INIT_TRACE); + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_trace_opts); + qemu_init_exec_dir(argv[0]); + + while ((ch =3D getopt_long(argc, argv, sopt, lopt, &opt_ind)) !=3D -1)= { + switch (ch) { + case 'k': + socket_path =3D optarg; + if (socket_path[0] !=3D '/') { + error_report("socket path must be absolute"); + exit(EXIT_FAILURE); + } + break; + case 'f': + pidfile =3D optarg; + break; +#ifdef CONFIG_LIBCAP + case 'u': { + unsigned long res; + struct passwd *userinfo =3D getpwnam(optarg); + if (userinfo) { + uid =3D userinfo->pw_uid; + } else if (qemu_strtoul(optarg, NULL, 10, &res) =3D=3D 0 && + (uid_t)res =3D=3D res) { + uid =3D res; + } else { + error_report("invalid user '%s'", optarg); + exit(EXIT_FAILURE); + } + break; + } + case 'g': { + unsigned long res; + struct group *groupinfo =3D getgrnam(optarg); + if (groupinfo) { + gid =3D groupinfo->gr_gid; + } else if (qemu_strtoul(optarg, NULL, 10, &res) =3D=3D 0 && + (gid_t)res =3D=3D res) { + gid =3D res; + } else { + error_report("invalid group '%s'", optarg); + exit(EXIT_FAILURE); + } + break; + } +#else + case 'u': + case 'g': + error_report("-%c not supported by this %s", ch, argv[0]); + exit(1); +#endif + case 'd': + daemonize =3D true; + break; + case 'q': + quiet =3D 1; + break; + case 'T': + g_free(trace_file); + trace_file =3D trace_opt_parse(optarg); + break; + case 'V': + version(argv[0]); + exit(0); + break; + case 'h': + usage(argv[0]); + exit(0); + break; + case '?': + error_report("Try `%s --help' for more information.", argv[0]); + exit(EXIT_FAILURE); + } + } + + /* set verbosity */ + verbose =3D !quiet; + + if (!trace_init_backends()) { + exit(1); + } + trace_init_file(trace_file); + qemu_set_log(LOG_TRACE); + +#ifdef CONFIG_MPATH + dm_init(); + multipath_pr_init(); +#endif + + socket_activation =3D check_socket_activation(); + if (socket_activation =3D=3D 0) { + SocketAddress saddr =3D { + .type =3D SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path =3D g_strdup(socket_path) + }; + server_ioc =3D qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(server_ioc, &saddr, &local_err)= < 0) { + object_unref(OBJECT(server_ioc)); + error_report_err(local_err); + return 1; + } + g_free(saddr.u.q_unix.path); + } else { + /* Using socket activation - check user didn't use -p etc. */ + const char *err_msg =3D socket_activation_validate_opts(); + if (err_msg !=3D NULL) { + error_report("%s", err_msg); + exit(EXIT_FAILURE); + } + + /* Can only listen on a single socket. */ + if (socket_activation > 1) { + error_report("%s does not support socket activation with LISTE= N_FDS > 1", + argv[0]); + exit(EXIT_FAILURE); + } + server_ioc =3D qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_F= D, + &local_err); + if (server_ioc =3D=3D NULL) { + error_report("Failed to use socket activation: %s", + error_get_pretty(local_err)); + exit(EXIT_FAILURE); + } + socket_path =3D NULL; + } + + if (qemu_init_main_loop(&local_err)) { + error_report_err(local_err); + exit(EXIT_FAILURE); + } + + server_watch =3D qio_channel_add_watch(QIO_CHANNEL(server_ioc), + G_IO_IN, + accept_client, + NULL, NULL); + +#ifdef CONFIG_LIBCAP + if (drop_privileges() < 0) { + error_report("Failed to drop privileges: %s", strerror(errno)); + exit(EXIT_FAILURE); + } +#endif + + if (daemonize) { + if (daemon(0, 0) < 0) { + error_report("Failed to daemonize: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + state =3D RUNNING; + do { + main_loop_wait(false); + if (state =3D=3D TERMINATE) { + state =3D TERMINATING; + close_server_socket(); + } + } while (num_active_sockets > 0); + + exit(EXIT_SUCCESS); +} --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1503408101569396.3013407519918; Tue, 22 Aug 2017 06:21:41 -0700 (PDT) Received: from localhost ([::1]:45937 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk97k-00055g-3q for importer@patchew.org; Tue, 22 Aug 2017 09:21:40 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49792) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk95D-0002sh-LN for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk95B-00063T-7N for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:03 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46524) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk953-0005vQ-MQ; Tue, 22 Aug 2017 09:18:53 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id AE688C058EBD; Tue, 22 Aug 2017 13:18:52 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8589F81B49; Tue, 22 Aug 2017 13:18:49 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com AE688C058EBD Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:31 +0200 Message-Id: <20170822131832.20191-10-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Tue, 22 Aug 2017 13:18:52 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 09/10] scsi: add multipath support to qemu-pr-helper X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Proper support of persistent reservation for multipath devices requires communication with the multipath daemon, so that the reservation is registered and applied when a path comes up. The device mapper utilities provide a library to do so; this patch makes qemu-pr-helper.c detect multipath devices and, when one is found, delegate the operation to libmpathpersist. Signed-off-by: Paolo Bonzini --- Makefile | 3 + configure | 57 ++++++++- docs/pr-manager.rst | 27 +++++ include/scsi/utils.h | 6 + scsi/qemu-pr-helper.c | 311 ++++++++++++++++++++++++++++++++++++++++++++++= +++- scsi/utils.c | 15 +++ 6 files changed, 414 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index bfd4f69ecd..f1acaad05b 100644 --- a/Makefile +++ b/Makefile @@ -388,6 +388,9 @@ fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-= helper.o fsdev/9p-marshal fsdev/virtfs-proxy-helper$(EXESUF): LIBS +=3D -lcap =20 scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-= obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +ifdef CONFIG_MPATH +scsi/qemu-pr-helper$(EXESUF): LIBS +=3D -ludev -lmultipath -lmpathpersist +endif =20 qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@= ") diff --git a/configure b/configure index 772aff18d6..d3c9371f7c 100755 --- a/configure +++ b/configure @@ -286,6 +286,7 @@ pixman=3D"" sdl=3D"" sdlabi=3D"" virtfs=3D"" +mpath=3D"" vnc=3D"yes" sparse=3D"no" vde=3D"" @@ -948,6 +949,10 @@ for opt do ;; --enable-virtfs) virtfs=3D"yes" ;; + --disable-mpath) mpath=3D"no" + ;; + --enable-mpath) mpath=3D"yes" + ;; --disable-vnc) vnc=3D"no" ;; --enable-vnc) vnc=3D"yes" @@ -1491,6 +1496,7 @@ disabled with --disable-FEATURE, default is enabled i= f available: vnc-png PNG compression for VNC server cocoa Cocoa UI (Mac OS X only) virtfs VirtFS + mpath Multipath persistent reservation passthrough xen xen backend driver support xen-pci-passthrough brlapi BrlAPI (Braile) @@ -3336,6 +3342,29 @@ else fi =20 ########################################## +# libmpathpersist probe + +if test "$mpath" !=3D "no" ; then + cat > $TMPC < +#include +unsigned mpath_mx_alloc_len =3D 1024; +int logsink; +int main(void) { + struct udev *udev =3D udev_new(); + mpath_lib_init(udev); +} +EOF + if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then + mpathpersist=3Dyes + else + mpathpersist=3Dno + fi +else + mpathpersist=3Dno +fi + +########################################## # libcap probe =20 if test "$cap" !=3D "no" ; then @@ -5070,16 +5099,34 @@ if test "$want_tools" =3D "yes" ; then fi fi if test "$softmmu" =3D yes ; then - if test "$virtfs" !=3D no ; then - if test "$cap" =3D yes && test "$linux" =3D yes && test "$attr" =3D ye= s ; then + if test "$linux" =3D yes; then + if test "$virtfs" !=3D no && test "$cap" =3D yes && test "$attr" =3D y= es ; then virtfs=3Dyes tools=3D"$tools fsdev/virtfs-proxy-helper\$(EXESUF)" else if test "$virtfs" =3D yes; then - error_exit "VirtFS is supported only on Linux and requires libcap = devel and libattr devel" + error_exit "VirtFS requires libcap devel and libattr devel" fi virtfs=3Dno fi + if test "$mpath" !=3D no && test "$mpathpersist" =3D yes ; then + mpath=3Dyes + tools=3D"$tools mpath/qemu-mpath-helper\$(EXESUF)" + else + if test "$mpath" =3D yes; then + error_exit "Multipath requires libmpathpersist devel" + fi + mpath=3Dno + fi + else + if test "$virtfs" =3D yes; then + error_exit "VirtFS is supported only on Linux" + fi + virtfs=3Dno + if test "$mpath" =3D yes; then + error_exit "Multipath is supported only on Linux" + fi + mpath=3Dno fi fi =20 @@ -5326,6 +5373,7 @@ echo "Audio drivers $audio_drv_list" echo "Block whitelist (rw) $block_drv_rw_whitelist" echo "Block whitelist (ro) $block_drv_ro_whitelist" echo "VirtFS support $virtfs" +echo "Multipath support $mpath" echo "VNC support $vnc" if test "$vnc" =3D "yes" ; then echo "VNC SASL support $vnc_sasl" @@ -5773,6 +5821,9 @@ fi if test "$virtfs" =3D "yes" ; then echo "CONFIG_VIRTFS=3Dy" >> $config_host_mak fi +if test "$mpath" =3D "yes" ; then + echo "CONFIG_MPATH=3Dy" >> $config_host_mak +fi if test "$vhost_scsi" =3D "yes" ; then echo "CONFIG_VHOST_SCSI=3Dy" >> $config_host_mak fi diff --git a/docs/pr-manager.rst b/docs/pr-manager.rst index 7107e59fb8..9b1de198b1 100644 --- a/docs/pr-manager.rst +++ b/docs/pr-manager.rst @@ -60,6 +60,7 @@ system service and supports the following option: =20 -d, --daemon run in the background -q, --quiet decrease verbosity +-v, --verbose increase verbosity -f, --pidfile=3Dpath PID file when running as a daemon -k, --socket=3Dpath path to the socket -T, --trace=3Dtrace-opts tracing options @@ -82,3 +83,29 @@ its operation. To do this, add the following options: =20 -u, --user=3Duser user to drop privileges to -g, --group=3Dgroup group to drop privileges to + +--------------------------------------------- +Multipath devices and persistent reservations +--------------------------------------------- + +Proper support of persistent reservation for multipath devices requires +communication with the multipath daemon, so that the reservation is +registered and applied when a path is newly discovered or becomes online +again. :command:`qemu-pr-helper` can do this if the ``libmpathpersist`` +library was available on the system at build time. + +As of August 2017, a reservation key must be specified in ``multipath.conf= `` +for ``multipathd`` to check for persistent reservation for newly +discovered paths or reinstated paths. The attribute can be added +to the ``defaults`` section or the ``multipaths`` section; for example:: + + multipaths { + multipath { + wwid XXXXXXXXXXXXXXXX + alias yellow + reservation_key 0x123abc + } + } + +Linking :program:`qemu-pr-helper` to ``libmpathpersist`` does not impede +its usage on regular SCSI devices. diff --git a/include/scsi/utils.h b/include/scsi/utils.h index c12b34f2e5..c626be2c78 100644 --- a/include/scsi/utils.h +++ b/include/scsi/utils.h @@ -44,6 +44,8 @@ extern const struct SCSISense sense_code_LUN_NOT_READY; extern const struct SCSISense sense_code_NO_MEDIUM; /* LUN not ready, medium removal prevented */ extern const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED; +/* Medium Error, Unrecoverable Read Error */ +extern const struct SCSISense sense_code_READ_ERROR; /* Hardware error, internal target failure */ extern const struct SCSISense sense_code_TARGET_FAILURE; /* Illegal request, invalid command operation code */ @@ -80,6 +82,8 @@ extern const struct SCSISense sense_code_CAPACITY_CHANGED; extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM; /* Unit attention, Power on, reset or bus device reset occurred */ extern const struct SCSISense sense_code_RESET; +/* Unit attention, SCSI Bus Reset */ +extern const struct SCSISense sense_code_SCSI_BUS_RESET; /* Unit attention, Medium may have changed*/ extern const struct SCSISense sense_code_MEDIUM_CHANGED; /* Unit attention, Reported LUNs data has changed */ @@ -90,6 +94,8 @@ extern const struct SCSISense sense_code_DEVICE_INTERNAL_= RESET; extern const struct SCSISense sense_code_WRITE_PROTECTED; /* Data Protection, Space Allocation Failed Write Protect */ extern const struct SCSISense sense_code_SPACE_ALLOC_FAILED; +/* Aborted Command, LUN Communication Failure */ +extern const struct SCSISense sense_code_LUN_COMM_FAILURE; =20 #define SENSE_CODE(x) sense_code_ ## x =20 diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index d52234af0f..707bb6127b 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -51,6 +51,14 @@ #include #include =20 +#ifdef CONFIG_MPATH +#define CONTROL_PATH "/dev/mapper/control" +#include +#include "mpath_cmd.h" +#include "mpath_persist.h" +#endif + +QEMU_BUILD_BUG_ON(PR_HELPER_DATA_SIZE > MPATH_MAX_PARAM_LEN); =20 #define PR_OUT_FIXED_PARAM_SIZE 24 =20 @@ -60,6 +68,7 @@ static enum { RUNNING, TERMINATE, TERMINATING } state; static QIOChannelSocket *server_ioc; static int server_watch; static int num_active_sockets =3D 1; +static int noisy; static int verbose; =20 #ifdef CONFIG_LIBCAP @@ -162,9 +171,290 @@ static int do_sgio(int fd, const uint8_t *cdb, uint8_= t *sense, return thread_pool_submit_co(pool, do_sgio_worker, &data); } =20 +/* Device mapper interface */ + +#ifdef CONFIG_MPATH +typedef struct DMData { + struct dm_ioctl dm; + uint8_t data[1024]; +} DMData; + +static int control_fd; + +static void *dm_ioctl(int ioc, struct dm_ioctl *dm) +{ + static DMData d; + memcpy(&d.dm, dm, sizeof(d.dm)); + QEMU_BUILD_BUG_ON(sizeof(d.data) < sizeof(struct dm_target_spec)); + + d.dm.version[0] =3D DM_VERSION_MAJOR; + d.dm.version[1] =3D 0; + d.dm.version[2] =3D 0; + d.dm.data_size =3D 1024; + d.dm.data_start =3D offsetof(DMData, data); + if (ioctl(control_fd, ioc, &d) < 0) { + return NULL; + } + memcpy(dm, &d.dm, sizeof(d.dm)); + return &d.data; +} + +static void *dm_dev_ioctl(int fd, int ioc, struct dm_ioctl *dm) +{ + struct stat st; + int r; + + r =3D fstat(fd, &st); + if (r < 0) { + perror("fstat"); + exit(1); + } + + dm->dev =3D st.st_rdev; + return dm_ioctl(ioc, dm); +} + +static void dm_init(void) +{ + control_fd =3D open(CONTROL_PATH, O_RDWR); + if (control_fd < 0) { + perror("Cannot open " CONTROL_PATH); + exit(1); + } + struct dm_ioctl dm =3D { 0 }; + if (!dm_ioctl(DM_VERSION, &dm)) { + perror("ioctl"); + exit(1); + } + if (dm.version[0] !=3D DM_VERSION_MAJOR) { + fprintf(stderr, "Unsupported device mapper interface"); + exit(1); + } +} + +/* Variables required by libmultipath and libmpathpersist. */ +unsigned mpath_mx_alloc_len =3D PR_HELPER_DATA_SIZE; +int logsink; + +static void multipath_pr_init(void) +{ + static struct udev *udev; + + udev =3D udev_new(); + mpath_lib_init(udev); +} + +static int is_mpath(int fd) +{ + struct dm_ioctl dm =3D { .flags =3D DM_NOFLUSH_FLAG }; + struct dm_target_spec *tgt; + + tgt =3D dm_dev_ioctl(fd, DM_TABLE_STATUS, &dm); + if (!tgt) { + if (errno =3D=3D ENXIO) { + return 0; + } + perror("ioctl"); + exit(EXIT_FAILURE); + } + return !strncmp(tgt->target_type, "multipath", DM_MAX_TYPE_NAME); +} + +static int mpath_reconstruct_sense(int fd, int r, uint8_t *sense) +{ + switch (r) { + case MPATH_PR_SUCCESS: + return GOOD; + case MPATH_PR_SENSE_NOT_READY: + case MPATH_PR_SENSE_MEDIUM_ERROR: + case MPATH_PR_SENSE_HARDWARE_ERROR: + case MPATH_PR_SENSE_ABORTED_COMMAND: + { + /* libmpathpersist ate the exact sense. Try to find it by + * issuing TEST UNIT READY. + */ + uint8_t cdb[6] =3D { TEST_UNIT_READY }; + return do_sgio(fd, cdb, sense, NULL, 0, SG_DXFER_NONE); + } + + case MPATH_PR_SENSE_UNIT_ATTENTION: + /* Congratulations libmpathpersist, you ruined the Unit Attention.= .. + * Return a heavyweight one. + */ + scsi_build_sense(sense, SENSE_CODE(SCSI_BUS_RESET)); + return CHECK_CONDITION; + case MPATH_PR_SENSE_INVALID_OP: + /* Only one valid sense. */ + scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE)); + return CHECK_CONDITION; + case MPATH_PR_ILLEGAL_REQ: + /* Guess. */ + scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); + return CHECK_CONDITION; + case MPATH_PR_NO_SENSE: + scsi_build_sense(sense, SENSE_CODE(NO_SENSE)); + return CHECK_CONDITION; + + case MPATH_PR_RESERV_CONFLICT: + return RESERVATION_CONFLICT; + + case MPATH_PR_OTHER: + default: + scsi_build_sense(sense, SENSE_CODE(LUN_COMM_FAILURE)); + return CHECK_CONDITION; + } +} + +static int multipath_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, + uint8_t *data, int sz) +{ + int rq_servact =3D cdb[1]; + struct prin_resp resp; + size_t written; + int r; + + switch (rq_servact) { + case MPATH_PRIN_RKEY_SA: + case MPATH_PRIN_RRES_SA: + case MPATH_PRIN_RCAP_SA: + break; + case MPATH_PRIN_RFSTAT_SA: + /* Nobody implements it anyway, so bail out. */ + default: + /* Cannot parse any other output. */ + scsi_build_sense(sense, SENSE_CODE(INVALID_FIELD)); + return CHECK_CONDITION; + } + + r =3D mpath_persistent_reserve_in(fd, rq_servact, &resp, noisy, verbos= e); + if (r =3D=3D MPATH_PR_SUCCESS) { + switch (rq_servact) { + case MPATH_PRIN_RKEY_SA: + case MPATH_PRIN_RRES_SA: { + struct prin_readdescr *out =3D &resp.prin_descriptor.prin_read= keys; + stl_be_p(&data[0], out->prgeneration); + stl_be_p(&data[4], out->additional_length); + memcpy(&data[8], out->key_list, MIN(out->additional_length, sz= - 8)); + written =3D MIN(out->additional_length + 8, sz); + break; + } + case MPATH_PRIN_RCAP_SA: { + struct prin_capdescr *out =3D &resp.prin_descriptor.prin_readc= ap; + stw_be_p(&data[0], out->length); + data[2] =3D out->flags[0]; + data[3] =3D out->flags[1]; + stw_be_p(&data[4], out->pr_type_mask); + written =3D MIN(6, sz); + break; + } + default: + scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE)); + return CHECK_CONDITION; + } + assert(written < sz); + memset(data + written, 0, sz - written); + } + + return mpath_reconstruct_sense(fd, r, sense); +} + +static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, + const uint8_t *param, int sz) +{ + int rq_servact =3D cdb[1]; + int rq_scope =3D cdb[2] >> 4; + int rq_type =3D cdb[2] & 0xf; + struct prout_param_descriptor paramp; + char transportids[PR_HELPER_DATA_SIZE]; + int r; + int i, j; + + switch (rq_servact) { + case MPATH_PROUT_REG_SA: + case MPATH_PROUT_RES_SA: + case MPATH_PROUT_REL_SA: + case MPATH_PROUT_CLEAR_SA: + case MPATH_PROUT_PREE_SA: + case MPATH_PROUT_PREE_AB_SA: + case MPATH_PROUT_REG_IGN_SA: + case MPATH_PROUT_REG_MOV_SA: + break; + default: + /* Cannot parse any other input. */ + scsi_build_sense(sense, SENSE_CODE(INVALID_FIELD)); + return CHECK_CONDITION; + } + + /* Convert input data, especially transport IDs, to the structs + * used by libmpathpersist (which, of course, will immediately + * do the opposite). + */ + memset(¶mp, 0, sizeof(paramp)); + memcpy(¶mp.key, ¶m[0], 8); + memcpy(¶mp.sa_key, ¶m[8], 8); + paramp.sa_flags =3D param[10]; + for (i =3D PR_OUT_FIXED_PARAM_SIZE, j =3D 0; i < sz; ) { + struct transportid *id =3D (struct transportid *) &transportids[j]; + int len; + + id->format_code =3D param[i] & 0xc0; + id->protocol_id =3D param[i] & 0x0f; + switch (param[i] & 0xcf) { + case 0: + /* FC transport. */ + memcpy(id->n_port_name, ¶m[i + 8], 8); + j +=3D offsetof(struct transportid, n_port_name[8]); + i +=3D 24; + break; + case 3: + case 0x43: + /* iSCSI transport. */ + len =3D lduw_be_p(¶m[i + 2]); + if (len > 252 || (len & 3)) { + /* For format code 00, the standard says the maximum is 22= 3, + * plus the NUL terminator. For format code 01 there is no + * maximum length, but libmpathpersist ignores the first b= yte + * of id->iscsi_name so our maximum is 252. + */ + goto illegal_req; + } + if (memchr(¶m[i + 4], 0, len) =3D=3D NULL) { + goto illegal_req; + } + memcpy(id->iscsi_name, ¶m[i + 2], len + 2); + j +=3D offsetof(struct transportid, iscsi_name[len + 2]); + i +=3D len + 4; + break; + case 6: + /* SAS transport. */ + memcpy(id->sas_address, ¶m[i + 4], 8); + j +=3D offsetof(struct transportid, sas_address[8]); + i +=3D 24; + break; + default: + illegal_req: + scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); + return CHECK_CONDITION; + } + + paramp.trnptid_list[paramp.num_transportid++] =3D id; + } + + r =3D mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type, + ¶mp, noisy, verbose); + return mpath_reconstruct_sense(fd, r, sense); +} +#endif + static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, uint8_t *data, int sz) { +#ifdef CONFIG_MPATH + if (is_mpath(fd)) { + return multipath_pr_in(fd, cdb, sense, data, sz); + } +#endif + return do_sgio(fd, cdb, sense, data, sz, SG_DXFER_FROM_DEV); } @@ -172,6 +462,12 @@ static int do_pr_in(int fd, const uint8_t *cdb, uint8_= t *sense, static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, const uint8_t *param, int sz) { +#ifdef CONFIG_MPATH + if (is_mpath(fd)) { + return multipath_pr_out(fd, cdb, sense, param, sz); + } +#endif + return do_sgio(fd, cdb, sense, (uint8_t *)param, sz, SG_DXFER_TO_DEV); } @@ -444,6 +740,11 @@ static int drop_privileges(void) CAP_SYS_RAWIO) < 0) { return -1; } + /* For /dev/mapper/control ioctls */ + if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, + CAP_SYS_ADMIN) < 0) { + return -1; + } =20 /* Change user/group id, retaining the capabilities. Because file des= criptors * are passed via SCM_RIGHTS, we don't need supplementary groups (and = in @@ -461,7 +762,7 @@ static int drop_privileges(void) =20 int main(int argc, char **argv) { - const char *sopt =3D "hVk:fdT:u:g:q"; + const char *sopt =3D "hVk:fdT:u:g:vq"; struct option lopt[] =3D { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -471,10 +772,12 @@ int main(int argc, char **argv) { "trace", required_argument, NULL, 'T' }, { "user", required_argument, NULL, 'u' }, { "group", required_argument, NULL, 'g' }, + { "verbose", no_argument, NULL, 'v' }, { "quiet", no_argument, NULL, 'q' }, { NULL, 0, NULL, 0 } }; int opt_ind =3D 0; + int loglevel =3D 1; int quiet =3D 0; char ch; Error *local_err =3D NULL; @@ -551,6 +854,9 @@ int main(int argc, char **argv) case 'q': quiet =3D 1; break; + case 'v': + ++loglevel; + break; case 'T': g_free(trace_file); trace_file =3D trace_opt_parse(optarg); @@ -570,7 +876,8 @@ int main(int argc, char **argv) } =20 /* set verbosity */ - verbose =3D !quiet; + noisy =3D !quiet && (loglevel >=3D 3); + verbose =3D quiet ? 0 : MIN(loglevel, 3); =20 if (!trace_init_backends()) { exit(1); diff --git a/scsi/utils.c b/scsi/utils.c index eedd5f45b4..b5c0e05408 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -116,6 +116,11 @@ const struct SCSISense sense_code_NOT_READY_REMOVAL_PR= EVENTED =3D { .key =3D NOT_READY, .asc =3D 0x53, .ascq =3D 0x02 }; =20 +/* Medium Error, Unrecoverable Read Error */ +const struct SCSISense sense_code_READ_ERROR =3D { + .key =3D MEDIUM_ERROR, .asc =3D 0x11, .ascq =3D 0x00 +}; + /* Hardware error, internal target failure */ const struct SCSISense sense_code_TARGET_FAILURE =3D { .key =3D HARDWARE_ERROR, .asc =3D 0x44, .ascq =3D 0x00 @@ -191,6 +196,11 @@ const struct SCSISense sense_code_OVERLAPPED_COMMANDS = =3D { .key =3D ABORTED_COMMAND, .asc =3D 0x4e, .ascq =3D 0x00 }; =20 +/* Command aborted, LUN Communication Failure */ +const struct SCSISense sense_code_LUN_COMM_FAILURE =3D { + .key =3D ABORTED_COMMAND, .asc =3D 0x08, .ascq =3D 0x00 +}; + /* Unit attention, Capacity data has changed */ const struct SCSISense sense_code_CAPACITY_CHANGED =3D { .key =3D UNIT_ATTENTION, .asc =3D 0x2a, .ascq =3D 0x09 @@ -201,6 +211,11 @@ const struct SCSISense sense_code_RESET =3D { .key =3D UNIT_ATTENTION, .asc =3D 0x29, .ascq =3D 0x00 }; =20 +/* Unit attention, SCSI bus reset */ +const struct SCSISense sense_code_SCSI_BUS_RESET =3D { + .key =3D UNIT_ATTENTION, .asc =3D 0x29, .ascq =3D 0x02 +}; + /* Unit attention, No medium */ const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM =3D { .key =3D UNIT_ATTENTION, .asc =3D 0x3a, .ascq =3D 0x00 --=20 2.13.5 From nobody Mon Apr 29 14:28:37 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1503408694438854.9776442570313; Tue, 22 Aug 2017 06:31:34 -0700 (PDT) Received: from localhost ([::1]:46361 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk9HJ-0005jq-4f for importer@patchew.org; Tue, 22 Aug 2017 09:31:33 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49771) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dk95B-0002ph-DC for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dk95A-00062J-0y for qemu-devel@nongnu.org; Tue, 22 Aug 2017 09:19:01 -0400 Received: from mx1.redhat.com ([209.132.183.28]:50000) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dk955-0005xr-QA; Tue, 22 Aug 2017 09:18:56 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D098F404333; Tue, 22 Aug 2017 13:18:54 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-116-244.ams2.redhat.com [10.36.116.244]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0BB5281B49; Tue, 22 Aug 2017 13:18:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com D098F404333 Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=pbonzini@redhat.com From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 22 Aug 2017 15:18:32 +0200 Message-Id: <20170822131832.20191-11-pbonzini@redhat.com> In-Reply-To: <20170822131832.20191-1-pbonzini@redhat.com> References: <20170822131832.20191-1-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Tue, 22 Aug 2017 13:18:54 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 10/10] scsi: add persistent reservation manager using qemu-pr-helper X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This adds a concrete subclass of pr-manager that talks to qemu-pr-helper. Signed-off-by: Paolo Bonzini --- scsi/Makefile.objs | 2 +- scsi/pr-manager-helper.c | 288 +++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 scsi/pr-manager-helper.c diff --git a/scsi/Makefile.objs b/scsi/Makefile.objs index 5496d2ae6a..4d25e476cf 100644 --- a/scsi/Makefile.objs +++ b/scsi/Makefile.objs @@ -1,3 +1,3 @@ block-obj-y +=3D utils.o =20 -block-obj-$(CONFIG_LINUX) +=3D pr-manager.o +block-obj-$(CONFIG_LINUX) +=3D pr-manager.o pr-manager-helper.o diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c new file mode 100644 index 0000000000..c9d9606696 --- /dev/null +++ b/scsi/pr-manager-helper.c @@ -0,0 +1,288 @@ +/* + * Persistent reservation manager that talks to qemu-mpath-helper + * + * Copyright (c) 2017 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This code is licensed under the LGPL. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "scsi/constants.h" +#include "scsi/pr-manager.h" +#include "scsi/utils.h" +#include "io/channel.h" +#include "io/channel-socket.h" +#include "pr-helper.h" + +#include + +#define PR_MAX_RECONNECT_ATTEMPTS 5 + +#define TYPE_PR_MANAGER_HELPER "pr-manager-helper" + +#define PR_MANAGER_HELPER(obj) \ + INTERFACE_CHECK(PRManagerHelper, (obj), \ + TYPE_PR_MANAGER_HELPER) + +typedef struct PRManagerHelper { + /* */ + PRManager parent; + + char *path; + + QemuMutex lock; + QIOChannel *ioc; +} PRManagerHelper; + +/* Called with lock held. */ +static int pr_manager_helper_read(PRManagerHelper *pr_mgr, + void *buf, int sz, Error **errp) +{ + ssize_t r =3D qio_channel_read_all(pr_mgr->ioc, buf, sz, errp); + + if (r < 0) { + object_unref(OBJECT(pr_mgr->ioc)); + pr_mgr->ioc =3D NULL; + return r; + } + + return r < 0 ? r : 0; +} + +/* Called with lock held. */ +static int pr_manager_helper_write(PRManagerHelper *pr_mgr, + int fd, + const void *buf, int sz, Error **errp) +{ + size_t nfds =3D (fd !=3D -1); + while (sz > 0) { + struct iovec iov; + ssize_t n_written; + + iov.iov_base =3D (void *)buf; + iov.iov_len =3D sz; + n_written =3D qio_channel_writev_full(QIO_CHANNEL(pr_mgr->ioc), &i= ov, 1, + nfds ? &fd : NULL, nfds, errp); + + if (n_written <=3D 0) { + assert(n_written !=3D QIO_CHANNEL_ERR_BLOCK); + object_unref(OBJECT(pr_mgr->ioc)); + pr_mgr->ioc =3D NULL; + return n_written; + } + + nfds =3D 0; + buf +=3D n_written; + sz -=3D n_written; + } + + return 0; +} + +/* Called with lock held. */ +static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, + Error **errp) +{ + uint32_t flags; + + SocketAddress saddr =3D { + .type =3D SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path =3D g_strdup(pr_mgr->path) + }; + QIOChannelSocket *sioc =3D qio_channel_socket_new(); + Error *local_err =3D NULL; + + qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper"); + qio_channel_socket_connect_sync(sioc, + &saddr, + &local_err); + if (local_err) { + object_unref(OBJECT(sioc)); + error_propagate(errp, local_err); + return -ENOTCONN; + } + + qio_channel_set_delay(QIO_CHANNEL(sioc), false); + pr_mgr->ioc =3D QIO_CHANNEL(sioc); + + /* A simple feature negotation protocol, even though there is + * no optional feature right now. + */ + if (pr_manager_helper_read(pr_mgr, &flags, sizeof(flags), errp) < 0) { + return -EINVAL; + } + + flags =3D 0; + if (pr_manager_helper_write(pr_mgr, -1, &flags, sizeof(flags), errp) <= 0) { + return -EINVAL; + } + + return 0; +} + +static int pr_manager_helper_run(PRManager *p, + int fd, struct sg_io_hdr *io_hdr) +{ + PRManagerHelper *pr_mgr =3D PR_MANAGER_HELPER(p); + + uint32_t len; + PRHelperResponse resp; + int ret; + int expected_dir; + int attempts; + uint8_t cdb[PR_HELPER_CDB_SIZE] =3D { 0 }; + + if (!io_hdr->cmd_len || io_hdr->cmd_len > PR_HELPER_CDB_SIZE) { + return -EINVAL; + } + + memcpy(cdb, io_hdr->cmdp, io_hdr->cmd_len); + assert(cdb[0] =3D=3D PERSISTENT_RESERVE_OUT || cdb[0] =3D=3D PERSISTEN= T_RESERVE_IN); + expected_dir =3D + (cdb[0] =3D=3D PERSISTENT_RESERVE_OUT ? SG_DXFER_TO_DEV : SG_DXFER= _FROM_DEV); + if (io_hdr->dxfer_direction !=3D expected_dir) { + return -EINVAL; + } + + len =3D scsi_cdb_xfer(cdb); + if (io_hdr->dxfer_len < len || len > PR_HELPER_DATA_SIZE) { + return -EINVAL; + } + + ret =3D 0; + qemu_mutex_lock(&pr_mgr->lock); + + /* Try to reconnect while sending the CDB. */ + for (attempts =3D 0; attempts < PR_MAX_RECONNECT_ATTEMPTS; attempts++)= { + if (!pr_mgr->ioc) { + if (pr_manager_helper_initialize(pr_mgr, NULL) < 0) { + qemu_mutex_unlock(&pr_mgr->lock); + g_usleep(G_USEC_PER_SEC); + qemu_mutex_lock(&pr_mgr->lock); + } + } + + if (pr_mgr->ioc) { + if (pr_manager_helper_write(pr_mgr, fd, cdb, + ARRAY_SIZE(cdb), NULL) >=3D 0) { + break; + } + } + } + if (attempts =3D=3D PR_MAX_RECONNECT_ATTEMPTS) { + ret =3D -EINVAL; + goto out; + } + + /* After sending the CDB, any communications failure causes the + * command to fail. The failure is transient, retrying the command + * will invoke pr_manager_helper_initialize again. + */ + if (expected_dir =3D=3D SG_DXFER_TO_DEV) { + if (pr_manager_helper_write(pr_mgr, -1, io_hdr->dxferp, + len, NULL) < 0) { + ret =3D -EINVAL; + goto out; + } + } + if (pr_manager_helper_read(pr_mgr, &resp, sizeof(resp), NULL) < 0) { + ret =3D -EINVAL; + goto out; + } + if (expected_dir =3D=3D SG_DXFER_FROM_DEV && resp.result =3D=3D 0) { + if (pr_manager_helper_read(pr_mgr, io_hdr->dxferp, len, NULL) < 0)= { + ret =3D -EINVAL; + goto out; + } + } + + io_hdr->status =3D resp.result; + if (resp.result =3D=3D CHECK_CONDITION) { + io_hdr->driver_status =3D SG_ERR_DRIVER_SENSE; + io_hdr->sb_len_wr =3D MIN(io_hdr->mx_sb_len, PR_HELPER_SENSE_SIZE); + memcpy(io_hdr->sbp, resp.sense, io_hdr->sb_len_wr); + } + +out: + if (ret < 0) { + int sense_len =3D scsi_build_sense(io_hdr->sbp, + SENSE_CODE(LUN_COMM_FAILURE)); + io_hdr->driver_status =3D SG_ERR_DRIVER_SENSE; + io_hdr->sb_len_wr =3D MIN(io_hdr->mx_sb_len, sense_len); + io_hdr->status =3D CHECK_CONDITION; + } + qemu_mutex_unlock(&pr_mgr->lock); + return ret; +} + +static void pr_manager_helper_complete(UserCreatable *uc, Error **errp) +{ + PRManagerHelper *pr_mgr =3D PR_MANAGER_HELPER(uc); + + qemu_mutex_lock(&pr_mgr->lock); + pr_manager_helper_initialize(pr_mgr, errp); + qemu_mutex_unlock(&pr_mgr->lock); +} + +static char *get_path(Object *obj, Error **errp) +{ + PRManagerHelper *pr_mgr =3D PR_MANAGER_HELPER(obj); + + return g_strdup(pr_mgr->path); +} + +static void set_path(Object *obj, const char *str, Error **errp) +{ + PRManagerHelper *pr_mgr =3D PR_MANAGER_HELPER(obj); + + g_free(pr_mgr->path); + pr_mgr->path =3D g_strdup(str); +} + +static void pr_manager_helper_instance_finalize(Object *obj) +{ + PRManagerHelper *pr_mgr =3D PR_MANAGER_HELPER(obj); + + g_free(pr_mgr->path); + object_unref(OBJECT(pr_mgr->ioc)); + qemu_mutex_destroy(&pr_mgr->lock); +} + +static void pr_manager_helper_instance_init(Object *obj) +{ + PRManagerHelper *pr_mgr =3D PR_MANAGER_HELPER(obj); + + qemu_mutex_init(&pr_mgr->lock); +} + +static void pr_manager_helper_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + PRManagerClass *prmgr_klass =3D PR_MANAGER_CLASS(klass); + UserCreatableClass *uc_klass =3D USER_CREATABLE_CLASS(klass); + + object_class_property_add_str(klass, "path", get_path, set_path, + &error_abort); + uc_klass->complete =3D pr_manager_helper_complete; + prmgr_klass->run =3D pr_manager_helper_run; +} + +static const TypeInfo pr_manager_helper_info =3D { + .parent =3D TYPE_PR_MANAGER, + .name =3D TYPE_PR_MANAGER_HELPER, + .instance_size =3D sizeof(PRManagerHelper), + .instance_init =3D pr_manager_helper_instance_init, + .instance_finalize =3D pr_manager_helper_instance_finalize, + .class_init =3D pr_manager_helper_class_init, +}; + +static void pr_manager_helper_register_types(void) +{ + type_register_static(&pr_manager_helper_info); +} + +type_init(pr_manager_helper_register_types); --=20 2.13.5