diff --git a/.gitignore b/.gitignore index 6cefd1b..99836e4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ /qemu-2.10.0-rc3.tar.xz /qemu-2.10.0-rc4.tar.xz /qemu-2.10.0.tar.xz +/qemu-2.10.1.tar.xz diff --git a/0001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch b/0001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch new file mode 100644 index 0000000..a2d6dc1 --- /dev/null +++ b/0001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch @@ -0,0 +1,380 @@ +From: "Daniel P. Berrange" +Date: Wed, 30 Aug 2017 14:53:59 +0100 +Subject: [PATCH] io: add new qio_channel_{readv, writev, read, write}_all + functions + +These functions wait until they are able to read / write the full +requested data buffer(s). + +Reviewed-by: Eric Blake +Signed-off-by: Daniel P. Berrange +--- + include/io/channel.h | 90 +++++++++++++++++++++++++++++++++++++++ + io/channel.c | 94 +++++++++++++++++++++++++++++++++++++++++ + tests/io-channel-helpers.c | 102 ++++----------------------------------------- + 3 files changed, 193 insertions(+), 93 deletions(-) + +diff --git a/include/io/channel.h b/include/io/channel.h +index db9bb022a1..e11a62ea50 100644 +--- a/include/io/channel.h ++++ b/include/io/channel.h +@@ -268,6 +268,58 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, + size_t nfds, + Error **errp); + ++/** ++ * qio_channel_readv_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Read data from the IO channel, storing it in the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully populated with data ++ * before the next one is used. The @niov parameter ++ * specifies the total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be read, yielding from the current coroutine ++ * if required. ++ * ++ * If end-of-file occurs before all requested data ++ * has been read, an error will be reported. ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++int qio_channel_readv_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_writev_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Write data to the IO channel, reading it from the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully sent, before the next ++ * one is used. The @niov parameter specifies the ++ * total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be written, yielding from the current coroutine ++ * if required. ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++int qio_channel_writev_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **erp); ++ + /** + * qio_channel_readv: + * @ioc: the channel object +@@ -330,6 +382,44 @@ ssize_t qio_channel_write(QIOChannel *ioc, + size_t buflen, + Error **errp); + ++/** ++ * 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. If end-of-file ++ * occurs it will return an error rather than a short-read. Otherwise ++ * behaves as qio_channel_read(). ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++int qio_channel_read_all(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp); ++/** ++ * 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(). ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++int qio_channel_write_all(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp); ++ + /** + * qio_channel_set_blocking: + * @ioc: the channel object +diff --git a/io/channel.c b/io/channel.c +index 1cfb8b33a2..5e8c2f0a91 100644 +--- a/io/channel.c ++++ b/io/channel.c +@@ -22,6 +22,7 @@ + #include "io/channel.h" + #include "qapi/error.h" + #include "qemu/main-loop.h" ++#include "qemu/iov.h" + + bool qio_channel_has_feature(QIOChannel *ioc, + QIOChannelFeature feature) +@@ -85,6 +86,79 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, + } + + ++ ++int qio_channel_readv_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ int ret = -1; ++ struct iovec *local_iov = g_new(struct iovec, niov); ++ struct iovec *local_iov_head = local_iov; ++ unsigned int nlocal_iov = niov; ++ ++ nlocal_iov = iov_copy(local_iov, nlocal_iov, ++ iov, niov, ++ 0, iov_size(iov, niov)); ++ ++ while (nlocal_iov > 0) { ++ ssize_t len; ++ len = qio_channel_readv(ioc, local_iov, nlocal_iov, errp); ++ if (len == QIO_CHANNEL_ERR_BLOCK) { ++ qio_channel_wait(ioc, G_IO_IN); ++ continue; ++ } else if (len < 0) { ++ goto cleanup; ++ } else if (len == 0) { ++ error_setg(errp, ++ "Unexpected end-of-file before all bytes were read"); ++ goto cleanup; ++ } ++ ++ iov_discard_front(&local_iov, &nlocal_iov, len); ++ } ++ ++ ret = 0; ++ ++ cleanup: ++ g_free(local_iov_head); ++ return ret; ++} ++ ++int qio_channel_writev_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ int ret = -1; ++ struct iovec *local_iov = g_new(struct iovec, niov); ++ struct iovec *local_iov_head = local_iov; ++ unsigned int nlocal_iov = niov; ++ ++ nlocal_iov = iov_copy(local_iov, nlocal_iov, ++ iov, niov, ++ 0, iov_size(iov, niov)); ++ ++ while (nlocal_iov > 0) { ++ ssize_t len; ++ len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp); ++ if (len == QIO_CHANNEL_ERR_BLOCK) { ++ qio_channel_wait(ioc, G_IO_OUT); ++ continue; ++ } ++ if (len < 0) { ++ goto cleanup; ++ } ++ ++ iov_discard_front(&local_iov, &nlocal_iov, len); ++ } ++ ++ ret = 0; ++ cleanup: ++ g_free(local_iov_head); ++ return ret; ++} ++ + ssize_t qio_channel_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, +@@ -123,6 +197,26 @@ ssize_t qio_channel_write(QIOChannel *ioc, + } + + ++int qio_channel_read_all(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; ++ return qio_channel_readv_all(ioc, &iov, 1, errp); ++} ++ ++ ++int qio_channel_write_all(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; ++ return qio_channel_writev_all(ioc, &iov, 1, errp); ++} ++ ++ + int qio_channel_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c +index 05e5579cf8..5430e1389d 100644 +--- a/tests/io-channel-helpers.c ++++ b/tests/io-channel-helpers.c +@@ -21,6 +21,7 @@ + #include "qemu/osdep.h" + #include "io-channel-helpers.h" + #include "qapi/error.h" ++#include "qemu/iov.h" + + struct QIOChannelTest { + QIOChannel *src; +@@ -37,77 +38,17 @@ struct QIOChannelTest { + }; + + +-static void test_skip_iovec(struct iovec **iov, +- size_t *niov, +- size_t skip, +- struct iovec *old) +-{ +- size_t offset = 0; +- size_t i; +- +- for (i = 0; i < *niov; i++) { +- if (skip < (*iov)[i].iov_len) { +- old->iov_len = (*iov)[i].iov_len; +- old->iov_base = (*iov)[i].iov_base; +- +- (*iov)[i].iov_len -= skip; +- (*iov)[i].iov_base += skip; +- break; +- } else { +- skip -= (*iov)[i].iov_len; +- +- if (i == 0 && old->iov_base) { +- (*iov)[i].iov_len = old->iov_len; +- (*iov)[i].iov_base = old->iov_base; +- old->iov_len = 0; +- old->iov_base = NULL; +- } +- +- offset++; +- } +- } +- +- *iov = *iov + offset; +- *niov -= offset; +-} +- +- + /* This thread sends all data using iovecs */ + static gpointer test_io_thread_writer(gpointer opaque) + { + QIOChannelTest *data = opaque; +- struct iovec *iov = data->inputv; +- size_t niov = data->niov; +- struct iovec old = { 0 }; + + qio_channel_set_blocking(data->src, data->blocking, NULL); + +- while (niov) { +- ssize_t ret; +- ret = qio_channel_writev(data->src, +- iov, +- niov, +- &data->writeerr); +- if (ret == QIO_CHANNEL_ERR_BLOCK) { +- if (data->blocking) { +- error_setg(&data->writeerr, +- "Unexpected I/O blocking"); +- break; +- } else { +- qio_channel_wait(data->src, +- G_IO_OUT); +- continue; +- } +- } else if (ret < 0) { +- break; +- } else if (ret == 0) { +- error_setg(&data->writeerr, +- "Unexpected zero length write"); +- break; +- } +- +- test_skip_iovec(&iov, &niov, ret, &old); +- } ++ qio_channel_writev_all(data->src, ++ data->inputv, ++ data->niov, ++ &data->writeerr); + + return NULL; + } +@@ -117,38 +58,13 @@ static gpointer test_io_thread_writer(gpointer opaque) + static gpointer test_io_thread_reader(gpointer opaque) + { + QIOChannelTest *data = opaque; +- struct iovec *iov = data->outputv; +- size_t niov = data->niov; +- struct iovec old = { 0 }; + + qio_channel_set_blocking(data->dst, data->blocking, NULL); + +- while (niov) { +- ssize_t ret; +- +- ret = qio_channel_readv(data->dst, +- iov, +- niov, +- &data->readerr); +- +- if (ret == QIO_CHANNEL_ERR_BLOCK) { +- if (data->blocking) { +- error_setg(&data->readerr, +- "Unexpected I/O blocking"); +- break; +- } else { +- qio_channel_wait(data->dst, +- G_IO_IN); +- continue; +- } +- } else if (ret < 0) { +- break; +- } else if (ret == 0) { +- break; +- } +- +- test_skip_iovec(&iov, &niov, ret, &old); +- } ++ qio_channel_readv_all(data->dst, ++ data->outputv, ++ data->niov, ++ &data->readerr); + + return NULL; + } diff --git a/0002-io-Yield-rather-than-wait-when-already-in-coroutine.patch b/0002-io-Yield-rather-than-wait-when-already-in-coroutine.patch new file mode 100644 index 0000000..1fc3a1a --- /dev/null +++ b/0002-io-Yield-rather-than-wait-when-already-in-coroutine.patch @@ -0,0 +1,52 @@ +From: Eric Blake +Date: Tue, 5 Sep 2017 14:11:12 -0500 +Subject: [PATCH] io: Yield rather than wait when already in coroutine + +The new qio_channel_{read,write}{,v}_all functions are documented +as yielding until data is available. When used on a blocking +channel, this yield is done via qio_channel_wait() which spawns +a nested event loop under the hood (so it is that secondary loop +which yields as needed); but if we are already in a coroutine (at +which point QIO_CHANNEL_ERR_BLOCK is only possible if we are a +non-blocking channel), we want to yield the current coroutine +instead of spawning a nested event loop. + +Signed-off-by: Eric Blake +Message-Id: <20170905191114.5959-2-eblake@redhat.com> +Acked-by: Daniel P. Berrange +[commit message updated] +Signed-off-by: Eric Blake +--- + io/channel.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/io/channel.c b/io/channel.c +index 5e8c2f0a91..9e62794cab 100644 +--- a/io/channel.c ++++ b/io/channel.c +@@ -105,7 +105,11 @@ int qio_channel_readv_all(QIOChannel *ioc, + ssize_t len; + len = qio_channel_readv(ioc, local_iov, nlocal_iov, errp); + if (len == QIO_CHANNEL_ERR_BLOCK) { +- qio_channel_wait(ioc, G_IO_IN); ++ if (qemu_in_coroutine()) { ++ qio_channel_yield(ioc, G_IO_IN); ++ } else { ++ qio_channel_wait(ioc, G_IO_IN); ++ } + continue; + } else if (len < 0) { + goto cleanup; +@@ -143,7 +147,11 @@ int qio_channel_writev_all(QIOChannel *ioc, + ssize_t len; + len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp); + if (len == QIO_CHANNEL_ERR_BLOCK) { +- qio_channel_wait(ioc, G_IO_OUT); ++ if (qemu_in_coroutine()) { ++ qio_channel_yield(ioc, G_IO_OUT); ++ } else { ++ qio_channel_wait(ioc, G_IO_OUT); ++ } + continue; + } + if (len < 0) { diff --git a/0003-scsi-Refactor-scsi-sense-interpreting-code.patch b/0003-scsi-Refactor-scsi-sense-interpreting-code.patch new file mode 100644 index 0000000..a9d91be --- /dev/null +++ b/0003-scsi-Refactor-scsi-sense-interpreting-code.patch @@ -0,0 +1,190 @@ +From: Fam Zheng +Date: Mon, 21 Aug 2017 22:10:05 +0800 +Subject: [PATCH] scsi: Refactor scsi sense interpreting code + +So that it can be reused outside of iscsi.c. + +Also update MAINTAINERS to include the new files in SCSI section. + +Signed-off-by: Fam Zheng +Message-Id: <20170821141008.19383-2-famz@redhat.com> +Signed-off-by: Paolo Bonzini +--- + MAINTAINERS | 2 ++ + block/iscsi.c | 45 ++++----------------------------------------- + include/scsi/scsi.h | 19 +++++++++++++++++++ + util/Makefile.objs | 1 + + util/scsi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 78 insertions(+), 41 deletions(-) + create mode 100644 include/scsi/scsi.h + create mode 100644 util/scsi.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index ccee28b12d..2a4e5036ae 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -969,7 +969,9 @@ SCSI + M: Paolo Bonzini + S: Supported + F: include/hw/scsi/* ++F: include/scsi/* + F: hw/scsi/* ++F: util/scsi* + F: tests/virtio-scsi-test.c + T: git git://github.com/bonzini/qemu.git scsi-next + +diff --git a/block/iscsi.c b/block/iscsi.c +index d557c99668..4bed63cd6d 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -40,6 +40,7 @@ + #include "qmp-commands.h" + #include "qapi/qmp/qstring.h" + #include "crypto/secret.h" ++#include "scsi/scsi.h" + + #include + #include +@@ -209,47 +210,9 @@ static inline unsigned exp_random(double mean) + + static int iscsi_translate_sense(struct scsi_sense *sense) + { +- int ret; +- +- switch (sense->key) { +- case SCSI_SENSE_NOT_READY: +- return -EBUSY; +- case SCSI_SENSE_DATA_PROTECTION: +- return -EACCES; +- case SCSI_SENSE_COMMAND_ABORTED: +- return -ECANCELED; +- case SCSI_SENSE_ILLEGAL_REQUEST: +- /* Parse ASCQ */ +- break; +- default: +- return -EIO; +- } +- switch (sense->ascq) { +- case SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR: +- case SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE: +- case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB: +- case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST: +- ret = -EINVAL; +- break; +- case SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE: +- ret = -ENOSPC; +- break; +- case SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED: +- ret = -ENOTSUP; +- break; +- case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT: +- case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED: +- case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN: +- ret = -ENOMEDIUM; +- break; +- case SCSI_SENSE_ASCQ_WRITE_PROTECTED: +- ret = -EACCES; +- break; +- default: +- ret = -EIO; +- break; +- } +- return ret; ++ return - scsi_sense_to_errno(sense->key, ++ (sense->ascq & 0xFF00) >> 8, ++ sense->ascq & 0xFF); + } + + /* Called (via iscsi_service) with QemuMutex held. */ +diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h +new file mode 100644 +index 0000000000..f894ace4bf +--- /dev/null ++++ b/include/scsi/scsi.h +@@ -0,0 +1,19 @@ ++/* ++ * SCSI helpers ++ * ++ * Copyright 2017 Red Hat, Inc. ++ * ++ * Authors: ++ * Fam Zheng ++ * ++ * 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; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++#ifndef QEMU_SCSI_H ++#define QEMU_SCSI_H ++ ++int scsi_sense_to_errno(int key, int asc, int ascq); ++ ++#endif +diff --git a/util/Makefile.objs b/util/Makefile.objs +index 50a55ecc75..c9e6c493d3 100644 +--- a/util/Makefile.objs ++++ b/util/Makefile.objs +@@ -45,3 +45,4 @@ util-obj-y += qht.o + util-obj-y += range.o + util-obj-y += stats64.o + util-obj-y += systemd.o ++util-obj-y += scsi.o +diff --git a/util/scsi.c b/util/scsi.c +new file mode 100644 +index 0000000000..a6710799fc +--- /dev/null ++++ b/util/scsi.c +@@ -0,0 +1,52 @@ ++/* ++ * SCSI helpers ++ * ++ * Copyright 2017 Red Hat, Inc. ++ * ++ * Authors: ++ * Fam Zheng ++ * ++ * 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; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "scsi/scsi.h" ++ ++int scsi_sense_to_errno(int key, int asc, int ascq) ++{ ++ switch (key) { ++ case 0x02: /* NOT READY */ ++ return EBUSY; ++ case 0x07: /* DATA PROTECTION */ ++ return EACCES; ++ case 0x0b: /* COMMAND ABORTED */ ++ return ECANCELED; ++ case 0x05: /* ILLEGAL REQUEST */ ++ /* Parse ASCQ */ ++ break; ++ default: ++ return EIO; ++ } ++ switch ((asc << 8) | ascq) { ++ case 0x1a00: /* PARAMETER LIST LENGTH ERROR */ ++ case 0x2000: /* INVALID OPERATION CODE */ ++ case 0x2400: /* INVALID FIELD IN CDB */ ++ case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ ++ return EINVAL; ++ case 0x2100: /* LBA OUT OF RANGE */ ++ return ENOSPC; ++ case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ ++ return ENOTSUP; ++ case 0x3a00: /* MEDIUM NOT PRESENT */ ++ case 0x3a01: /* MEDIUM NOT PRESENT TRAY CLOSED */ ++ case 0x3a02: /* MEDIUM NOT PRESENT TRAY OPEN */ ++ return ENOMEDIUM; ++ case 0x2700: /* WRITE PROTECTED */ ++ return EACCES; ++ default: ++ return EIO; ++ } ++} diff --git a/0004-scsi-Improve-scsi_sense_to_errno.patch b/0004-scsi-Improve-scsi_sense_to_errno.patch new file mode 100644 index 0000000..4156754 --- /dev/null +++ b/0004-scsi-Improve-scsi_sense_to_errno.patch @@ -0,0 +1,57 @@ +From: Fam Zheng +Date: Mon, 21 Aug 2017 22:10:06 +0800 +Subject: [PATCH] scsi: Improve scsi_sense_to_errno + +Tweak the errno mapping to return more accurate/appropriate values. + +Signed-off-by: Fam Zheng +Message-Id: <20170821141008.19383-3-famz@redhat.com> +Signed-off-by: Paolo Bonzini +--- + util/scsi.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/util/scsi.c b/util/scsi.c +index a6710799fc..472eb5bea5 100644 +--- a/util/scsi.c ++++ b/util/scsi.c +@@ -18,13 +18,16 @@ + int scsi_sense_to_errno(int key, int asc, int ascq) + { + switch (key) { +- case 0x02: /* NOT READY */ +- return EBUSY; +- case 0x07: /* DATA PROTECTION */ +- return EACCES; ++ case 0x00: /* NO SENSE */ ++ case 0x01: /* RECOVERED ERROR */ ++ case 0x06: /* UNIT ATTENTION */ ++ /* These sense keys are not errors */ ++ return 0; + case 0x0b: /* COMMAND ABORTED */ + return ECANCELED; ++ case 0x02: /* NOT READY */ + case 0x05: /* ILLEGAL REQUEST */ ++ case 0x07: /* DATA PROTECTION */ + /* Parse ASCQ */ + break; + default: +@@ -37,6 +40,7 @@ int scsi_sense_to_errno(int key, int asc, int ascq) + case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ + return EINVAL; + case 0x2100: /* LBA OUT OF RANGE */ ++ case 0x2707: /* SPACE ALLOC FAILED */ + return ENOSPC; + case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ + return ENOTSUP; +@@ -46,6 +50,10 @@ int scsi_sense_to_errno(int key, int asc, int ascq) + return ENOMEDIUM; + case 0x2700: /* WRITE PROTECTED */ + return EACCES; ++ case 0x0401: /* NOT READY, IN PROGRESS OF BECOMING READY */ ++ return EAGAIN; ++ case 0x0402: /* NOT READY, INITIALIZING COMMAND REQUIRED */ ++ return ENOTCONN; + default: + return EIO; + } diff --git a/0005-scsi-Introduce-scsi_sense_buf_to_errno.patch b/0005-scsi-Introduce-scsi_sense_buf_to_errno.patch new file mode 100644 index 0000000..006c73c --- /dev/null +++ b/0005-scsi-Introduce-scsi_sense_buf_to_errno.patch @@ -0,0 +1,64 @@ +From: Fam Zheng +Date: Mon, 21 Aug 2017 22:10:07 +0800 +Subject: [PATCH] scsi: Introduce scsi_sense_buf_to_errno + +This recognizes the "fixed" and "descriptor" format sense data, extracts +the sense key/asc/ascq fields then converts them to an errno. + +Signed-off-by: Fam Zheng +Message-Id: <20170821141008.19383-4-famz@redhat.com> +Signed-off-by: Paolo Bonzini +--- + include/scsi/scsi.h | 1 + + util/scsi.c | 30 ++++++++++++++++++++++++++++++ + 2 files changed, 31 insertions(+) + +diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h +index f894ace4bf..fe330385d8 100644 +--- a/include/scsi/scsi.h ++++ b/include/scsi/scsi.h +@@ -15,5 +15,6 @@ + #define QEMU_SCSI_H + + int scsi_sense_to_errno(int key, int asc, int ascq); ++int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size); + + #endif +diff --git a/util/scsi.c b/util/scsi.c +index 472eb5bea5..472293d59b 100644 +--- a/util/scsi.c ++++ b/util/scsi.c +@@ -58,3 +58,33 @@ int scsi_sense_to_errno(int key, int asc, int ascq) + return EIO; + } + } ++ ++int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size) ++{ ++ int key, asc, ascq; ++ if (sense_size < 1) { ++ return EIO; ++ } ++ switch (sense[0]) { ++ case 0x70: /* Fixed format sense data. */ ++ if (sense_size < 14) { ++ return EIO; ++ } ++ key = sense[2] & 0xF; ++ asc = sense[12]; ++ ascq = sense[13]; ++ break; ++ case 0x72: /* Descriptor format sense data. */ ++ if (sense_size < 4) { ++ return EIO; ++ } ++ key = sense[1] & 0xF; ++ asc = sense[2]; ++ ascq = sense[3]; ++ break; ++ default: ++ return EIO; ++ break; ++ } ++ return scsi_sense_to_errno(key, asc, ascq); ++} diff --git a/0006-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch b/0006-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch new file mode 100644 index 0000000..eed33b7 --- /dev/null +++ b/0006-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch @@ -0,0 +1,88 @@ +From: Paolo Bonzini +Date: Tue, 22 Aug 2017 09:31:36 +0200 +Subject: [PATCH] scsi: rename scsi_build_sense to scsi_convert_sense +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. + +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Paolo Bonzini +--- + 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 ade31c11f5..fac360e20f 100644 +--- a/hw/scsi/scsi-bus.c ++++ b/hw/scsi/scsi-bus.c +@@ -790,7 +790,7 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) + return 0; + } + +- ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true); ++ ret = scsi_convert_sense(req->sense, req->sense_len, buf, len, true); + + /* + * FIXME: clearing unit attention conditions upon autosense should be done +@@ -811,7 +811,7 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) + + int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed) + { +- return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed); ++ return scsi_convert_sense(dev->sense, dev->sense_len, buf, len, fixed); + } + + void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) +@@ -1531,12 +1531,12 @@ const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { + }; + + /* +- * 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 = scsi_build_sense(NULL, 0, outbuf, r->buflen, +- (req->cmd.buf[1] & 1) == 0); ++ buflen = scsi_convert_sense(NULL, 0, outbuf, r->buflen, ++ (req->cmd.buf[1] & 1) == 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_FAILED; + 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); + + SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, + uint32_t tag, uint32_t lun, void *hba_private); diff --git a/0007-scsi-move-non-emulation-specific-code-to-scsi.patch b/0007-scsi-move-non-emulation-specific-code-to-scsi.patch new file mode 100644 index 0000000..92650e7 --- /dev/null +++ b/0007-scsi-move-non-emulation-specific-code-to-scsi.patch @@ -0,0 +1,1430 @@ +From: Paolo Bonzini +Date: Tue, 22 Aug 2017 07:08:27 +0200 +Subject: [PATCH] scsi: move non-emulation specific code to scsi/ +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +util/scsi.c includes some SCSI code that is shared by block/iscsi.c and +hw/scsi, but the introduction of the persistent reservation helper +will add many more instances of this. There is also include/block/scsi.h, +which actually is not part of the core block layer. + +The persistent reservation manager will also need a home. A scsi/ +directory provides one for both the aforementioned shared code and +the PR manager code. + +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Paolo Bonzini +--- + MAINTAINERS | 7 + + Makefile.objs | 2 +- + block/iscsi.c | 6 +- + 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/scsi.h | 20 -- + include/scsi/utils.h | 119 ++++++++++++ + scsi/Makefile.objs | 1 + + scsi/utils.c | 492 +++++++++++++++++++++++++++++++++++++++++++++++++ + util/Makefile.objs | 1 - + util/scsi.c | 90 --------- + 13 files changed, 626 insertions(+), 613 deletions(-) + delete mode 100644 include/scsi/scsi.h + create mode 100644 include/scsi/utils.h + create mode 100644 scsi/Makefile.objs + create mode 100644 scsi/utils.c + delete mode 100644 util/scsi.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 2a4e5036ae..074da8c976 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1215,6 +1215,13 @@ F: migration/block* + F: include/block/aio.h + T: git git://github.com/stefanha/qemu.git block + ++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 = chardev/ + + block-obj-y += nbd/ + block-obj-y += block.o blockjob.o +-block-obj-y += block/ ++block-obj-y += block/ scsi/ + block-obj-y += qemu-io-cmds.o + block-obj-$(CONFIG_REPLICATION) += replication.o + +diff --git a/block/iscsi.c b/block/iscsi.c +index 4bed63cd6d..40adc3c493 100644 +--- a/block/iscsi.c ++++ b/block/iscsi.c +@@ -40,10 +40,14 @@ + #include "qmp-commands.h" + #include "qapi/qmp/qstring.h" + #include "crypto/secret.h" +-#include "scsi/scsi.h" ++#include "scsi/utils.h" + ++/* Conflict between scsi/utils.h and libiscsi! :( */ ++#define SCSI_XFER_NONE ISCSI_XFER_NONE + #include + #include ++#undef SCSI_XFER_NONE ++QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); + + #ifdef __linux__ + #include +diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c +index fac360e20f..42920d5422 100644 +--- a/hw/scsi/scsi-bus.c ++++ b/hw/scsi/scsi-bus.c +@@ -956,36 +956,6 @@ static int ata_passthrough_16_xfer(SCSIDevice *dev, uint8_t *buf) + return xfer * unit; + } + +-uint32_t scsi_data_cdb_xfer(uint8_t *buf) +-{ +- if ((buf[0] >> 5) == 0 && buf[4] == 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 = scsi_cdb_xfer(buf); +@@ -1298,53 +1268,6 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) + } + } + +-static uint64_t scsi_cmd_lba(SCSICommand *cmd) +-{ +- uint8_t *buf = cmd->buf; +- uint64_t lba; +- +- switch (buf[0] >> 5) { +- case 0: +- lba = ldl_be_p(&buf[0]) & 0x1fffff; +- break; +- case 1: +- case 2: +- case 5: +- lba = ldl_be_p(&buf[2]) & 0xffffffffULL; +- break; +- case 4: +- lba = ldq_be_p(&buf[2]); +- break; +- default: +- lba = -1; +- +- } +- return lba; +-} +- +-int scsi_cdb_length(uint8_t *buf) { +- int cdb_len; +- +- switch (buf[0] >> 5) { +- case 0: +- cdb_len = 6; +- break; +- case 1: +- case 2: +- cdb_len = 10; +- break; +- case 4: +- cdb_len = 16; +- break; +- case 5: +- cdb_len = 12; +- break; +- default: +- cdb_len = -1; +- } +- return cdb_len; +-} +- + int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) + { + int rc; +@@ -1391,326 +1314,6 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense) + } + } + +-/* +- * Predefined sense codes +- */ +- +-/* No sense data available */ +-const struct SCSISense sense_code_NO_SENSE = { +- .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 +-}; +- +-/* LUN not ready, Manual intervention required */ +-const struct SCSISense sense_code_LUN_NOT_READY = { +- .key = NOT_READY, .asc = 0x04, .ascq = 0x03 +-}; +- +-/* LUN not ready, Medium not present */ +-const struct SCSISense sense_code_NO_MEDIUM = { +- .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 +-}; +- +-/* LUN not ready, medium removal prevented */ +-const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = { +- .key = NOT_READY, .asc = 0x53, .ascq = 0x02 +-}; +- +-/* Hardware error, internal target failure */ +-const struct SCSISense sense_code_TARGET_FAILURE = { +- .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 +-}; +- +-/* Illegal request, invalid command operation code */ +-const struct SCSISense sense_code_INVALID_OPCODE = { +- .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 +-}; +- +-/* Illegal request, LBA out of range */ +-const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { +- .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 +-}; +- +-/* Illegal request, Invalid field in CDB */ +-const struct SCSISense sense_code_INVALID_FIELD = { +- .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 +-}; +- +-/* Illegal request, Invalid field in parameter list */ +-const struct SCSISense sense_code_INVALID_PARAM = { +- .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00 +-}; +- +-/* Illegal request, Parameter list length error */ +-const struct SCSISense sense_code_INVALID_PARAM_LEN = { +- .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00 +-}; +- +-/* Illegal request, LUN not supported */ +-const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { +- .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 +-}; +- +-/* Illegal request, Saving parameters not supported */ +-const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { +- .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00 +-}; +- +-/* Illegal request, Incompatible medium installed */ +-const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { +- .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 +-}; +- +-/* Illegal request, medium removal prevented */ +-const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { +- .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 +-}; +- +-/* Illegal request, Invalid Transfer Tag */ +-const struct SCSISense sense_code_INVALID_TAG = { +- .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 +-}; +- +-/* Command aborted, I/O process terminated */ +-const struct SCSISense sense_code_IO_ERROR = { +- .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 +-}; +- +-/* Command aborted, I_T Nexus loss occurred */ +-const struct SCSISense sense_code_I_T_NEXUS_LOSS = { +- .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 +-}; +- +-/* Command aborted, Logical Unit failure */ +-const struct SCSISense sense_code_LUN_FAILURE = { +- .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 +-}; +- +-/* Command aborted, Overlapped Commands Attempted */ +-const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { +- .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 +-}; +- +-/* Unit attention, Capacity data has changed */ +-const struct SCSISense sense_code_CAPACITY_CHANGED = { +- .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 +-}; +- +-/* Unit attention, Power on, reset or bus device reset occurred */ +-const struct SCSISense sense_code_RESET = { +- .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 +-}; +- +-/* Unit attention, No medium */ +-const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { +- .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 +-}; +- +-/* Unit attention, Medium may have changed */ +-const struct SCSISense sense_code_MEDIUM_CHANGED = { +- .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 +-}; +- +-/* Unit attention, Reported LUNs data has changed */ +-const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = { +- .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e +-}; +- +-/* Unit attention, Device internal reset */ +-const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { +- .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 +-}; +- +-/* Data Protection, Write Protected */ +-const struct SCSISense sense_code_WRITE_PROTECTED = { +- .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 +-}; +- +-/* Data Protection, Space Allocation Failed Write Protect */ +-const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { +- .key = DATA_PROTECT, .asc = 0x27, .ascq = 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 == 0) { +- sense.key = NO_SENSE; +- sense.asc = 0; +- sense.ascq = 0; +- } else { +- fixed_in = (in_buf[0] & 2) == 0; +- +- if (fixed == fixed_in) { +- memcpy(buf, in_buf, MIN(len, in_len)); +- return MIN(len, in_len); +- } +- +- if (fixed_in) { +- sense.key = in_buf[2]; +- sense.asc = in_buf[12]; +- sense.ascq = in_buf[13]; +- } else { +- sense.key = in_buf[1]; +- sense.asc = in_buf[2]; +- sense.ascq = in_buf[3]; +- } +- } +- +- memset(buf, 0, len); +- if (fixed) { +- /* Return fixed format sense buffer */ +- buf[0] = 0x70; +- buf[2] = sense.key; +- buf[7] = 10; +- buf[12] = sense.asc; +- buf[13] = sense.ascq; +- return MIN(len, SCSI_SENSE_LEN); +- } else { +- /* Return descriptor format sense buffer */ +- buf[0] = 0x72; +- buf[1] = sense.key; +- buf[2] = sense.asc; +- buf[3] = sense.ascq; +- return 8; +- } +-} +- +-const char *scsi_command_name(uint8_t cmd) +-{ +- static const char *names[] = { +- [ TEST_UNIT_READY ] = "TEST_UNIT_READY", +- [ REWIND ] = "REWIND", +- [ REQUEST_SENSE ] = "REQUEST_SENSE", +- [ FORMAT_UNIT ] = "FORMAT_UNIT", +- [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", +- [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS", +- /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */ +- [ READ_6 ] = "READ_6", +- [ WRITE_6 ] = "WRITE_6", +- [ SET_CAPACITY ] = "SET_CAPACITY", +- [ READ_REVERSE ] = "READ_REVERSE", +- [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS", +- [ SPACE ] = "SPACE", +- [ INQUIRY ] = "INQUIRY", +- [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", +- [ MAINTENANCE_IN ] = "MAINTENANCE_IN", +- [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", +- [ MODE_SELECT ] = "MODE_SELECT", +- [ RESERVE ] = "RESERVE", +- [ RELEASE ] = "RELEASE", +- [ COPY ] = "COPY", +- [ ERASE ] = "ERASE", +- [ MODE_SENSE ] = "MODE_SENSE", +- [ START_STOP ] = "START_STOP/LOAD_UNLOAD", +- /* LOAD_UNLOAD and START_STOP use the same operation code */ +- [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", +- [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", +- [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", +- [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", +- [ READ_10 ] = "READ_10", +- [ WRITE_10 ] = "WRITE_10", +- [ SEEK_10 ] = "SEEK_10/POSITION_TO_ELEMENT", +- /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ +- [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", +- [ VERIFY_10 ] = "VERIFY_10", +- [ SEARCH_HIGH ] = "SEARCH_HIGH", +- [ SEARCH_EQUAL ] = "SEARCH_EQUAL", +- [ SEARCH_LOW ] = "SEARCH_LOW", +- [ SET_LIMITS ] = "SET_LIMITS", +- [ PRE_FETCH ] = "PRE_FETCH/READ_POSITION", +- /* READ_POSITION and PRE_FETCH use the same operation code */ +- [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", +- [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", +- [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE", +- /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */ +- [ MEDIUM_SCAN ] = "MEDIUM_SCAN", +- [ COMPARE ] = "COMPARE", +- [ COPY_VERIFY ] = "COPY_VERIFY", +- [ WRITE_BUFFER ] = "WRITE_BUFFER", +- [ READ_BUFFER ] = "READ_BUFFER", +- [ UPDATE_BLOCK ] = "UPDATE_BLOCK", +- [ READ_LONG_10 ] = "READ_LONG_10", +- [ WRITE_LONG_10 ] = "WRITE_LONG_10", +- [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", +- [ WRITE_SAME_10 ] = "WRITE_SAME_10", +- [ UNMAP ] = "UNMAP", +- [ READ_TOC ] = "READ_TOC", +- [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", +- [ SANITIZE ] = "SANITIZE", +- [ GET_CONFIGURATION ] = "GET_CONFIGURATION", +- [ LOG_SELECT ] = "LOG_SELECT", +- [ LOG_SENSE ] = "LOG_SENSE", +- [ MODE_SELECT_10 ] = "MODE_SELECT_10", +- [ RESERVE_10 ] = "RESERVE_10", +- [ RELEASE_10 ] = "RELEASE_10", +- [ MODE_SENSE_10 ] = "MODE_SENSE_10", +- [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", +- [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", +- [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", +- [ EXTENDED_COPY ] = "EXTENDED_COPY", +- [ ATA_PASSTHROUGH_16 ] = "ATA_PASSTHROUGH_16", +- [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", +- [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", +- [ READ_16 ] = "READ_16", +- [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE", +- [ WRITE_16 ] = "WRITE_16", +- [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", +- [ VERIFY_16 ] = "VERIFY_16", +- [ PRE_FETCH_16 ] = "PRE_FETCH_16", +- [ SYNCHRONIZE_CACHE_16 ] = "SPACE_16/SYNCHRONIZE_CACHE_16", +- /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ +- [ LOCATE_16 ] = "LOCATE_16", +- [ WRITE_SAME_16 ] = "ERASE_16/WRITE_SAME_16", +- /* ERASE_16 and WRITE_SAME_16 use the same operation code */ +- [ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16", +- [ WRITE_LONG_16 ] = "WRITE_LONG_16", +- [ REPORT_LUNS ] = "REPORT_LUNS", +- [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", +- [ MOVE_MEDIUM ] = "MOVE_MEDIUM", +- [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", +- [ READ_12 ] = "READ_12", +- [ WRITE_12 ] = "WRITE_12", +- [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE", +- /* ERASE_12 and GET_PERFORMANCE use the same operation code */ +- [ SERVICE_ACTION_IN_12 ] = "SERVICE_ACTION_IN_12", +- [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", +- [ VERIFY_12 ] = "VERIFY_12", +- [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", +- [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", +- [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", +- [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", +- [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG/SET_STREAMING", +- /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ +- [ READ_CD ] = "READ_CD", +- [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", +- [ READ_DVD_STRUCTURE ] = "READ_DVD_STRUCTURE", +- [ RESERVE_TRACK ] = "RESERVE_TRACK", +- [ SEND_CUE_SHEET ] = "SEND_CUE_SHEET", +- [ SEND_DVD_STRUCTURE ] = "SEND_DVD_STRUCTURE", +- [ SET_CD_SPEED ] = "SET_CD_SPEED", +- [ SET_READ_AHEAD ] = "SET_READ_AHEAD", +- [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", +- [ MECHANISM_STATUS ] = "MECHANISM_STATUS", +- [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", +- [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", +- }; +- +- if (cmd >= ARRAY_SIZE(names) || names[cmd] == 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" + +-#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 + +-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" + + #define MAX_SCSI_DEVS 255 + +-#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; + +-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 + +-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, BlockBackend *blk, + void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated); + void scsi_legacy_handle_cmdline(void); + +-/* +- * 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/scsi.h b/include/scsi/scsi.h +deleted file mode 100644 +index fe330385d8..0000000000 +--- a/include/scsi/scsi.h ++++ /dev/null +@@ -1,20 +0,0 @@ +-/* +- * SCSI helpers +- * +- * Copyright 2017 Red Hat, Inc. +- * +- * Authors: +- * Fam Zheng +- * +- * 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; either version 2 of the License, or (at your option) +- * any later version. +- */ +-#ifndef QEMU_SCSI_H +-#define QEMU_SCSI_H +- +-int scsi_sense_to_errno(int key, int asc, int ascq); +-int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size); +- +-#endif +diff --git a/include/scsi/utils.h b/include/scsi/utils.h +new file mode 100644 +index 0000000000..90bf4dce6e +--- /dev/null ++++ b/include/scsi/utils.h +@@ -0,0 +1,119 @@ ++#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_sense_to_errno(int key, int asc, int ascq); ++int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size); ++ ++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 += utils.o +diff --git a/scsi/utils.c b/scsi/utils.c +new file mode 100644 +index 0000000000..2327e06da0 +--- /dev/null ++++ b/scsi/utils.c +@@ -0,0 +1,492 @@ ++/* ++ * SCSI helpers ++ * ++ * Copyright 2017 Red Hat, Inc. ++ * ++ * Authors: ++ * Fam Zheng ++ * 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; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#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) == 0 && buf[4] == 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 = cmd->buf; ++ uint64_t lba; ++ ++ switch (buf[0] >> 5) { ++ case 0: ++ lba = ldl_be_p(&buf[0]) & 0x1fffff; ++ break; ++ case 1: ++ case 2: ++ case 5: ++ lba = ldl_be_p(&buf[2]) & 0xffffffffULL; ++ break; ++ case 4: ++ lba = ldq_be_p(&buf[2]); ++ break; ++ default: ++ lba = -1; ++ ++ } ++ return lba; ++} ++ ++int scsi_cdb_length(uint8_t *buf) ++{ ++ int cdb_len; ++ ++ switch (buf[0] >> 5) { ++ case 0: ++ cdb_len = 6; ++ break; ++ case 1: ++ case 2: ++ cdb_len = 10; ++ break; ++ case 4: ++ cdb_len = 16; ++ break; ++ case 5: ++ cdb_len = 12; ++ break; ++ default: ++ cdb_len = -1; ++ } ++ return cdb_len; ++} ++ ++/* ++ * Predefined sense codes ++ */ ++ ++/* No sense data available */ ++const struct SCSISense sense_code_NO_SENSE = { ++ .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 ++}; ++ ++/* LUN not ready, Manual intervention required */ ++const struct SCSISense sense_code_LUN_NOT_READY = { ++ .key = NOT_READY, .asc = 0x04, .ascq = 0x03 ++}; ++ ++/* LUN not ready, Medium not present */ ++const struct SCSISense sense_code_NO_MEDIUM = { ++ .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 ++}; ++ ++/* LUN not ready, medium removal prevented */ ++const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = { ++ .key = NOT_READY, .asc = 0x53, .ascq = 0x02 ++}; ++ ++/* Hardware error, internal target failure */ ++const struct SCSISense sense_code_TARGET_FAILURE = { ++ .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 ++}; ++ ++/* Illegal request, invalid command operation code */ ++const struct SCSISense sense_code_INVALID_OPCODE = { ++ .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 ++}; ++ ++/* Illegal request, LBA out of range */ ++const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { ++ .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 ++}; ++ ++/* Illegal request, Invalid field in CDB */ ++const struct SCSISense sense_code_INVALID_FIELD = { ++ .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 ++}; ++ ++/* Illegal request, Invalid field in parameter list */ ++const struct SCSISense sense_code_INVALID_PARAM = { ++ .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00 ++}; ++ ++/* Illegal request, Parameter list length error */ ++const struct SCSISense sense_code_INVALID_PARAM_LEN = { ++ .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00 ++}; ++ ++/* Illegal request, LUN not supported */ ++const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { ++ .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 ++}; ++ ++/* Illegal request, Saving parameters not supported */ ++const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { ++ .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00 ++}; ++ ++/* Illegal request, Incompatible medium installed */ ++const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { ++ .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 ++}; ++ ++/* Illegal request, medium removal prevented */ ++const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { ++ .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 ++}; ++ ++/* Illegal request, Invalid Transfer Tag */ ++const struct SCSISense sense_code_INVALID_TAG = { ++ .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 ++}; ++ ++/* Command aborted, I/O process terminated */ ++const struct SCSISense sense_code_IO_ERROR = { ++ .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 ++}; ++ ++/* Command aborted, I_T Nexus loss occurred */ ++const struct SCSISense sense_code_I_T_NEXUS_LOSS = { ++ .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 ++}; ++ ++/* Command aborted, Logical Unit failure */ ++const struct SCSISense sense_code_LUN_FAILURE = { ++ .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 ++}; ++ ++/* Command aborted, Overlapped Commands Attempted */ ++const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { ++ .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 ++}; ++ ++/* Unit attention, Capacity data has changed */ ++const struct SCSISense sense_code_CAPACITY_CHANGED = { ++ .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 ++}; ++ ++/* Unit attention, Power on, reset or bus device reset occurred */ ++const struct SCSISense sense_code_RESET = { ++ .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 ++}; ++ ++/* Unit attention, No medium */ ++const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { ++ .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 ++}; ++ ++/* Unit attention, Medium may have changed */ ++const struct SCSISense sense_code_MEDIUM_CHANGED = { ++ .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 ++}; ++ ++/* Unit attention, Reported LUNs data has changed */ ++const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = { ++ .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e ++}; ++ ++/* Unit attention, Device internal reset */ ++const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { ++ .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 ++}; ++ ++/* Data Protection, Write Protected */ ++const struct SCSISense sense_code_WRITE_PROTECTED = { ++ .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 ++}; ++ ++/* Data Protection, Space Allocation Failed Write Protect */ ++const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { ++ .key = DATA_PROTECT, .asc = 0x27, .ascq = 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 == 0) { ++ sense.key = NO_SENSE; ++ sense.asc = 0; ++ sense.ascq = 0; ++ } else { ++ fixed_in = (in_buf[0] & 2) == 0; ++ ++ if (fixed == fixed_in) { ++ memcpy(buf, in_buf, MIN(len, in_len)); ++ return MIN(len, in_len); ++ } ++ ++ if (fixed_in) { ++ sense.key = in_buf[2]; ++ sense.asc = in_buf[12]; ++ sense.ascq = in_buf[13]; ++ } else { ++ sense.key = in_buf[1]; ++ sense.asc = in_buf[2]; ++ sense.ascq = in_buf[3]; ++ } ++ } ++ ++ memset(buf, 0, len); ++ if (fixed) { ++ /* Return fixed format sense buffer */ ++ buf[0] = 0x70; ++ buf[2] = sense.key; ++ buf[7] = 10; ++ buf[12] = sense.asc; ++ buf[13] = sense.ascq; ++ return MIN(len, SCSI_SENSE_LEN); ++ } else { ++ /* Return descriptor format sense buffer */ ++ buf[0] = 0x72; ++ buf[1] = sense.key; ++ buf[2] = sense.asc; ++ buf[3] = sense.ascq; ++ return 8; ++ } ++} ++ ++int scsi_sense_to_errno(int key, int asc, int ascq) ++{ ++ switch (key) { ++ case 0x00: /* NO SENSE */ ++ case 0x01: /* RECOVERED ERROR */ ++ case 0x06: /* UNIT ATTENTION */ ++ /* These sense keys are not errors */ ++ return 0; ++ case 0x0b: /* COMMAND ABORTED */ ++ return ECANCELED; ++ case 0x02: /* NOT READY */ ++ case 0x05: /* ILLEGAL REQUEST */ ++ case 0x07: /* DATA PROTECTION */ ++ /* Parse ASCQ */ ++ break; ++ default: ++ return EIO; ++ } ++ switch ((asc << 8) | ascq) { ++ case 0x1a00: /* PARAMETER LIST LENGTH ERROR */ ++ case 0x2000: /* INVALID OPERATION CODE */ ++ case 0x2400: /* INVALID FIELD IN CDB */ ++ case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ ++ return EINVAL; ++ case 0x2100: /* LBA OUT OF RANGE */ ++ case 0x2707: /* SPACE ALLOC FAILED */ ++ return ENOSPC; ++ case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ ++ return ENOTSUP; ++ case 0x3a00: /* MEDIUM NOT PRESENT */ ++ case 0x3a01: /* MEDIUM NOT PRESENT TRAY CLOSED */ ++ case 0x3a02: /* MEDIUM NOT PRESENT TRAY OPEN */ ++ return ENOMEDIUM; ++ case 0x2700: /* WRITE PROTECTED */ ++ return EACCES; ++ case 0x0401: /* NOT READY, IN PROGRESS OF BECOMING READY */ ++ return EAGAIN; ++ case 0x0402: /* NOT READY, INITIALIZING COMMAND REQUIRED */ ++ return ENOTCONN; ++ default: ++ return EIO; ++ } ++} ++ ++int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size) ++{ ++ int key, asc, ascq; ++ if (sense_size < 1) { ++ return EIO; ++ } ++ switch (sense[0]) { ++ case 0x70: /* Fixed format sense data. */ ++ if (sense_size < 14) { ++ return EIO; ++ } ++ key = sense[2] & 0xF; ++ asc = sense[12]; ++ ascq = sense[13]; ++ break; ++ case 0x72: /* Descriptor format sense data. */ ++ if (sense_size < 4) { ++ return EIO; ++ } ++ key = sense[1] & 0xF; ++ asc = sense[2]; ++ ascq = sense[3]; ++ break; ++ default: ++ return EIO; ++ break; ++ } ++ return scsi_sense_to_errno(key, asc, ascq); ++} ++ ++const char *scsi_command_name(uint8_t cmd) ++{ ++ static const char *names[] = { ++ [ TEST_UNIT_READY ] = "TEST_UNIT_READY", ++ [ REWIND ] = "REWIND", ++ [ REQUEST_SENSE ] = "REQUEST_SENSE", ++ [ FORMAT_UNIT ] = "FORMAT_UNIT", ++ [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", ++ [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS", ++ /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */ ++ [ READ_6 ] = "READ_6", ++ [ WRITE_6 ] = "WRITE_6", ++ [ SET_CAPACITY ] = "SET_CAPACITY", ++ [ READ_REVERSE ] = "READ_REVERSE", ++ [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS", ++ [ SPACE ] = "SPACE", ++ [ INQUIRY ] = "INQUIRY", ++ [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", ++ [ MAINTENANCE_IN ] = "MAINTENANCE_IN", ++ [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", ++ [ MODE_SELECT ] = "MODE_SELECT", ++ [ RESERVE ] = "RESERVE", ++ [ RELEASE ] = "RELEASE", ++ [ COPY ] = "COPY", ++ [ ERASE ] = "ERASE", ++ [ MODE_SENSE ] = "MODE_SENSE", ++ [ START_STOP ] = "START_STOP/LOAD_UNLOAD", ++ /* LOAD_UNLOAD and START_STOP use the same operation code */ ++ [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", ++ [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", ++ [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", ++ [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", ++ [ READ_10 ] = "READ_10", ++ [ WRITE_10 ] = "WRITE_10", ++ [ SEEK_10 ] = "SEEK_10/POSITION_TO_ELEMENT", ++ /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ ++ [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", ++ [ VERIFY_10 ] = "VERIFY_10", ++ [ SEARCH_HIGH ] = "SEARCH_HIGH", ++ [ SEARCH_EQUAL ] = "SEARCH_EQUAL", ++ [ SEARCH_LOW ] = "SEARCH_LOW", ++ [ SET_LIMITS ] = "SET_LIMITS", ++ [ PRE_FETCH ] = "PRE_FETCH/READ_POSITION", ++ /* READ_POSITION and PRE_FETCH use the same operation code */ ++ [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", ++ [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", ++ [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE", ++ /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */ ++ [ MEDIUM_SCAN ] = "MEDIUM_SCAN", ++ [ COMPARE ] = "COMPARE", ++ [ COPY_VERIFY ] = "COPY_VERIFY", ++ [ WRITE_BUFFER ] = "WRITE_BUFFER", ++ [ READ_BUFFER ] = "READ_BUFFER", ++ [ UPDATE_BLOCK ] = "UPDATE_BLOCK", ++ [ READ_LONG_10 ] = "READ_LONG_10", ++ [ WRITE_LONG_10 ] = "WRITE_LONG_10", ++ [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", ++ [ WRITE_SAME_10 ] = "WRITE_SAME_10", ++ [ UNMAP ] = "UNMAP", ++ [ READ_TOC ] = "READ_TOC", ++ [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", ++ [ SANITIZE ] = "SANITIZE", ++ [ GET_CONFIGURATION ] = "GET_CONFIGURATION", ++ [ LOG_SELECT ] = "LOG_SELECT", ++ [ LOG_SENSE ] = "LOG_SENSE", ++ [ MODE_SELECT_10 ] = "MODE_SELECT_10", ++ [ RESERVE_10 ] = "RESERVE_10", ++ [ RELEASE_10 ] = "RELEASE_10", ++ [ MODE_SENSE_10 ] = "MODE_SENSE_10", ++ [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", ++ [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", ++ [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", ++ [ EXTENDED_COPY ] = "EXTENDED_COPY", ++ [ ATA_PASSTHROUGH_16 ] = "ATA_PASSTHROUGH_16", ++ [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", ++ [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", ++ [ READ_16 ] = "READ_16", ++ [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE", ++ [ WRITE_16 ] = "WRITE_16", ++ [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", ++ [ VERIFY_16 ] = "VERIFY_16", ++ [ PRE_FETCH_16 ] = "PRE_FETCH_16", ++ [ SYNCHRONIZE_CACHE_16 ] = "SPACE_16/SYNCHRONIZE_CACHE_16", ++ /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ ++ [ LOCATE_16 ] = "LOCATE_16", ++ [ WRITE_SAME_16 ] = "ERASE_16/WRITE_SAME_16", ++ /* ERASE_16 and WRITE_SAME_16 use the same operation code */ ++ [ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16", ++ [ WRITE_LONG_16 ] = "WRITE_LONG_16", ++ [ REPORT_LUNS ] = "REPORT_LUNS", ++ [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", ++ [ MOVE_MEDIUM ] = "MOVE_MEDIUM", ++ [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", ++ [ READ_12 ] = "READ_12", ++ [ WRITE_12 ] = "WRITE_12", ++ [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE", ++ /* ERASE_12 and GET_PERFORMANCE use the same operation code */ ++ [ SERVICE_ACTION_IN_12 ] = "SERVICE_ACTION_IN_12", ++ [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", ++ [ VERIFY_12 ] = "VERIFY_12", ++ [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", ++ [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", ++ [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", ++ [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", ++ [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG/SET_STREAMING", ++ /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ ++ [ READ_CD ] = "READ_CD", ++ [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", ++ [ READ_DVD_STRUCTURE ] = "READ_DVD_STRUCTURE", ++ [ RESERVE_TRACK ] = "RESERVE_TRACK", ++ [ SEND_CUE_SHEET ] = "SEND_CUE_SHEET", ++ [ SEND_DVD_STRUCTURE ] = "SEND_DVD_STRUCTURE", ++ [ SET_CD_SPEED ] = "SET_CD_SPEED", ++ [ SET_READ_AHEAD ] = "SET_READ_AHEAD", ++ [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", ++ [ MECHANISM_STATUS ] = "MECHANISM_STATUS", ++ [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", ++ [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", ++ }; ++ ++ if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) { ++ return "*UNKNOWN*"; ++ } ++ return names[cmd]; ++} +diff --git a/util/Makefile.objs b/util/Makefile.objs +index c9e6c493d3..50a55ecc75 100644 +--- a/util/Makefile.objs ++++ b/util/Makefile.objs +@@ -45,4 +45,3 @@ util-obj-y += qht.o + util-obj-y += range.o + util-obj-y += stats64.o + util-obj-y += systemd.o +-util-obj-y += scsi.o +diff --git a/util/scsi.c b/util/scsi.c +deleted file mode 100644 +index 472293d59b..0000000000 +--- a/util/scsi.c ++++ /dev/null +@@ -1,90 +0,0 @@ +-/* +- * SCSI helpers +- * +- * Copyright 2017 Red Hat, Inc. +- * +- * Authors: +- * Fam Zheng +- * +- * 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; either version 2 of the License, or (at your option) +- * any later version. +- */ +- +-#include "qemu/osdep.h" +-#include "scsi/scsi.h" +- +-int scsi_sense_to_errno(int key, int asc, int ascq) +-{ +- switch (key) { +- case 0x00: /* NO SENSE */ +- case 0x01: /* RECOVERED ERROR */ +- case 0x06: /* UNIT ATTENTION */ +- /* These sense keys are not errors */ +- return 0; +- case 0x0b: /* COMMAND ABORTED */ +- return ECANCELED; +- case 0x02: /* NOT READY */ +- case 0x05: /* ILLEGAL REQUEST */ +- case 0x07: /* DATA PROTECTION */ +- /* Parse ASCQ */ +- break; +- default: +- return EIO; +- } +- switch ((asc << 8) | ascq) { +- case 0x1a00: /* PARAMETER LIST LENGTH ERROR */ +- case 0x2000: /* INVALID OPERATION CODE */ +- case 0x2400: /* INVALID FIELD IN CDB */ +- case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ +- return EINVAL; +- case 0x2100: /* LBA OUT OF RANGE */ +- case 0x2707: /* SPACE ALLOC FAILED */ +- return ENOSPC; +- case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ +- return ENOTSUP; +- case 0x3a00: /* MEDIUM NOT PRESENT */ +- case 0x3a01: /* MEDIUM NOT PRESENT TRAY CLOSED */ +- case 0x3a02: /* MEDIUM NOT PRESENT TRAY OPEN */ +- return ENOMEDIUM; +- case 0x2700: /* WRITE PROTECTED */ +- return EACCES; +- case 0x0401: /* NOT READY, IN PROGRESS OF BECOMING READY */ +- return EAGAIN; +- case 0x0402: /* NOT READY, INITIALIZING COMMAND REQUIRED */ +- return ENOTCONN; +- default: +- return EIO; +- } +-} +- +-int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size) +-{ +- int key, asc, ascq; +- if (sense_size < 1) { +- return EIO; +- } +- switch (sense[0]) { +- case 0x70: /* Fixed format sense data. */ +- if (sense_size < 14) { +- return EIO; +- } +- key = sense[2] & 0xF; +- asc = sense[12]; +- ascq = sense[13]; +- break; +- case 0x72: /* Descriptor format sense data. */ +- if (sense_size < 4) { +- return EIO; +- } +- key = sense[1] & 0xF; +- asc = sense[2]; +- ascq = sense[3]; +- break; +- default: +- return EIO; +- break; +- } +- return scsi_sense_to_errno(key, asc, ascq); +-} diff --git a/0008-scsi-introduce-scsi_build_sense.patch b/0008-scsi-introduce-scsi_build_sense.patch new file mode 100644 index 0000000..e7ce4e3 --- /dev/null +++ b/0008-scsi-introduce-scsi_build_sense.patch @@ -0,0 +1,73 @@ +From: Paolo Bonzini +Date: Tue, 22 Aug 2017 09:42:59 +0200 +Subject: [PATCH] scsi: introduce scsi_build_sense +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Move more knowledge of sense data format out of hw/scsi/scsi-bus.c +for reusability. + +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Paolo Bonzini +--- + 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 42920d5422..652ab046ab 100644 +--- a/hw/scsi/scsi-bus.c ++++ b/hw/scsi/scsi-bus.c +@@ -818,13 +818,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] = 0x70; +- req->sense[2] = sense.key; +- req->sense[7] = 10; +- req->sense[12] = sense.asc; +- req->sense[13] = sense.ascq; +- req->sense_len = 18; ++ req->sense_len = scsi_build_sense(req->sense, sense); + } + + static void scsi_req_enqueue_internal(SCSIRequest *req) +diff --git a/include/scsi/utils.h b/include/scsi/utils.h +index 90bf4dce6e..b49392d841 100644 +--- a/include/scsi/utils.h ++++ b/include/scsi/utils.h +@@ -30,6 +30,8 @@ typedef struct SCSISense { + uint8_t ascq; + } SCSISense; + ++int scsi_build_sense(uint8_t *buf, SCSISense sense); ++ + /* + * Predefined sense codes + */ +diff --git a/scsi/utils.c b/scsi/utils.c +index 2327e06da0..89d9167d9d 100644 +--- a/scsi/utils.c ++++ b/scsi/utils.c +@@ -96,6 +96,17 @@ int scsi_cdb_length(uint8_t *buf) + return cdb_len; + } + ++int scsi_build_sense(uint8_t *buf, SCSISense sense) ++{ ++ memset(buf, 0, 18); ++ buf[0] = 0x70; ++ buf[2] = sense.key; ++ buf[7] = 10; ++ buf[12] = sense.asc; ++ buf[13] = sense.ascq; ++ return 18; ++} ++ + /* + * Predefined sense codes + */ diff --git a/0009-scsi-introduce-sg_io_sense_from_errno.patch b/0009-scsi-introduce-sg_io_sense_from_errno.patch new file mode 100644 index 0000000..350084a --- /dev/null +++ b/0009-scsi-introduce-sg_io_sense_from_errno.patch @@ -0,0 +1,133 @@ +From: Paolo Bonzini +Date: Tue, 22 Aug 2017 09:43:14 +0200 +Subject: [PATCH] scsi: introduce sg_io_sense_from_errno + +Move more knowledge of SG_IO out of hw/scsi/scsi-generic.c, for +reusability. + +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Paolo Bonzini +--- + 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; + + assert(r->req.aiocb == NULL); + +@@ -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 = r->io_header.sb_len_wr; +- } +- +- if (ret != 0) { +- switch (ret) { +- case -EDOM: +- status = TASK_SET_FULL; +- break; +- case -ENOMEM: +- status = CHECK_CONDITION; +- scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); +- break; +- default: +- status = CHECK_CONDITION; +- scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); +- break; +- } +- } else { +- if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || +- r->io_header.host_status == SG_ERR_DID_BUS_BUSY || +- r->io_header.host_status == SG_ERR_DID_TIME_OUT || +- (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { +- status = BUSY; +- BADF("Driver Timeout\n"); +- } else if (r->io_header.host_status) { +- status = CHECK_CONDITION; +- scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); +- } else if (r->io_header.status) { +- status = r->io_header.status; +- } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { +- status = CHECK_CONDITION; ++ status = sg_io_sense_from_errno(-ret, &r->io_header, &sense); ++ if (status == CHECK_CONDITION) { ++ if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { ++ r->req.sense_len = r->io_header.sb_len_wr; + } else { +- status = GOOD; ++ scsi_req_build_sense(&r->req, sense); + } + } ++ + DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", + r, r->req.tag, status); + +diff --git a/include/scsi/utils.h b/include/scsi/utils.h +index b49392d841..d301b31768 100644 +--- a/include/scsi/utils.h ++++ b/include/scsi/utils.h +@@ -116,6 +116,9 @@ int scsi_cdb_length(uint8_t *buf); + #define SG_ERR_DID_TIME_OUT 0x03 + + #define SG_ERR_DRIVER_SENSE 0x08 ++ ++int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr, ++ SCSISense *sense); + #endif + + #endif +diff --git a/scsi/utils.c b/scsi/utils.c +index 89d9167d9d..6ee9f4095b 100644 +--- a/scsi/utils.c ++++ b/scsi/utils.c +@@ -501,3 +501,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 != 0) { ++ switch (errno_value) { ++ case EDOM: ++ return TASK_SET_FULL; ++ case ENOMEM: ++ *sense = SENSE_CODE(TARGET_FAILURE); ++ return CHECK_CONDITION; ++ default: ++ *sense = SENSE_CODE(IO_ERROR); ++ return CHECK_CONDITION; ++ } ++ } else { ++ if (io_hdr->host_status == SG_ERR_DID_NO_CONNECT || ++ io_hdr->host_status == SG_ERR_DID_BUS_BUSY || ++ io_hdr->host_status == SG_ERR_DID_TIME_OUT || ++ (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT)) { ++ return BUSY; ++ } else if (io_hdr->host_status) { ++ *sense = 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 diff --git a/0010-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch b/0010-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch new file mode 100644 index 0000000..8bdc71c --- /dev/null +++ b/0010-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch @@ -0,0 +1,884 @@ +From: Paolo Bonzini +Date: Tue, 22 Aug 2017 09:23:55 +0200 +Subject: [PATCH] scsi: move block/scsi.h to include/scsi/constants.h +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Complete the transition by renaming this header, which was +shared by block/iscsi.c and the SCSI emulation code. + +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Paolo Bonzini +--- + 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/block/scsi.h | 314 ---------------------------------------- + include/hw/ide/internal.h | 2 +- + include/scsi/constants.h | 314 ++++++++++++++++++++++++++++++++++++++++ + scsi/utils.c | 2 +- + tests/virtio-scsi-test.c | 2 +- + 18 files changed, 330 insertions(+), 330 deletions(-) + delete mode 100644 include/block/scsi.h + create mode 100644 include/scsi/constants.h + +diff --git a/block/iscsi.c b/block/iscsi.c +index 40adc3c493..c4586be720 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" + + #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 652ab046ab..977f7bce1f 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__); } while (0) + do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) + + #include +-#include "block/scsi.h" ++#include "scsi/constants.h" + + #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-dataplane.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" + +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" + +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" + + /* --------------------------------------------------------------------- */ + +diff --git a/include/block/scsi.h b/include/block/scsi.h +deleted file mode 100644 +index a141dd71f8..0000000000 +--- a/include/block/scsi.h ++++ /dev/null +@@ -1,314 +0,0 @@ +-/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- +- The GNU C Library is free software; you can redistribute it and/or +- modify it under the terms of the GNU Lesser General Public +- License as published by the Free Software Foundation; either +- version 2.1 of the License, or (at your option) any later version. +- +- The GNU C Library 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 +- Lesser General Public License for more details. +- +- You should have received a copy of the GNU Lesser General Public +- License along with this library; if not, see . +-*/ +- +-/* +- * This header file contains public constants and structures used by +- * the scsi code for linux. +- */ +- +-#ifndef BLOCK_SCSI_H +-#define BLOCK_SCSI_H +- +-/* +- * SCSI opcodes +- */ +- +-#define TEST_UNIT_READY 0x00 +-#define REWIND 0x01 +-#define REQUEST_SENSE 0x03 +-#define FORMAT_UNIT 0x04 +-#define READ_BLOCK_LIMITS 0x05 +-#define INITIALIZE_ELEMENT_STATUS 0x07 +-#define REASSIGN_BLOCKS 0x07 +-#define READ_6 0x08 +-#define WRITE_6 0x0a +-#define SET_CAPACITY 0x0b +-#define READ_REVERSE 0x0f +-#define WRITE_FILEMARKS 0x10 +-#define SPACE 0x11 +-#define INQUIRY 0x12 +-#define RECOVER_BUFFERED_DATA 0x14 +-#define MODE_SELECT 0x15 +-#define RESERVE 0x16 +-#define RELEASE 0x17 +-#define COPY 0x18 +-#define ERASE 0x19 +-#define MODE_SENSE 0x1a +-#define LOAD_UNLOAD 0x1b +-#define SCAN 0x1b +-#define START_STOP 0x1b +-#define RECEIVE_DIAGNOSTIC 0x1c +-#define SEND_DIAGNOSTIC 0x1d +-#define ALLOW_MEDIUM_REMOVAL 0x1e +-#define SET_WINDOW 0x24 +-#define READ_CAPACITY_10 0x25 +-#define GET_WINDOW 0x25 +-#define READ_10 0x28 +-#define WRITE_10 0x2a +-#define SEND 0x2a +-#define SEEK_10 0x2b +-#define LOCATE_10 0x2b +-#define POSITION_TO_ELEMENT 0x2b +-#define WRITE_VERIFY_10 0x2e +-#define VERIFY_10 0x2f +-#define SEARCH_HIGH 0x30 +-#define SEARCH_EQUAL 0x31 +-#define OBJECT_POSITION 0x31 +-#define SEARCH_LOW 0x32 +-#define SET_LIMITS 0x33 +-#define PRE_FETCH 0x34 +-#define READ_POSITION 0x34 +-#define GET_DATA_BUFFER_STATUS 0x34 +-#define SYNCHRONIZE_CACHE 0x35 +-#define LOCK_UNLOCK_CACHE 0x36 +-#define INITIALIZE_ELEMENT_STATUS_WITH_RANGE 0x37 +-#define READ_DEFECT_DATA 0x37 +-#define MEDIUM_SCAN 0x38 +-#define COMPARE 0x39 +-#define COPY_VERIFY 0x3a +-#define WRITE_BUFFER 0x3b +-#define READ_BUFFER 0x3c +-#define UPDATE_BLOCK 0x3d +-#define READ_LONG_10 0x3e +-#define WRITE_LONG_10 0x3f +-#define CHANGE_DEFINITION 0x40 +-#define WRITE_SAME_10 0x41 +-#define UNMAP 0x42 +-#define READ_TOC 0x43 +-#define REPORT_DENSITY_SUPPORT 0x44 +-#define GET_CONFIGURATION 0x46 +-#define SANITIZE 0x48 +-#define GET_EVENT_STATUS_NOTIFICATION 0x4a +-#define LOG_SELECT 0x4c +-#define LOG_SENSE 0x4d +-#define READ_DISC_INFORMATION 0x51 +-#define RESERVE_TRACK 0x53 +-#define MODE_SELECT_10 0x55 +-#define RESERVE_10 0x56 +-#define RELEASE_10 0x57 +-#define MODE_SENSE_10 0x5a +-#define SEND_CUE_SHEET 0x5d +-#define PERSISTENT_RESERVE_IN 0x5e +-#define PERSISTENT_RESERVE_OUT 0x5f +-#define VARLENGTH_CDB 0x7f +-#define WRITE_FILEMARKS_16 0x80 +-#define READ_REVERSE_16 0x81 +-#define ALLOW_OVERWRITE 0x82 +-#define EXTENDED_COPY 0x83 +-#define ATA_PASSTHROUGH_16 0x85 +-#define ACCESS_CONTROL_IN 0x86 +-#define ACCESS_CONTROL_OUT 0x87 +-#define READ_16 0x88 +-#define COMPARE_AND_WRITE 0x89 +-#define WRITE_16 0x8a +-#define WRITE_VERIFY_16 0x8e +-#define VERIFY_16 0x8f +-#define PRE_FETCH_16 0x90 +-#define SPACE_16 0x91 +-#define SYNCHRONIZE_CACHE_16 0x91 +-#define LOCATE_16 0x92 +-#define WRITE_SAME_16 0x93 +-#define ERASE_16 0x93 +-#define SERVICE_ACTION_IN_16 0x9e +-#define WRITE_LONG_16 0x9f +-#define REPORT_LUNS 0xa0 +-#define ATA_PASSTHROUGH_12 0xa1 +-#define MAINTENANCE_IN 0xa3 +-#define MAINTENANCE_OUT 0xa4 +-#define MOVE_MEDIUM 0xa5 +-#define EXCHANGE_MEDIUM 0xa6 +-#define SET_READ_AHEAD 0xa7 +-#define READ_12 0xa8 +-#define WRITE_12 0xaa +-#define SERVICE_ACTION_IN_12 0xab +-#define ERASE_12 0xac +-#define READ_DVD_STRUCTURE 0xad +-#define WRITE_VERIFY_12 0xae +-#define VERIFY_12 0xaf +-#define SEARCH_HIGH_12 0xb0 +-#define SEARCH_EQUAL_12 0xb1 +-#define SEARCH_LOW_12 0xb2 +-#define READ_ELEMENT_STATUS 0xb8 +-#define SEND_VOLUME_TAG 0xb6 +-#define READ_DEFECT_DATA_12 0xb7 +-#define SET_CD_SPEED 0xbb +-#define MECHANISM_STATUS 0xbd +-#define READ_CD 0xbe +-#define SEND_DVD_STRUCTURE 0xbf +- +-/* +- * SERVICE ACTION IN subcodes +- */ +-#define SAI_READ_CAPACITY_16 0x10 +- +-/* +- * READ POSITION service action codes +- */ +-#define SHORT_FORM_BLOCK_ID 0x00 +-#define SHORT_FORM_VENDOR_SPECIFIC 0x01 +-#define LONG_FORM 0x06 +-#define EXTENDED_FORM 0x08 +- +-/* +- * SAM Status codes +- */ +- +-#define GOOD 0x00 +-#define CHECK_CONDITION 0x02 +-#define CONDITION_GOOD 0x04 +-#define BUSY 0x08 +-#define INTERMEDIATE_GOOD 0x10 +-#define INTERMEDIATE_C_GOOD 0x14 +-#define RESERVATION_CONFLICT 0x18 +-#define COMMAND_TERMINATED 0x22 +-#define TASK_SET_FULL 0x28 +-#define ACA_ACTIVE 0x30 +-#define TASK_ABORTED 0x40 +- +-#define STATUS_MASK 0x3e +- +-/* +- * SENSE KEYS +- */ +- +-#define NO_SENSE 0x00 +-#define RECOVERED_ERROR 0x01 +-#define NOT_READY 0x02 +-#define MEDIUM_ERROR 0x03 +-#define HARDWARE_ERROR 0x04 +-#define ILLEGAL_REQUEST 0x05 +-#define UNIT_ATTENTION 0x06 +-#define DATA_PROTECT 0x07 +-#define BLANK_CHECK 0x08 +-#define COPY_ABORTED 0x0a +-#define ABORTED_COMMAND 0x0b +-#define VOLUME_OVERFLOW 0x0d +-#define MISCOMPARE 0x0e +- +- +-/* +- * DEVICE TYPES +- */ +- +-#define TYPE_DISK 0x00 +-#define TYPE_TAPE 0x01 +-#define TYPE_PRINTER 0x02 +-#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +-#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +-#define TYPE_ROM 0x05 +-#define TYPE_SCANNER 0x06 +-#define TYPE_MOD 0x07 /* Magneto-optical disk - +- * - treated as TYPE_DISK */ +-#define TYPE_MEDIUM_CHANGER 0x08 +-#define TYPE_STORAGE_ARRAY 0x0c /* Storage array device */ +-#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +-#define TYPE_RBC 0x0e /* Simplified Direct-Access Device */ +-#define TYPE_OSD 0x11 /* Object-storage Device */ +-#define TYPE_WLUN 0x1e /* Well known LUN */ +-#define TYPE_NOT_PRESENT 0x1f +-#define TYPE_INACTIVE 0x20 +-#define TYPE_NO_LUN 0x7f +- +-/* Mode page codes for mode sense/set */ +-#define MODE_PAGE_R_W_ERROR 0x01 +-#define MODE_PAGE_HD_GEOMETRY 0x04 +-#define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05 +-#define MODE_PAGE_CACHING 0x08 +-#define MODE_PAGE_AUDIO_CTL 0x0e +-#define MODE_PAGE_POWER 0x1a +-#define MODE_PAGE_FAULT_FAIL 0x1c +-#define MODE_PAGE_TO_PROTECT 0x1d +-#define MODE_PAGE_CAPABILITIES 0x2a +-#define MODE_PAGE_ALLS 0x3f +-/* Not in Mt. Fuji, but in ATAPI 2.6 -- deprecated now in favor +- * of MODE_PAGE_SENSE_POWER */ +-#define MODE_PAGE_CDROM 0x0d +- +-/* Event notification classes for GET EVENT STATUS NOTIFICATION */ +-#define GESN_NO_EVENTS 0 +-#define GESN_OPERATIONAL_CHANGE 1 +-#define GESN_POWER_MANAGEMENT 2 +-#define GESN_EXTERNAL_REQUEST 3 +-#define GESN_MEDIA 4 +-#define GESN_MULTIPLE_HOSTS 5 +-#define GESN_DEVICE_BUSY 6 +- +-/* Event codes for MEDIA event status notification */ +-#define MEC_NO_CHANGE 0 +-#define MEC_EJECT_REQUESTED 1 +-#define MEC_NEW_MEDIA 2 +-#define MEC_MEDIA_REMOVAL 3 /* only for media changers */ +-#define MEC_MEDIA_CHANGED 4 /* only for media changers */ +-#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */ +-#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */ +- +-#define MS_TRAY_OPEN 1 +-#define MS_MEDIA_PRESENT 2 +- +-/* +- * Based on values from but extending CD_MINS +- * to the maximum common size allowed by the Orange's Book ATIP +- * +- * 90 and 99 min CDs are also available but using them as the +- * upper limit reduces the effectiveness of the heuristic to +- * detect DVDs burned to less than 25% of their maximum capacity +- */ +- +-/* Some generally useful CD-ROM information */ +-#define CD_MINS 80 /* max. minutes per CD */ +-#define CD_SECS 60 /* seconds per minute */ +-#define CD_FRAMES 75 /* frames per second */ +-#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */ +-#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE) +-#define CD_MAX_SECTORS (CD_MAX_BYTES / 512) +- +-/* +- * The MMC values are not IDE specific and might need to be moved +- * to a common header if they are also needed for the SCSI emulation +- */ +- +-/* Profile list from MMC-6 revision 1 table 91 */ +-#define MMC_PROFILE_NONE 0x0000 +-#define MMC_PROFILE_CD_ROM 0x0008 +-#define MMC_PROFILE_CD_R 0x0009 +-#define MMC_PROFILE_CD_RW 0x000A +-#define MMC_PROFILE_DVD_ROM 0x0010 +-#define MMC_PROFILE_DVD_R_SR 0x0011 +-#define MMC_PROFILE_DVD_RAM 0x0012 +-#define MMC_PROFILE_DVD_RW_RO 0x0013 +-#define MMC_PROFILE_DVD_RW_SR 0x0014 +-#define MMC_PROFILE_DVD_R_DL_SR 0x0015 +-#define MMC_PROFILE_DVD_R_DL_JR 0x0016 +-#define MMC_PROFILE_DVD_RW_DL 0x0017 +-#define MMC_PROFILE_DVD_DDR 0x0018 +-#define MMC_PROFILE_DVD_PLUS_RW 0x001A +-#define MMC_PROFILE_DVD_PLUS_R 0x001B +-#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A +-#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B +-#define MMC_PROFILE_BD_ROM 0x0040 +-#define MMC_PROFILE_BD_R_SRM 0x0041 +-#define MMC_PROFILE_BD_R_RRM 0x0042 +-#define MMC_PROFILE_BD_RE 0x0043 +-#define MMC_PROFILE_HDDVD_ROM 0x0050 +-#define MMC_PROFILE_HDDVD_R 0x0051 +-#define MMC_PROFILE_HDDVD_RAM 0x0052 +-#define MMC_PROFILE_HDDVD_RW 0x0053 +-#define MMC_PROFILE_HDDVD_R_DL 0x0058 +-#define MMC_PROFILE_HDDVD_RW_DL 0x005A +-#define MMC_PROFILE_INVALID 0xFFFF +- +-#endif +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" + + /* debug IDE devices */ + //#define DEBUG_IDE +diff --git a/include/scsi/constants.h b/include/scsi/constants.h +new file mode 100644 +index 0000000000..a141dd71f8 +--- /dev/null ++++ b/include/scsi/constants.h +@@ -0,0 +1,314 @@ ++/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, see . ++*/ ++ ++/* ++ * This header file contains public constants and structures used by ++ * the scsi code for linux. ++ */ ++ ++#ifndef BLOCK_SCSI_H ++#define BLOCK_SCSI_H ++ ++/* ++ * SCSI opcodes ++ */ ++ ++#define TEST_UNIT_READY 0x00 ++#define REWIND 0x01 ++#define REQUEST_SENSE 0x03 ++#define FORMAT_UNIT 0x04 ++#define READ_BLOCK_LIMITS 0x05 ++#define INITIALIZE_ELEMENT_STATUS 0x07 ++#define REASSIGN_BLOCKS 0x07 ++#define READ_6 0x08 ++#define WRITE_6 0x0a ++#define SET_CAPACITY 0x0b ++#define READ_REVERSE 0x0f ++#define WRITE_FILEMARKS 0x10 ++#define SPACE 0x11 ++#define INQUIRY 0x12 ++#define RECOVER_BUFFERED_DATA 0x14 ++#define MODE_SELECT 0x15 ++#define RESERVE 0x16 ++#define RELEASE 0x17 ++#define COPY 0x18 ++#define ERASE 0x19 ++#define MODE_SENSE 0x1a ++#define LOAD_UNLOAD 0x1b ++#define SCAN 0x1b ++#define START_STOP 0x1b ++#define RECEIVE_DIAGNOSTIC 0x1c ++#define SEND_DIAGNOSTIC 0x1d ++#define ALLOW_MEDIUM_REMOVAL 0x1e ++#define SET_WINDOW 0x24 ++#define READ_CAPACITY_10 0x25 ++#define GET_WINDOW 0x25 ++#define READ_10 0x28 ++#define WRITE_10 0x2a ++#define SEND 0x2a ++#define SEEK_10 0x2b ++#define LOCATE_10 0x2b ++#define POSITION_TO_ELEMENT 0x2b ++#define WRITE_VERIFY_10 0x2e ++#define VERIFY_10 0x2f ++#define SEARCH_HIGH 0x30 ++#define SEARCH_EQUAL 0x31 ++#define OBJECT_POSITION 0x31 ++#define SEARCH_LOW 0x32 ++#define SET_LIMITS 0x33 ++#define PRE_FETCH 0x34 ++#define READ_POSITION 0x34 ++#define GET_DATA_BUFFER_STATUS 0x34 ++#define SYNCHRONIZE_CACHE 0x35 ++#define LOCK_UNLOCK_CACHE 0x36 ++#define INITIALIZE_ELEMENT_STATUS_WITH_RANGE 0x37 ++#define READ_DEFECT_DATA 0x37 ++#define MEDIUM_SCAN 0x38 ++#define COMPARE 0x39 ++#define COPY_VERIFY 0x3a ++#define WRITE_BUFFER 0x3b ++#define READ_BUFFER 0x3c ++#define UPDATE_BLOCK 0x3d ++#define READ_LONG_10 0x3e ++#define WRITE_LONG_10 0x3f ++#define CHANGE_DEFINITION 0x40 ++#define WRITE_SAME_10 0x41 ++#define UNMAP 0x42 ++#define READ_TOC 0x43 ++#define REPORT_DENSITY_SUPPORT 0x44 ++#define GET_CONFIGURATION 0x46 ++#define SANITIZE 0x48 ++#define GET_EVENT_STATUS_NOTIFICATION 0x4a ++#define LOG_SELECT 0x4c ++#define LOG_SENSE 0x4d ++#define READ_DISC_INFORMATION 0x51 ++#define RESERVE_TRACK 0x53 ++#define MODE_SELECT_10 0x55 ++#define RESERVE_10 0x56 ++#define RELEASE_10 0x57 ++#define MODE_SENSE_10 0x5a ++#define SEND_CUE_SHEET 0x5d ++#define PERSISTENT_RESERVE_IN 0x5e ++#define PERSISTENT_RESERVE_OUT 0x5f ++#define VARLENGTH_CDB 0x7f ++#define WRITE_FILEMARKS_16 0x80 ++#define READ_REVERSE_16 0x81 ++#define ALLOW_OVERWRITE 0x82 ++#define EXTENDED_COPY 0x83 ++#define ATA_PASSTHROUGH_16 0x85 ++#define ACCESS_CONTROL_IN 0x86 ++#define ACCESS_CONTROL_OUT 0x87 ++#define READ_16 0x88 ++#define COMPARE_AND_WRITE 0x89 ++#define WRITE_16 0x8a ++#define WRITE_VERIFY_16 0x8e ++#define VERIFY_16 0x8f ++#define PRE_FETCH_16 0x90 ++#define SPACE_16 0x91 ++#define SYNCHRONIZE_CACHE_16 0x91 ++#define LOCATE_16 0x92 ++#define WRITE_SAME_16 0x93 ++#define ERASE_16 0x93 ++#define SERVICE_ACTION_IN_16 0x9e ++#define WRITE_LONG_16 0x9f ++#define REPORT_LUNS 0xa0 ++#define ATA_PASSTHROUGH_12 0xa1 ++#define MAINTENANCE_IN 0xa3 ++#define MAINTENANCE_OUT 0xa4 ++#define MOVE_MEDIUM 0xa5 ++#define EXCHANGE_MEDIUM 0xa6 ++#define SET_READ_AHEAD 0xa7 ++#define READ_12 0xa8 ++#define WRITE_12 0xaa ++#define SERVICE_ACTION_IN_12 0xab ++#define ERASE_12 0xac ++#define READ_DVD_STRUCTURE 0xad ++#define WRITE_VERIFY_12 0xae ++#define VERIFY_12 0xaf ++#define SEARCH_HIGH_12 0xb0 ++#define SEARCH_EQUAL_12 0xb1 ++#define SEARCH_LOW_12 0xb2 ++#define READ_ELEMENT_STATUS 0xb8 ++#define SEND_VOLUME_TAG 0xb6 ++#define READ_DEFECT_DATA_12 0xb7 ++#define SET_CD_SPEED 0xbb ++#define MECHANISM_STATUS 0xbd ++#define READ_CD 0xbe ++#define SEND_DVD_STRUCTURE 0xbf ++ ++/* ++ * SERVICE ACTION IN subcodes ++ */ ++#define SAI_READ_CAPACITY_16 0x10 ++ ++/* ++ * READ POSITION service action codes ++ */ ++#define SHORT_FORM_BLOCK_ID 0x00 ++#define SHORT_FORM_VENDOR_SPECIFIC 0x01 ++#define LONG_FORM 0x06 ++#define EXTENDED_FORM 0x08 ++ ++/* ++ * SAM Status codes ++ */ ++ ++#define GOOD 0x00 ++#define CHECK_CONDITION 0x02 ++#define CONDITION_GOOD 0x04 ++#define BUSY 0x08 ++#define INTERMEDIATE_GOOD 0x10 ++#define INTERMEDIATE_C_GOOD 0x14 ++#define RESERVATION_CONFLICT 0x18 ++#define COMMAND_TERMINATED 0x22 ++#define TASK_SET_FULL 0x28 ++#define ACA_ACTIVE 0x30 ++#define TASK_ABORTED 0x40 ++ ++#define STATUS_MASK 0x3e ++ ++/* ++ * SENSE KEYS ++ */ ++ ++#define NO_SENSE 0x00 ++#define RECOVERED_ERROR 0x01 ++#define NOT_READY 0x02 ++#define MEDIUM_ERROR 0x03 ++#define HARDWARE_ERROR 0x04 ++#define ILLEGAL_REQUEST 0x05 ++#define UNIT_ATTENTION 0x06 ++#define DATA_PROTECT 0x07 ++#define BLANK_CHECK 0x08 ++#define COPY_ABORTED 0x0a ++#define ABORTED_COMMAND 0x0b ++#define VOLUME_OVERFLOW 0x0d ++#define MISCOMPARE 0x0e ++ ++ ++/* ++ * DEVICE TYPES ++ */ ++ ++#define TYPE_DISK 0x00 ++#define TYPE_TAPE 0x01 ++#define TYPE_PRINTER 0x02 ++#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ ++#define TYPE_WORM 0x04 /* Treated as ROM by our system */ ++#define TYPE_ROM 0x05 ++#define TYPE_SCANNER 0x06 ++#define TYPE_MOD 0x07 /* Magneto-optical disk - ++ * - treated as TYPE_DISK */ ++#define TYPE_MEDIUM_CHANGER 0x08 ++#define TYPE_STORAGE_ARRAY 0x0c /* Storage array device */ ++#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ ++#define TYPE_RBC 0x0e /* Simplified Direct-Access Device */ ++#define TYPE_OSD 0x11 /* Object-storage Device */ ++#define TYPE_WLUN 0x1e /* Well known LUN */ ++#define TYPE_NOT_PRESENT 0x1f ++#define TYPE_INACTIVE 0x20 ++#define TYPE_NO_LUN 0x7f ++ ++/* Mode page codes for mode sense/set */ ++#define MODE_PAGE_R_W_ERROR 0x01 ++#define MODE_PAGE_HD_GEOMETRY 0x04 ++#define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05 ++#define MODE_PAGE_CACHING 0x08 ++#define MODE_PAGE_AUDIO_CTL 0x0e ++#define MODE_PAGE_POWER 0x1a ++#define MODE_PAGE_FAULT_FAIL 0x1c ++#define MODE_PAGE_TO_PROTECT 0x1d ++#define MODE_PAGE_CAPABILITIES 0x2a ++#define MODE_PAGE_ALLS 0x3f ++/* Not in Mt. Fuji, but in ATAPI 2.6 -- deprecated now in favor ++ * of MODE_PAGE_SENSE_POWER */ ++#define MODE_PAGE_CDROM 0x0d ++ ++/* Event notification classes for GET EVENT STATUS NOTIFICATION */ ++#define GESN_NO_EVENTS 0 ++#define GESN_OPERATIONAL_CHANGE 1 ++#define GESN_POWER_MANAGEMENT 2 ++#define GESN_EXTERNAL_REQUEST 3 ++#define GESN_MEDIA 4 ++#define GESN_MULTIPLE_HOSTS 5 ++#define GESN_DEVICE_BUSY 6 ++ ++/* Event codes for MEDIA event status notification */ ++#define MEC_NO_CHANGE 0 ++#define MEC_EJECT_REQUESTED 1 ++#define MEC_NEW_MEDIA 2 ++#define MEC_MEDIA_REMOVAL 3 /* only for media changers */ ++#define MEC_MEDIA_CHANGED 4 /* only for media changers */ ++#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */ ++#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */ ++ ++#define MS_TRAY_OPEN 1 ++#define MS_MEDIA_PRESENT 2 ++ ++/* ++ * Based on values from but extending CD_MINS ++ * to the maximum common size allowed by the Orange's Book ATIP ++ * ++ * 90 and 99 min CDs are also available but using them as the ++ * upper limit reduces the effectiveness of the heuristic to ++ * detect DVDs burned to less than 25% of their maximum capacity ++ */ ++ ++/* Some generally useful CD-ROM information */ ++#define CD_MINS 80 /* max. minutes per CD */ ++#define CD_SECS 60 /* seconds per minute */ ++#define CD_FRAMES 75 /* frames per second */ ++#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */ ++#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE) ++#define CD_MAX_SECTORS (CD_MAX_BYTES / 512) ++ ++/* ++ * The MMC values are not IDE specific and might need to be moved ++ * to a common header if they are also needed for the SCSI emulation ++ */ ++ ++/* Profile list from MMC-6 revision 1 table 91 */ ++#define MMC_PROFILE_NONE 0x0000 ++#define MMC_PROFILE_CD_ROM 0x0008 ++#define MMC_PROFILE_CD_R 0x0009 ++#define MMC_PROFILE_CD_RW 0x000A ++#define MMC_PROFILE_DVD_ROM 0x0010 ++#define MMC_PROFILE_DVD_R_SR 0x0011 ++#define MMC_PROFILE_DVD_RAM 0x0012 ++#define MMC_PROFILE_DVD_RW_RO 0x0013 ++#define MMC_PROFILE_DVD_RW_SR 0x0014 ++#define MMC_PROFILE_DVD_R_DL_SR 0x0015 ++#define MMC_PROFILE_DVD_R_DL_JR 0x0016 ++#define MMC_PROFILE_DVD_RW_DL 0x0017 ++#define MMC_PROFILE_DVD_DDR 0x0018 ++#define MMC_PROFILE_DVD_PLUS_RW 0x001A ++#define MMC_PROFILE_DVD_PLUS_R 0x001B ++#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A ++#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B ++#define MMC_PROFILE_BD_ROM 0x0040 ++#define MMC_PROFILE_BD_R_SRM 0x0041 ++#define MMC_PROFILE_BD_R_RRM 0x0042 ++#define MMC_PROFILE_BD_RE 0x0043 ++#define MMC_PROFILE_HDDVD_ROM 0x0050 ++#define MMC_PROFILE_HDDVD_R 0x0051 ++#define MMC_PROFILE_HDDVD_RAM 0x0052 ++#define MMC_PROFILE_HDDVD_RW 0x0053 ++#define MMC_PROFILE_HDDVD_R_DL 0x0058 ++#define MMC_PROFILE_HDDVD_RW_DL 0x005A ++#define MMC_PROFILE_INVALID 0xFFFF ++ ++#endif +diff --git a/scsi/utils.c b/scsi/utils.c +index 6ee9f4095b..fab60bdf20 100644 +--- a/scsi/utils.c ++++ b/scsi/utils.c +@@ -14,7 +14,7 @@ + */ + + #include "qemu/osdep.h" +-#include "block/scsi.h" ++#include "scsi/constants.h" + #include "scsi/utils.h" + #include "qemu/bswap.h" + +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 @@ + + #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" diff --git a/0011-scsi-file-posix-add-support-for-persistent-reservati.patch b/0011-scsi-file-posix-add-support-for-persistent-reservati.patch new file mode 100644 index 0000000..e747615 --- /dev/null +++ b/0011-scsi-file-posix-add-support-for-persistent-reservati.patch @@ -0,0 +1,436 @@ +From: Paolo Bonzini +Date: Mon, 21 Aug 2017 18:58:56 +0200 +Subject: [PATCH] scsi, file-posix: add support for persistent reservation + management + +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=helper0,path=/var/run/qemu-pr-helper.sock + -drive if=none,id=hd,driver=raw,file.filename=/dev/sdb,file.pr-manager=helper0 + -device scsi-block,drive=hd + +or: + + $ qemu-system-x86_64 + -device virtio-scsi \ + -object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock + -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0 + -device scsi-block,drive=hd + +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 | 56 ++++++++++++++++++++++++ + qapi/block-core.json | 4 ++ + scsi/Makefile.objs | 2 + + scsi/pr-manager.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++ + scsi/trace-events | 3 ++ + vl.c | 3 +- + 9 files changed, 258 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 + create mode 100644 scsi/trace-events + +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 += qapi + trace-events-subdirs += accel/tcg + trace-events-subdirs += accel/kvm + trace-events-subdirs += nbd ++trace-events-subdirs += scsi + + trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) + +diff --git a/block/file-posix.c b/block/file-posix.c +index cb3bfce147..9cacf06685 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -34,6 +34,9 @@ + #include "qapi/util.h" + #include "qapi/qmp/qstring.h" + ++#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; + + typedef struct BDRVRawReopenState { +@@ -403,6 +408,11 @@ static QemuOptsList raw_runtime_opts = { + .type = QEMU_OPT_STRING, + .help = "file locking mode (on/off/auto, default: auto)", + }, ++ { ++ .name = "pr-manager", ++ .type = QEMU_OPT_STRING, ++ .help = "id of persistent reservation manager object (default: none)", ++ }, + { /* end of list */ } + }, + }; +@@ -414,6 +424,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + QemuOpts *opts; + Error *local_err = NULL; + const char *filename = NULL; ++ const char *str; + BlockdevAioOptions aio, aio_default; + int fd, ret; + struct stat st; +@@ -475,6 +486,16 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + abort(); + } + ++ str = qemu_opt_get(opts, "pr-manager"); ++ if (str) { ++ s->pr_mgr = pr_manager_lookup(str, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ + s->open_flags = open_flags; + raw_parse_flags(bdrv_flags, &s->open_flags); + +@@ -2597,6 +2618,15 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, + if (fd_open(bs) < 0) + return NULL; + ++ if (req == SG_IO && s->pr_mgr) { ++ struct sg_io_hdr *io_hdr = buf; ++ if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || ++ io_hdr->cmdp[0] == PERSISTENT_RESERVE_IN) { ++ return pr_manager_execute(s->pr_mgr, bdrv_get_aio_context(bs), ++ s->fd, io_hdr, cb, opaque); ++ } ++ } ++ + acb = g_new(RawPosixAIOData, 1); + acb->bs = bs; + acb->aio_type = 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 @@ ++====================================== ++Persistent reservation managers ++====================================== ++ ++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=helper0,path=/var/run/qemu-pr-helper.sock ++ -drive if=none,id=hd,driver=raw,file.filename=/dev/sdb,file.pr-manager=helper0 ++ -device scsi-block,drive=hd ++ ++Alternatively, using ``-blockdev``:: ++ ++ $ qemu-system-x86_64 ++ -device virtio-scsi \ ++ -object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock ++ -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0 ++ -device scsi-block,drive=hd +diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h +new file mode 100644 +index 0000000000..b2b37d63bc +--- /dev/null ++++ b/include/scsi/pr-manager.h +@@ -0,0 +1,56 @@ ++#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..1cf6ec8be7 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 id for the object that will handle persistent reservations ++# for this device (default: none, 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 enable + # 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' } } + +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 += utils.o ++ ++block-obj-$(CONFIG_LINUX) += pr-manager.o +diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c +new file mode 100644 +index 0000000000..87c45db5d4 +--- /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 ++ ++#include "qapi/error.h" ++#include "block/aio.h" ++#include "block/thread-pool.h" ++#include "scsi/pr-manager.h" ++#include "trace.h" ++ ++typedef struct PRManagerData { ++ PRManager *pr_mgr; ++ struct sg_io_hdr *hdr; ++ int fd; ++} PRManagerData; ++ ++static int pr_manager_worker(void *opaque) ++{ ++ PRManagerData *data = opaque; ++ PRManager *pr_mgr = data->pr_mgr; ++ PRManagerClass *pr_mgr_class = ++ PR_MANAGER_GET_CLASS(pr_mgr); ++ struct sg_io_hdr *hdr = data->hdr; ++ int fd = data->fd; ++ int r; ++ ++ g_free(data); ++ trace_pr_manager_run(fd, hdr->cmdp[0], hdr->cmdp[1]); ++ ++ /* The reference was taken in pr_manager_execute. */ ++ r = 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 = g_new(PRManagerData, 1); ++ ThreadPool *pool = aio_get_thread_pool(ctx); ++ ++ trace_pr_manager_execute(fd, hdr->cmdp[0], hdr->cmdp[1], opaque); ++ data->pr_mgr = pr_mgr; ++ data->fd = fd; ++ data->hdr = 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 = { ++ .parent = TYPE_OBJECT, ++ .name = TYPE_PR_MANAGER, ++ .class_size = sizeof(PRManagerClass), ++ .abstract = true, ++ .interfaces = (InterfaceInfo[]) { ++ { TYPE_USER_CREATABLE }, ++ { } ++ } ++}; ++ ++PRManager *pr_manager_lookup(const char *id, Error **errp) ++{ ++ Object *obj; ++ PRManager *pr_mgr; ++ ++ obj = 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 = (PRManager *) ++ object_dynamic_cast(obj, ++ TYPE_PR_MANAGER); ++ if (!pr_mgr) { ++ error_setg(errp, ++ "Object with id '%s' is not a persistent reservation manager", ++ 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/scsi/trace-events b/scsi/trace-events +new file mode 100644 +index 0000000000..45f5b6e49b +--- /dev/null ++++ b/scsi/trace-events +@@ -0,0 +1,3 @@ ++# scsi/pr-manager.c ++pr_manager_execute(int fd, int cmd, int sa, void *opaque) "fd=%d cmd=0x%02x service action=0x%02x opaque=%p" ++pr_manager_run(int fd, int cmd, int sa) "fd=%d cmd=0x%02x service action=0x%02x" +diff --git a/vl.c b/vl.c +index d63269332f..acaf9eab39 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; + } + diff --git a/0012-scsi-build-qemu-pr-helper.patch b/0012-scsi-build-qemu-pr-helper.patch new file mode 100644 index 0000000..9d97fc7 --- /dev/null +++ b/0012-scsi-build-qemu-pr-helper.patch @@ -0,0 +1,1009 @@ +From: Paolo Bonzini +Date: Tue, 22 Aug 2017 06:50:18 +0200 +Subject: [PATCH] scsi: build qemu-pr-helper + +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 | 4 +- + configure | 14 +- + docs/interop/pr-helper.rst | 83 +++++ + docs/pr-manager.rst | 33 ++ + scsi/pr-helper.h | 41 +++ + scsi/qemu-pr-helper.c | 735 +++++++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 905 insertions(+), 5 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 81447b1f08..f07c0d7e9c 100644 +--- a/Makefile ++++ b/Makefile +@@ -382,6 +382,8 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) + fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) + fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap + ++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","$@") + +@@ -489,7 +491,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.* *.pod *~ */*~ +- 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 +diff --git a/configure b/configure +index dd73cce62f..14bdf9bb31 100755 +--- a/configure ++++ b/configure +@@ -5070,16 +5070,22 @@ if test "$want_tools" = "yes" ; then + fi + fi + if test "$softmmu" = yes ; then +- if test "$virtfs" != no ; then +- if test "$cap" = yes && test "$linux" = yes && test "$attr" = yes ; then ++ if test "$linux" = yes; then ++ if test "$virtfs" != no && test "$cap" = yes && test "$attr" = yes ; then + virtfs=yes + tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)" + else + if test "$virtfs" = 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=no + fi ++ tools="$tools scsi/qemu-pr-helper\$(EXESUF)" ++ else ++ if test "$virtfs" = yes; then ++ error_exit "VirtFS is supported only on Linux" ++ fi ++ virtfs=no + fi + fi + +@@ -6545,7 +6551,7 @@ fi + + # build tree in object directory in case the source is not in the current directory + DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests" +-DIRS="$DIRS docs docs/interop fsdev" ++DIRS="$DIRS docs docs/interop fsdev scsi" + DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" + DIRS="$DIRS roms/seabios roms/vgabios" + DIRS="$DIRS qapi-generated" +diff --git a/docs/interop/pr-helper.rst b/docs/interop/pr-helper.rst +new file mode 100644 +index 0000000000..9f76d5bcf9 +--- /dev/null ++++ b/docs/interop/pr-helper.rst +@@ -0,0 +1,83 @@ ++.. ++ ++====================================== ++Persistent reservation helper protocol ++====================================== ++ ++QEMU's SCSI passthrough devices, ``scsi-block`` and ``scsi-generic``, ++can delegate implementation of persistent reservations to an external ++(and typically privileged) 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 ++----------------------------- ++ ++All data transmitted on the socket is big-endian. ++ ++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 reads it, ++then 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, ++the helper will not enable the feature. ++ ++Right now no feature is defined, so the two parties always write four ++zero bytes. ++ ++Command format ++-------------- ++ ++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. ++ ++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 length of the parameter list is taken from the CDB itself. ++ ++The helper's reply has the following structure: ++ ++- 4 bytes for the SCSI status ++ ++- 4 bytes for the payload size (nonzero only for PERSISTENT RESERVE IN ++ and only if the SCSI status is 0x00, i.e. GOOD) ++ ++- 96 bytes for the SCSI sense data ++ ++- if the size is nonzero, the payload follows ++ ++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). ++ ++The payload size is always less than or equal to the allocation length ++specified in the CDB for the PERSISTENT RESERVE IN command. ++ ++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=helper0,path=/var/run/qemu-pr-helper.sock + -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0 + -device scsi-block,drive=hd ++ ++---------------------------------- ++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=path PID file when running as a daemon ++-k, --socket=path path to the socket ++-T, --trace=trace-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=/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=user user to drop privileges to ++-g, --group=group group to drop privileges to +diff --git a/scsi/pr-helper.h b/scsi/pr-helper.h +new file mode 100644 +index 0000000000..96c50a9e5f +--- /dev/null ++++ b/scsi/pr-helper.h +@@ -0,0 +1,41 @@ ++/* Definitions for QEMU's persistent reservation helper daemon ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * Author: ++ * Paolo Bonzini ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++#ifndef QEMU_PR_HELPER_H ++#define QEMU_PR_HELPER_H 1 ++ ++#include ++ ++#define PR_HELPER_CDB_SIZE 16 ++#define PR_HELPER_SENSE_SIZE 96 ++#define PR_HELPER_DATA_SIZE 8192 ++ ++typedef struct PRHelperResponse { ++ int32_t result; ++ int32_t sz; ++ 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..e39efbd529 +--- /dev/null ++++ b/scsi/qemu-pr-helper.c +@@ -0,0 +1,735 @@ ++/* ++ * Privileged helper to handle persistent reservation commands for QEMU ++ * ++ * 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 ++#include ++#include ++ ++#ifdef CONFIG_LIBCAP ++#include ++#endif ++#include ++#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" ++ ++#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 = 1; ++static int verbose; ++ ++#ifdef CONFIG_LIBCAP ++static int uid = -1; ++static int gid = -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=PATH PID file when running as a daemon\n" ++" (default '%s')\n" ++" -k, --socket=PATH path to the unix socket\n" ++" (default '%s')\n" ++" -T, --trace [[enable=]][,events=][,file=]\n" ++" specify tracing options\n" ++#ifdef CONFIG_LIBCAP ++" -u, --user=USER user to drop privileges to\n" ++" -g, --group=GROUP 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 NO\n" ++"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" ++ , name); ++} ++ ++static void write_pidfile(void) ++{ ++ int pidfd; ++ char pidstr[32]; ++ ++ pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); ++ if (pidfd == -1) { ++ error_report("Cannot open pid file, %s", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (lockf(pidfd, F_TLOCK, 0)) { ++ error_report("Cannot lock pid file, %s", strerror(errno)); ++ goto fail; ++ } ++ if (ftruncate(pidfd, 0)) { ++ error_report("Failed to truncate pid file"); ++ goto fail; ++ } ++ ++ snprintf(pidstr, sizeof(pidstr), "%d\n", getpid()); ++ if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { ++ error_report("Failed to write pid file"); ++ goto fail; ++ } ++ return; ++ ++fail: ++ unlink(pidfile); ++ close(pidfd); ++ exit(EXIT_FAILURE); ++} ++ ++/* SG_IO support */ ++ ++typedef struct PRHelperSGIOData { ++ int fd; ++ const uint8_t *cdb; ++ uint8_t *sense; ++ uint8_t *buf; ++ int sz; /* input/output */ ++ int dir; ++} PRHelperSGIOData; ++ ++static int do_sgio_worker(void *opaque) ++{ ++ PRHelperSGIOData *data = 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 = 'S'; ++ io_hdr.cmd_len = PR_HELPER_CDB_SIZE; ++ io_hdr.cmdp = (uint8_t *)data->cdb; ++ io_hdr.sbp = data->sense; ++ io_hdr.mx_sb_len = PR_HELPER_SENSE_SIZE; ++ io_hdr.timeout = 1; ++ io_hdr.dxfer_direction = data->dir; ++ io_hdr.dxferp = (char *)data->buf; ++ io_hdr.dxfer_len = data->sz; ++ ret = ioctl(data->fd, SG_IO, &io_hdr); ++ status = sg_io_sense_from_errno(ret < 0 ? errno : 0, &io_hdr, ++ &sense_code); ++ if (status == GOOD) { ++ data->sz -= io_hdr.resid; ++ } else { ++ data->sz = 0; ++ } ++ ++ if (status == 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 = aio_get_thread_pool(qemu_get_aio_context()); ++ int r; ++ ++ PRHelperSGIOData data = { ++ .fd = fd, ++ .cdb = cdb, ++ .sense = sense, ++ .buf = buf, ++ .sz = *sz, ++ .dir = dir, ++ }; ++ ++ r = thread_pool_submit_co(pool, do_sgio_worker, &data); ++ *sz = data.sz; ++ return r; ++} ++ ++static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, ++ uint8_t *data, int *resp_sz) ++{ ++ return do_sgio(fd, cdb, sense, data, resp_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) ++{ ++ int resp_sz = sz; ++ return do_sgio(fd, cdb, sense, (uint8_t *)param, &resp_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 coroutine_fn prh_read(PRHelperClient *client, void *buf, int sz, ++ Error **errp) ++{ ++ int ret = 0; ++ ++ while (sz > 0) { ++ int *fds = NULL; ++ size_t nfds = 0; ++ int i; ++ struct iovec iov; ++ ssize_t n_read; ++ ++ iov.iov_base = buf; ++ iov.iov_len = sz; ++ n_read = qio_channel_readv_full(QIO_CHANNEL(client->ioc), &iov, 1, ++ &fds, &nfds, errp); ++ ++ if (n_read == QIO_CHANNEL_ERR_BLOCK) { ++ qio_channel_yield(QIO_CHANNEL(client->ioc), G_IO_IN); ++ continue; ++ } ++ if (n_read <= 0) { ++ ret = n_read ? n_read : -1; ++ goto err; ++ } ++ ++ /* Stash one file descriptor per request. */ ++ if (nfds) { ++ bool too_many = false; ++ for (i = 0; i < nfds; i++) { ++ if (client->fd == -1) { ++ client->fd = fds[i]; ++ } else { ++ close(fds[i]); ++ too_many = true; ++ } ++ } ++ g_free(fds); ++ if (too_many) { ++ ret = -1; ++ goto err; ++ } ++ } ++ ++ buf += n_read; ++ sz -= n_read; ++ } ++ ++ return 0; ++ ++err: ++ if (client->fd != -1) { ++ close(client->fd); ++ client->fd = -1; ++ } ++ return ret; ++} ++ ++static int coroutine_fn 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 == -1) { ++ error_setg(errp, "No file descriptor in request."); ++ return -1; ++ } ++ ++ if (req->cdb[0] != PERSISTENT_RESERVE_OUT && ++ req->cdb[0] != PERSISTENT_RESERVE_IN) { ++ error_setg(errp, "Invalid CDB, closing socket."); ++ goto out_close; ++ } ++ ++ sz = scsi_cdb_xfer(req->cdb); ++ if (sz > sizeof(client->data)) { ++ goto out_close; ++ } ++ ++ if (req->cdb[0] == PERSISTENT_RESERVE_OUT) { ++ if (qio_channel_read_all(QIO_CHANNEL(client->ioc), ++ (char *)client->data, sz, ++ errp) < 0) { ++ goto out_close; ++ } ++ if ((fcntl(client->fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { ++ scsi_build_sense(resp->sense, SENSE_CODE(INVALID_OPCODE)); ++ sz = 0; ++ } else if (sz < PR_OUT_FIXED_PARAM_SIZE) { ++ /* Illegal request, Parameter list length error. This isn't fatal; ++ * we have read the data, send an error without closing the socket. ++ */ ++ scsi_build_sense(resp->sense, SENSE_CODE(INVALID_PARAM_LEN)); ++ sz = 0; ++ } ++ if (sz == 0) { ++ resp->result = CHECK_CONDITION; ++ close(client->fd); ++ client->fd = -1; ++ } ++ } ++ ++ req->fd = client->fd; ++ req->sz = sz; ++ client->fd = -1; ++ return sz; ++ ++out_close: ++ close(client->fd); ++ client->fd = -1; ++ return -1; ++} ++ ++static int coroutine_fn prh_write_response(PRHelperClient *client, ++ PRHelperRequest *req, ++ PRHelperResponse *resp, Error **errp) ++{ ++ ssize_t r; ++ size_t sz; ++ ++ if (req->cdb[0] == PERSISTENT_RESERVE_IN && resp->result == GOOD) { ++ assert(resp->sz <= req->sz && resp->sz <= sizeof(client->data)); ++ } else { ++ assert(resp->sz == 0); ++ } ++ ++ sz = resp->sz; ++ ++ resp->result = cpu_to_be32(resp->result); ++ resp->sz = cpu_to_be32(resp->sz); ++ r = qio_channel_write_all(QIO_CHANNEL(client->ioc), ++ (char *) resp, sizeof(*resp), errp); ++ if (r < 0) { ++ return r; ++ } ++ ++ r = qio_channel_write_all(QIO_CHANNEL(client->ioc), ++ (char *) client->data, ++ sz, errp); ++ return r < 0 ? r : 0; ++} ++ ++static void coroutine_fn prh_co_entry(void *opaque) ++{ ++ PRHelperClient *client = opaque; ++ Error *local_err = 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 = cpu_to_be32(0); ++ r = qio_channel_write_all(QIO_CHANNEL(client->ioc), ++ (char *) &flags, sizeof(flags), NULL); ++ if (r < 0) { ++ goto out; ++ } ++ ++ r = qio_channel_read_all(QIO_CHANNEL(client->ioc), ++ (char *) &flags, sizeof(flags), NULL); ++ if (be32_to_cpu(flags) != 0 || r < 0) { ++ goto out; ++ } ++ ++ while (atomic_read(&state) == RUNNING) { ++ PRHelperRequest req; ++ PRHelperResponse resp; ++ int sz; ++ ++ sz = prh_read_request(client, &req, &resp, &local_err); ++ if (sz < 0) { ++ break; ++ } ++ ++ if (sz > 0) { ++ num_active_sockets++; ++ if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { ++ r = do_pr_out(req.fd, req.cdb, resp.sense, ++ client->data, sz); ++ resp.sz = 0; ++ } else { ++ resp.sz = sizeof(client->data); ++ r = do_pr_in(req.fd, req.cdb, resp.sense, ++ client->data, &resp.sz); ++ resp.sz = MIN(resp.sz, sz); ++ } ++ num_active_sockets--; ++ close(req.fd); ++ if (r == -1) { ++ break; ++ } ++ resp.result = r; ++ } ++ ++ if (prh_write_response(client, &req, &resp, &local_err) < 0) { ++ break; ++ } ++ } ++ ++ if (local_err) { ++ if (verbose == 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 = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), ++ NULL); ++ if (!cioc) { ++ return TRUE; ++ } ++ ++ prh = g_new(PRHelperClient, 1); ++ prh->ioc = cioc; ++ prh->fd = -1; ++ prh->co = 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 != NULL) { ++ return "Unix socket can't be set when using socket activation"; ++ } ++ ++ return NULL; ++} ++ ++static void compute_default_paths(void) ++{ ++ if (!socket_path) { ++ socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); ++ } ++} ++ ++static void termsig_handler(int signum) ++{ ++ atomic_cmpxchg(&state, RUNNING, TERMINATE); ++ qemu_notify_event(); ++} ++ ++static void close_server_socket(void) ++{ ++ assert(server_ioc); ++ ++ g_source_remove(server_watch); ++ server_watch = -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 descriptors ++ * 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 != -1 ? uid : getuid(), ++ gid != -1 ? gid : getgid(), ++ CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING)) { ++ return -1; ++ } ++ ++ return 0; ++} ++#endif ++ ++int main(int argc, char **argv) ++{ ++ const char *sopt = "hVk:fdT:u:g:q"; ++ struct option lopt[] = { ++ { "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 = 0; ++ int quiet = 0; ++ char ch; ++ Error *local_err = NULL; ++ char *trace_file = NULL; ++ bool daemonize = false; ++ unsigned socket_activation; ++ ++ struct sigaction sa_sigterm; ++ memset(&sa_sigterm, 0, sizeof(sa_sigterm)); ++ sa_sigterm.sa_handler = termsig_handler; ++ sigaction(SIGTERM, &sa_sigterm, NULL); ++ sigaction(SIGINT, &sa_sigterm, NULL); ++ sigaction(SIGHUP, &sa_sigterm, NULL); ++ ++ signal(SIGPIPE, SIG_IGN); ++ ++ module_call_init(MODULE_INIT_TRACE); ++ module_call_init(MODULE_INIT_QOM); ++ qemu_add_opts(&qemu_trace_opts); ++ qemu_init_exec_dir(argv[0]); ++ ++ pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); ++ ++ while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { ++ switch (ch) { ++ case 'k': ++ socket_path = optarg; ++ if (socket_path[0] != '/') { ++ error_report("socket path must be absolute"); ++ exit(EXIT_FAILURE); ++ } ++ break; ++ case 'f': ++ pidfile = optarg; ++ break; ++#ifdef CONFIG_LIBCAP ++ case 'u': { ++ unsigned long res; ++ struct passwd *userinfo = getpwnam(optarg); ++ if (userinfo) { ++ uid = userinfo->pw_uid; ++ } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && ++ (uid_t)res == res) { ++ uid = res; ++ } else { ++ error_report("invalid user '%s'", optarg); ++ exit(EXIT_FAILURE); ++ } ++ break; ++ } ++ case 'g': { ++ unsigned long res; ++ struct group *groupinfo = getgrnam(optarg); ++ if (groupinfo) { ++ gid = groupinfo->gr_gid; ++ } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && ++ (gid_t)res == res) { ++ gid = 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 = true; ++ break; ++ case 'q': ++ quiet = 1; ++ break; ++ case 'T': ++ g_free(trace_file); ++ trace_file = trace_opt_parse(optarg); ++ break; ++ case 'V': ++ version(argv[0]); ++ exit(EXIT_SUCCESS); ++ break; ++ case 'h': ++ usage(argv[0]); ++ exit(EXIT_SUCCESS); ++ break; ++ case '?': ++ error_report("Try `%s --help' for more information.", argv[0]); ++ exit(EXIT_FAILURE); ++ } ++ } ++ ++ /* set verbosity */ ++ verbose = !quiet; ++ ++ if (!trace_init_backends()) { ++ exit(EXIT_FAILURE); ++ } ++ trace_init_file(trace_file); ++ qemu_set_log(LOG_TRACE); ++ ++ socket_activation = check_socket_activation(); ++ if (socket_activation == 0) { ++ SocketAddress saddr; ++ compute_default_paths(); ++ saddr = (SocketAddress){ ++ .type = SOCKET_ADDRESS_TYPE_UNIX, ++ .u.q_unix.path = g_strdup(socket_path) ++ }; ++ server_ioc = 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 = socket_activation_validate_opts(); ++ if (err_msg != 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 LISTEN_FDS > 1", ++ argv[0]); ++ exit(EXIT_FAILURE); ++ } ++ server_ioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD, ++ &local_err); ++ if (server_ioc == NULL) { ++ error_report("Failed to use socket activation: %s", ++ error_get_pretty(local_err)); ++ exit(EXIT_FAILURE); ++ } ++ socket_path = NULL; ++ } ++ ++ if (qemu_init_main_loop(&local_err)) { ++ error_report_err(local_err); ++ exit(EXIT_FAILURE); ++ } ++ ++ server_watch = 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); ++ } ++ write_pidfile(); ++ } ++ ++ state = RUNNING; ++ do { ++ main_loop_wait(false); ++ if (state == TERMINATE) { ++ state = TERMINATING; ++ close_server_socket(); ++ } ++ } while (num_active_sockets > 0); ++ ++ exit(EXIT_SUCCESS); ++} diff --git a/0013-scsi-add-multipath-support-to-qemu-pr-helper.patch b/0013-scsi-add-multipath-support-to-qemu-pr-helper.patch new file mode 100644 index 0000000..46ab354 --- /dev/null +++ b/0013-scsi-add-multipath-support-to-qemu-pr-helper.patch @@ -0,0 +1,675 @@ +From: Paolo Bonzini +Date: Tue, 22 Aug 2017 06:50:55 +0200 +Subject: [PATCH] scsi: add multipath support to qemu-pr-helper + +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 | 54 ++++++++ + docs/pr-manager.rst | 27 ++++ + include/scsi/utils.h | 4 + + scsi/qemu-pr-helper.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++++- + scsi/utils.c | 10 ++ + 6 files changed, 452 insertions(+), 3 deletions(-) + +diff --git a/Makefile b/Makefile +index f07c0d7e9c..060c089af9 100644 +--- a/Makefile ++++ b/Makefile +@@ -383,6 +383,9 @@ fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal + fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap + + 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 += -ludev -lmultipath -lmpathpersist ++endif + + 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 14bdf9bb31..9eeb3ebf70 100755 +--- a/configure ++++ b/configure +@@ -286,6 +286,7 @@ pixman="" + sdl="" + sdlabi="" + virtfs="" ++mpath="" + vnc="yes" + sparse="no" + vde="" +@@ -948,6 +949,10 @@ for opt do + ;; + --enable-virtfs) virtfs="yes" + ;; ++ --disable-mpath) mpath="no" ++ ;; ++ --enable-mpath) mpath="yes" ++ ;; + --disable-vnc) vnc="no" + ;; + --enable-vnc) vnc="yes" +@@ -1491,6 +1496,7 @@ disabled with --disable-FEATURE, default is enabled if 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) +@@ -3335,6 +3341,38 @@ else + pixman_libs="-L\$(BUILD_DIR)/pixman/pixman/.libs -lpixman-1" + fi + ++########################################## ++# libmpathpersist probe ++ ++if test "$mpath" != "no" ; then ++ cat > $TMPC < ++#include ++unsigned mpath_mx_alloc_len = 1024; ++int logsink; ++static struct config *multipath_conf; ++extern struct udev *udev; ++extern struct config *get_multipath_config(void); ++extern void put_multipath_config(struct config *conf); ++struct udev *udev; ++struct config *get_multipath_config(void) { return multipath_conf; } ++void put_multipath_config(struct config *conf) { } ++ ++int main(void) { ++ udev = udev_new(); ++ multipath_conf = mpath_lib_init(); ++ return 0; ++} ++EOF ++ if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then ++ mpathpersist=yes ++ else ++ mpathpersist=no ++ fi ++else ++ mpathpersist=no ++fi ++ + ########################################## + # libcap probe + +@@ -5080,12 +5118,24 @@ if test "$softmmu" = yes ; then + fi + virtfs=no + fi ++ if test "$mpath" != no && test "$mpathpersist" = yes ; then ++ mpath=yes ++ else ++ if test "$mpath" = yes; then ++ error_exit "Multipath requires libmpathpersist devel" ++ fi ++ mpath=no ++ fi + tools="$tools scsi/qemu-pr-helper\$(EXESUF)" + else + if test "$virtfs" = yes; then + error_exit "VirtFS is supported only on Linux" + fi + virtfs=no ++ if test "$mpath" = yes; then ++ error_exit "Multipath is supported only on Linux" ++ fi ++ mpath=no + fi + fi + +@@ -5332,6 +5382,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" = "yes" ; then + echo "VNC SASL support $vnc_sasl" +@@ -5779,6 +5830,9 @@ fi + if test "$virtfs" = "yes" ; then + echo "CONFIG_VIRTFS=y" >> $config_host_mak + fi ++if test "$mpath" = "yes" ; then ++ echo "CONFIG_MPATH=y" >> $config_host_mak ++fi + if test "$vhost_scsi" = "yes" ; then + echo "CONFIG_VHOST_SCSI=y" >> $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: + + -d, --daemon run in the background + -q, --quiet decrease verbosity ++-v, --verbose increase verbosity + -f, --pidfile=path PID file when running as a daemon + -k, --socket=path path to the socket + -T, --trace=trace-opts tracing options +@@ -82,3 +83,29 @@ its operation. To do this, add the following options: + + -u, --user=user user to drop privileges to + -g, --group=group 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 d301b31768..00a4bdb080 100644 +--- a/include/scsi/utils.h ++++ b/include/scsi/utils.h +@@ -72,10 +72,14 @@ extern const struct SCSISense sense_code_IO_ERROR; + 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, LUN Communication failure */ ++extern const struct SCSISense sense_code_LUN_COMM_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; ++/* Unit attention, SCSI bus reset */ ++extern const struct SCSISense sense_code_SCSI_BUS_RESET; + /* 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 */ +diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c +index e39efbd529..be6d1fbade 100644 +--- a/scsi/qemu-pr-helper.c ++++ b/scsi/qemu-pr-helper.c +@@ -30,6 +30,12 @@ + #include + #include + ++#ifdef CONFIG_MPATH ++#include ++#include ++#include ++#endif ++ + #include "qapi/error.h" + #include "qemu-common.h" + #include "qemu/cutils.h" +@@ -60,6 +66,7 @@ static enum { RUNNING, TERMINATE, TERMINATING } state; + static QIOChannelSocket *server_ioc; + static int server_watch; + static int num_active_sockets = 1; ++static int noisy; + static int verbose; + + #ifdef CONFIG_LIBCAP +@@ -204,9 +211,327 @@ static int do_sgio(int fd, const uint8_t *cdb, uint8_t *sense, + return r; + } + ++/* Device mapper interface */ ++ ++#ifdef CONFIG_MPATH ++#define CONTROL_PATH "/dev/mapper/control" ++ ++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] = DM_VERSION_MAJOR; ++ d.dm.version[1] = 0; ++ d.dm.version[2] = 0; ++ d.dm.data_size = 1024; ++ d.dm.data_start = 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 = fstat(fd, &st); ++ if (r < 0) { ++ perror("fstat"); ++ exit(1); ++ } ++ ++ dm->dev = st.st_rdev; ++ return dm_ioctl(ioc, dm); ++} ++ ++static void dm_init(void) ++{ ++ control_fd = open(CONTROL_PATH, O_RDWR); ++ if (control_fd < 0) { ++ perror("Cannot open " CONTROL_PATH); ++ exit(1); ++ } ++ struct dm_ioctl dm = { 0 }; ++ if (!dm_ioctl(DM_VERSION, &dm)) { ++ perror("ioctl"); ++ exit(1); ++ } ++ if (dm.version[0] != DM_VERSION_MAJOR) { ++ fprintf(stderr, "Unsupported device mapper interface"); ++ exit(1); ++ } ++} ++ ++/* Variables required by libmultipath and libmpathpersist. */ ++QEMU_BUILD_BUG_ON(PR_HELPER_DATA_SIZE > MPATH_MAX_PARAM_LEN); ++static struct config *multipath_conf; ++unsigned mpath_mx_alloc_len = PR_HELPER_DATA_SIZE; ++int logsink; ++struct udev *udev; ++ ++extern struct config *get_multipath_config(void); ++struct config *get_multipath_config(void) ++{ ++ return multipath_conf; ++} ++ ++extern void put_multipath_config(struct config *conf); ++void put_multipath_config(struct config *conf) ++{ ++} ++ ++static void multipath_pr_init(void) ++{ ++ udev = udev_new(); ++ multipath_conf = mpath_lib_init(); ++} ++ ++static int is_mpath(int fd) ++{ ++ struct dm_ioctl dm = { .flags = DM_NOFLUSH_FLAG }; ++ struct dm_target_spec *tgt; ++ ++ tgt = dm_dev_ioctl(fd, DM_TABLE_STATUS, &dm); ++ if (!tgt) { ++ if (errno == 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] = { TEST_UNIT_READY }; ++ int sz = 0; ++ return do_sgio(fd, cdb, sense, NULL, &sz, 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 = 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 = mpath_persistent_reserve_in(fd, rq_servact, &resp, noisy, verbose); ++ if (r == MPATH_PR_SUCCESS) { ++ switch (rq_servact) { ++ case MPATH_PRIN_RKEY_SA: ++ case MPATH_PRIN_RRES_SA: { ++ struct prin_readdescr *out = &resp.prin_descriptor.prin_readkeys; ++ assert(sz >= 8); ++ written = MIN(out->additional_length + 8, sz); ++ stl_be_p(&data[0], out->prgeneration); ++ stl_be_p(&data[4], out->additional_length); ++ memcpy(&data[8], out->key_list, written - 8); ++ break; ++ } ++ case MPATH_PRIN_RCAP_SA: { ++ struct prin_capdescr *out = &resp.prin_descriptor.prin_readcap; ++ assert(sz >= 6); ++ written = 6; ++ stw_be_p(&data[0], out->length); ++ data[2] = out->flags[0]; ++ data[3] = out->flags[1]; ++ stw_be_p(&data[4], out->pr_type_mask); ++ 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 = cdb[1]; ++ int rq_scope = cdb[2] >> 4; ++ int rq_type = cdb[2] & 0xf; ++ struct prout_param_descriptor paramp; ++ char transportids[PR_HELPER_DATA_SIZE]; ++ int r; ++ ++ 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: ++ break; ++ case MPATH_PROUT_REG_MOV_SA: ++ /* Not supported by struct prout_param_descriptor. */ ++ 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 = param[10]; ++ if (sz > PR_OUT_FIXED_PARAM_SIZE) { ++ size_t transportid_len; ++ int i, j; ++ if (sz < PR_OUT_FIXED_PARAM_SIZE + 4) { ++ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN)); ++ return CHECK_CONDITION; ++ } ++ transportid_len = ldl_be_p(¶m[24]) + PR_OUT_FIXED_PARAM_SIZE + 4; ++ if (transportid_len > sz) { ++ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); ++ return CHECK_CONDITION; ++ } ++ for (i = PR_OUT_FIXED_PARAM_SIZE + 4, j = 0; i < transportid_len; ) { ++ struct transportid *id = (struct transportid *) &transportids[j]; ++ int len; ++ ++ id->format_code = param[i] & 0xc0; ++ id->protocol_id = param[i] & 0x0f; ++ switch (param[i] & 0xcf) { ++ case 0: ++ /* FC transport. */ ++ if (i + 24 > transportid_len) { ++ goto illegal_req; ++ } ++ memcpy(id->n_port_name, ¶m[i + 8], 8); ++ j += offsetof(struct transportid, n_port_name[8]); ++ i += 24; ++ break; ++ case 3: ++ case 0x43: ++ /* iSCSI transport. */ ++ len = lduw_be_p(¶m[i + 2]); ++ if (len > 252 || (len & 3) || i + len + 4 > transportid_len) { ++ /* For format code 00, the standard says the maximum is 223 ++ * plus the NUL terminator. For format code 01 there is no ++ * maximum length, but libmpathpersist ignores the first ++ * byte of id->iscsi_name so our maximum is 252. ++ */ ++ goto illegal_req; ++ } ++ if (memchr(¶m[i + 4], 0, len) == NULL) { ++ goto illegal_req; ++ } ++ memcpy(id->iscsi_name, ¶m[i + 2], len + 2); ++ j += offsetof(struct transportid, iscsi_name[len + 2]); ++ i += len + 4; ++ break; ++ case 6: ++ /* SAS transport. */ ++ if (i + 24 > transportid_len) { ++ goto illegal_req; ++ } ++ memcpy(id->sas_address, ¶m[i + 4], 8); ++ j += offsetof(struct transportid, sas_address[8]); ++ i += 24; ++ break; ++ default: ++ illegal_req: ++ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); ++ return CHECK_CONDITION; ++ } ++ ++ paramp.trnptid_list[paramp.num_transportid++] = id; ++ } ++ } ++ ++ r = 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 *resp_sz) + { ++#ifdef CONFIG_MPATH ++ if (is_mpath(fd)) { ++ /* multipath_pr_in fills the whole input buffer. */ ++ return multipath_pr_in(fd, cdb, sense, data, *resp_sz); ++ } ++#endif ++ + return do_sgio(fd, cdb, sense, data, resp_sz, + SG_DXFER_FROM_DEV); + } +@@ -214,7 +539,14 @@ 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) + { +- int resp_sz = sz; ++ int resp_sz; ++#ifdef CONFIG_MPATH ++ if (is_mpath(fd)) { ++ return multipath_pr_out(fd, cdb, sense, param, sz); ++ } ++#endif ++ ++ resp_sz = sz; + return do_sgio(fd, cdb, sense, (uint8_t *)param, &resp_sz, + SG_DXFER_TO_DEV); + } +@@ -525,6 +857,14 @@ static int drop_privileges(void) + return -1; + } + ++#ifdef CONFIG_MPATH ++ /* For /dev/mapper/control ioctls */ ++ if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, ++ CAP_SYS_ADMIN) < 0) { ++ return -1; ++ } ++#endif ++ + /* Change user/group id, retaining the capabilities. Because file descriptors + * are passed via SCM_RIGHTS, we don't need supplementary groups (and in + * fact the helper can run as "nobody"). +@@ -541,7 +881,7 @@ static int drop_privileges(void) + + int main(int argc, char **argv) + { +- const char *sopt = "hVk:fdT:u:g:q"; ++ const char *sopt = "hVk:fdT:u:g:vq"; + struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, +@@ -551,10 +891,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 = 0; ++ int loglevel = 1; + int quiet = 0; + char ch; + Error *local_err = NULL; +@@ -631,6 +973,9 @@ int main(int argc, char **argv) + case 'q': + quiet = 1; + break; ++ case 'v': ++ ++loglevel; ++ break; + case 'T': + g_free(trace_file); + trace_file = trace_opt_parse(optarg); +@@ -650,7 +995,8 @@ int main(int argc, char **argv) + } + + /* set verbosity */ +- verbose = !quiet; ++ noisy = !quiet && (loglevel >= 3); ++ verbose = quiet ? 0 : MIN(loglevel, 3); + + if (!trace_init_backends()) { + exit(EXIT_FAILURE); +@@ -658,6 +1004,11 @@ int main(int argc, char **argv) + trace_init_file(trace_file); + qemu_set_log(LOG_TRACE); + ++#ifdef CONFIG_MPATH ++ dm_init(); ++ multipath_pr_init(); ++#endif ++ + socket_activation = check_socket_activation(); + if (socket_activation == 0) { + SocketAddress saddr; +diff --git a/scsi/utils.c b/scsi/utils.c +index fab60bdf20..5684951b12 100644 +--- a/scsi/utils.c ++++ b/scsi/utils.c +@@ -206,6 +206,11 @@ const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { + .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 + }; + ++/* Command aborted, LUN Communication Failure */ ++const struct SCSISense sense_code_LUN_COMM_FAILURE = { ++ .key = ABORTED_COMMAND, .asc = 0x08, .ascq = 0x00 ++}; ++ + /* Unit attention, Capacity data has changed */ + const struct SCSISense sense_code_CAPACITY_CHANGED = { + .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 +@@ -216,6 +221,11 @@ const struct SCSISense sense_code_RESET = { + .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 + }; + ++/* Unit attention, SCSI bus reset */ ++const struct SCSISense sense_code_SCSI_BUS_RESET = { ++ .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x02 ++}; ++ + /* Unit attention, No medium */ + const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { + .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 diff --git a/0014-scsi-add-persistent-reservation-manager-using-qemu-p.patch b/0014-scsi-add-persistent-reservation-manager-using-qemu-p.patch new file mode 100644 index 0000000..af6ecc9 --- /dev/null +++ b/0014-scsi-add-persistent-reservation-manager-using-qemu-p.patch @@ -0,0 +1,330 @@ +From: Paolo Bonzini +Date: Mon, 21 Aug 2017 18:58:56 +0200 +Subject: [PATCH] scsi: add persistent reservation manager using qemu-pr-helper + +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 | 302 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 303 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 += utils.o + +-block-obj-$(CONFIG_LINUX) += pr-manager.o ++block-obj-$(CONFIG_LINUX) += 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..82ff6b6123 +--- /dev/null ++++ b/scsi/pr-manager-helper.c +@@ -0,0 +1,302 @@ ++/* ++ * Persistent reservation manager that talks to qemu-pr-helper ++ * ++ * Copyright (c) 2017 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This code is licensed under the LGPL v2.1 or later. ++ * ++ */ ++ ++#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) \ ++ OBJECT_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 = qio_channel_read_all(pr_mgr->ioc, buf, sz, errp); ++ ++ if (r < 0) { ++ object_unref(OBJECT(pr_mgr->ioc)); ++ pr_mgr->ioc = NULL; ++ return -EINVAL; ++ } ++ ++ return 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 = (fd != -1); ++ while (sz > 0) { ++ struct iovec iov; ++ ssize_t n_written; ++ ++ iov.iov_base = (void *)buf; ++ iov.iov_len = sz; ++ n_written = qio_channel_writev_full(QIO_CHANNEL(pr_mgr->ioc), &iov, 1, ++ nfds ? &fd : NULL, nfds, errp); ++ ++ if (n_written <= 0) { ++ assert(n_written != QIO_CHANNEL_ERR_BLOCK); ++ object_unref(OBJECT(pr_mgr->ioc)); ++ return n_written < 0 ? -EINVAL : 0; ++ } ++ ++ nfds = 0; ++ buf += n_written; ++ sz -= n_written; ++ } ++ ++ return 0; ++} ++ ++/* Called with lock held. */ ++static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, ++ Error **errp) ++{ ++ char *path = g_strdup(pr_mgr->path); ++ SocketAddress saddr = { ++ .type = SOCKET_ADDRESS_TYPE_UNIX, ++ .u.q_unix.path = path ++ }; ++ QIOChannelSocket *sioc = qio_channel_socket_new(); ++ Error *local_err = NULL; ++ ++ uint32_t flags; ++ int r; ++ ++ assert(!pr_mgr->ioc); ++ qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper"); ++ qio_channel_socket_connect_sync(sioc, ++ &saddr, ++ &local_err); ++ g_free(path); ++ 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 = QIO_CHANNEL(sioc); ++ ++ /* A simple feature negotation protocol, even though there is ++ * no optional feature right now. ++ */ ++ r = pr_manager_helper_read(pr_mgr, &flags, sizeof(flags), errp); ++ if (r < 0) { ++ goto out_close; ++ } ++ ++ flags = 0; ++ r = pr_manager_helper_write(pr_mgr, -1, &flags, sizeof(flags), errp); ++ if (r < 0) { ++ goto out_close; ++ } ++ ++ return 0; ++ ++out_close: ++ object_unref(OBJECT(pr_mgr->ioc)); ++ pr_mgr->ioc = NULL; ++ return r; ++} ++ ++static int pr_manager_helper_run(PRManager *p, ++ int fd, struct sg_io_hdr *io_hdr) ++{ ++ PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(p); ++ ++ uint32_t len; ++ PRHelperResponse resp; ++ int ret; ++ int expected_dir; ++ int attempts; ++ uint8_t cdb[PR_HELPER_CDB_SIZE] = { 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] == PERSISTENT_RESERVE_OUT || cdb[0] == PERSISTENT_RESERVE_IN); ++ expected_dir = ++ (cdb[0] == PERSISTENT_RESERVE_OUT ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV); ++ if (io_hdr->dxfer_direction != expected_dir) { ++ return -EINVAL; ++ } ++ ++ len = scsi_cdb_xfer(cdb); ++ if (io_hdr->dxfer_len < len || len > PR_HELPER_DATA_SIZE) { ++ return -EINVAL; ++ } ++ ++ qemu_mutex_lock(&pr_mgr->lock); ++ ++ /* Try to reconnect while sending the CDB. */ ++ for (attempts = 0; attempts < PR_MAX_RECONNECT_ATTEMPTS; attempts++) { ++ if (!pr_mgr->ioc) { ++ ret = pr_manager_helper_initialize(pr_mgr, NULL); ++ if (ret < 0) { ++ qemu_mutex_unlock(&pr_mgr->lock); ++ g_usleep(G_USEC_PER_SEC); ++ qemu_mutex_lock(&pr_mgr->lock); ++ continue; ++ } ++ } ++ ++ ret = pr_manager_helper_write(pr_mgr, fd, cdb, ARRAY_SIZE(cdb), NULL); ++ if (ret >= 0) { ++ break; ++ } ++ } ++ if (ret < 0) { ++ 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 == SG_DXFER_TO_DEV) { ++ io_hdr->resid = io_hdr->dxfer_len - len; ++ ret = pr_manager_helper_write(pr_mgr, -1, io_hdr->dxferp, len, NULL); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ret = pr_manager_helper_read(pr_mgr, &resp, sizeof(resp), NULL); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ resp.result = be32_to_cpu(resp.result); ++ resp.sz = be32_to_cpu(resp.sz); ++ if (io_hdr->dxfer_direction == SG_DXFER_FROM_DEV) { ++ assert(resp.sz <= io_hdr->dxfer_len); ++ ret = pr_manager_helper_read(pr_mgr, io_hdr->dxferp, resp.sz, NULL); ++ if (ret < 0) { ++ goto out; ++ } ++ io_hdr->resid = io_hdr->dxfer_len - resp.sz; ++ } else { ++ assert(resp.sz == 0); ++ } ++ ++ io_hdr->status = resp.result; ++ if (resp.result == CHECK_CONDITION) { ++ io_hdr->driver_status = SG_ERR_DRIVER_SENSE; ++ io_hdr->sb_len_wr = 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 = scsi_build_sense(io_hdr->sbp, ++ SENSE_CODE(LUN_COMM_FAILURE)); ++ io_hdr->driver_status = SG_ERR_DRIVER_SENSE; ++ io_hdr->sb_len_wr = MIN(io_hdr->mx_sb_len, sense_len); ++ io_hdr->status = CHECK_CONDITION; ++ } ++ qemu_mutex_unlock(&pr_mgr->lock); ++ return ret; ++} ++ ++static void pr_manager_helper_complete(UserCreatable *uc, Error **errp) ++{ ++ PRManagerHelper *pr_mgr = 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 = PR_MANAGER_HELPER(obj); ++ ++ return g_strdup(pr_mgr->path); ++} ++ ++static void set_path(Object *obj, const char *str, Error **errp) ++{ ++ PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); ++ ++ g_free(pr_mgr->path); ++ pr_mgr->path = g_strdup(str); ++} ++ ++static void pr_manager_helper_instance_finalize(Object *obj) ++{ ++ PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); ++ ++ object_unref(OBJECT(pr_mgr->ioc)); ++ qemu_mutex_destroy(&pr_mgr->lock); ++} ++ ++static void pr_manager_helper_instance_init(Object *obj) ++{ ++ PRManagerHelper *pr_mgr = 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 = PR_MANAGER_CLASS(klass); ++ UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); ++ ++ object_class_property_add_str(klass, "path", get_path, set_path, ++ &error_abort); ++ uc_klass->complete = pr_manager_helper_complete; ++ prmgr_klass->run = pr_manager_helper_run; ++} ++ ++static const TypeInfo pr_manager_helper_info = { ++ .parent = TYPE_PR_MANAGER, ++ .name = TYPE_PR_MANAGER_HELPER, ++ .instance_size = sizeof(PRManagerHelper), ++ .instance_init = pr_manager_helper_instance_init, ++ .instance_finalize = pr_manager_helper_instance_finalize, ++ .class_init = 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); diff --git a/0101-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch b/0101-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch new file mode 100644 index 0000000..8a8b374 --- /dev/null +++ b/0101-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch @@ -0,0 +1,30 @@ +From: "Daniel P. Berrange" +Date: Tue, 29 Aug 2017 17:03:30 +0100 +Subject: [PATCH] crypto: fix test cert generation to not use SHA1 algorithm + +GNUTLS 3.6.0 marked SHA1 as untrusted for certificates. +Unfortunately the gnutls_x509_crt_sign() method we are +using to create certificates in the test suite is fixed +to always use SHA1. We must switch to a different method +and explicitly ask for SHA256. + +Reviewed-by: Eric Blake +Signed-off-by: Daniel P. Berrange +--- + tests/crypto-tls-x509-helpers.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c +index 64073d3bd3..173d4e28fb 100644 +--- a/tests/crypto-tls-x509-helpers.c ++++ b/tests/crypto-tls-x509-helpers.c +@@ -406,7 +406,8 @@ test_tls_generate_cert(QCryptoTLSTestCertReq *req, + * If no 'ca' is set then we are self signing + * the cert. This is done for the root CA certs + */ +- err = gnutls_x509_crt_sign(crt, ca ? ca : crt, privkey); ++ err = gnutls_x509_crt_sign2(crt, ca ? ca : crt, privkey, ++ GNUTLS_DIG_SHA256, 0); + if (err < 0) { + g_critical("Failed to sign certificate %s", + gnutls_strerror(err)); diff --git a/0102-io-fix-check-for-handshake-completion-in-TLS-test.patch b/0102-io-fix-check-for-handshake-completion-in-TLS-test.patch new file mode 100644 index 0000000..261cb76 --- /dev/null +++ b/0102-io-fix-check-for-handshake-completion-in-TLS-test.patch @@ -0,0 +1,30 @@ +From: "Daniel P. Berrange" +Date: Tue, 29 Aug 2017 17:04:52 +0100 +Subject: [PATCH] io: fix check for handshake completion in TLS test + +The TLS I/O channel test had mistakenly used && instead +of || when checking for handshake completion. As a +result it could terminate the handshake process before +it had actually completed. This was harmless before but +changes in GNUTLS 3.6.0 exposed this bug and caused the +test suite to fail. + +Reviewed-by: Eric Blake +Signed-off-by: Daniel P. Berrange +--- + tests/test-io-channel-tls.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c +index 8eaa208e1b..e7c80f46cf 100644 +--- a/tests/test-io-channel-tls.c ++++ b/tests/test-io-channel-tls.c +@@ -218,7 +218,7 @@ static void test_io_channel_tls(const void *opaque) + mainloop = g_main_context_default(); + do { + g_main_context_iteration(mainloop, TRUE); +- } while (!clientHandshake.finished && ++ } while (!clientHandshake.finished || + !serverHandshake.finished); + + g_assert(clientHandshake.failed == data->expectClientFail); diff --git a/0103-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch b/0103-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch new file mode 100644 index 0000000..744ca56 --- /dev/null +++ b/0103-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch @@ -0,0 +1,30 @@ +From: "Daniel P. Berrange" +Date: Fri, 21 Jul 2017 12:47:39 +0100 +Subject: [PATCH] io: fix temp directory used by test-io-channel-tls test + +The test-io-channel-tls test was mistakenly using two of the +same directories as test-crypto-tlssession. This causes a +sporadic failure when using make -j$BIGNUM. + +Reported-by: Dr. David Alan Gilbert +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Daniel P. Berrange +--- + tests/test-io-channel-tls.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c +index e7c80f46cf..a210d01ba5 100644 +--- a/tests/test-io-channel-tls.c ++++ b/tests/test-io-channel-tls.c +@@ -127,8 +127,8 @@ static void test_io_channel_tls(const void *opaque) + /* We'll use this for our fake client-server connection */ + g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); + +-#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" +-#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" ++#define CLIENT_CERT_DIR "tests/test-io-channel-tls-client/" ++#define SERVER_CERT_DIR "tests/test-io-channel-tls-server/" + mkdir(CLIENT_CERT_DIR, 0700); + mkdir(SERVER_CERT_DIR, 0700); + diff --git a/0104-spapr-fallback-to-raw-mode-if-best-compat-mode-canno.patch b/0104-spapr-fallback-to-raw-mode-if-best-compat-mode-canno.patch new file mode 100644 index 0000000..8e8b6fc --- /dev/null +++ b/0104-spapr-fallback-to-raw-mode-if-best-compat-mode-canno.patch @@ -0,0 +1,76 @@ +From: Greg Kurz +Date: Thu, 17 Aug 2017 13:23:50 +0200 +Subject: [PATCH] spapr: fallback to raw mode if best compat mode cannot be set + during CAS + +KVM PR doesn't allow to set a compat mode. This causes ppc_set_compat_all() +to fail and we return H_HARDWARE to the guest right away. + +This is excessive: even if we favor compat mode since commit 152ef803ceb19, +we should at least fallback to raw mode if the guest supports it. + +This patch modifies cas_check_pvr() so that it also reports that the real +PVR was found in the table supplied by the guest. Note that this is only +makes sense if raw mode isn't explicitely disabled (ie, the user didn't +set the machine "max-cpu-compat" property). If this is the case, we can +simply ignore ppc_set_compat_all() failures, and let the guest run in raw +mode. + +Signed-off-by: Greg Kurz +Signed-off-by: David Gibson +(cherry picked from commit cc7b35b169e96600c09947a31c610c84a3eda3ff) +--- + hw/ppc/spapr_hcall.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c +index 07b3da8dc4..2f4c4f59e1 100644 +--- a/hw/ppc/spapr_hcall.c ++++ b/hw/ppc/spapr_hcall.c +@@ -1441,7 +1441,8 @@ static target_ulong h_signal_sys_reset(PowerPCCPU *cpu, + } + + static uint32_t cas_check_pvr(sPAPRMachineState *spapr, PowerPCCPU *cpu, +- target_ulong *addr, Error **errp) ++ target_ulong *addr, bool *raw_mode_supported, ++ Error **errp) + { + bool explicit_match = false; /* Matched the CPU's real PVR */ + uint32_t max_compat = spapr->max_compat_pvr; +@@ -1481,6 +1482,8 @@ static uint32_t cas_check_pvr(sPAPRMachineState *spapr, PowerPCCPU *cpu, + return 0; + } + ++ *raw_mode_supported = explicit_match; ++ + /* Parsing finished */ + trace_spapr_cas_pvr(cpu->compat_pvr, explicit_match, best_compat); + +@@ -1499,8 +1502,9 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, + sPAPROptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; + bool guest_radix; + Error *local_err = NULL; ++ bool raw_mode_supported = false; + +- cas_pvr = cas_check_pvr(spapr, cpu, &addr, &local_err); ++ cas_pvr = cas_check_pvr(spapr, cpu, &addr, &raw_mode_supported, &local_err); + if (local_err) { + error_report_err(local_err); + return H_HARDWARE; +@@ -1510,8 +1514,14 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, + if (cpu->compat_pvr != cas_pvr) { + ppc_set_compat_all(cas_pvr, &local_err); + if (local_err) { +- error_report_err(local_err); +- return H_HARDWARE; ++ /* We fail to set compat mode (likely because running with KVM PR), ++ * but maybe we can fallback to raw mode if the guest supports it. ++ */ ++ if (!raw_mode_supported) { ++ error_report_err(local_err); ++ return H_HARDWARE; ++ } ++ local_err = NULL; + } + } + diff --git a/0105-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch b/0105-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch new file mode 100644 index 0000000..a187fe8 --- /dev/null +++ b/0105-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch @@ -0,0 +1,40 @@ +From: Prasad J Pandit +Date: Mon, 16 Oct 2017 14:21:59 +0200 +Subject: [PATCH] 9pfs: use g_malloc0 to allocate space for xattr + +9p back-end first queries the size of an extended attribute, +allocates space for it via g_malloc() and then retrieves its +value into allocated buffer. Race between querying attribute +size and retrieving its could lead to memory bytes disclosure. +Use g_malloc0() to avoid it. + +Reported-by: Tuomas Tynkkynen +Signed-off-by: Prasad J Pandit +Signed-off-by: Greg Kurz +(cherry picked from commit 7bd92756303f2158a68d5166264dc30139b813b6) +--- + hw/9pfs/9p.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c +index 8e9490c5f5..c41c0eb106 100644 +--- a/hw/9pfs/9p.c ++++ b/hw/9pfs/9p.c +@@ -3236,7 +3236,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) + xattr_fidp->fid_type = P9_FID_XATTR; + xattr_fidp->fs.xattr.xattrwalk_fid = true; + if (size) { +- xattr_fidp->fs.xattr.value = g_malloc(size); ++ xattr_fidp->fs.xattr.value = g_malloc0(size); + err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, + xattr_fidp->fs.xattr.value, + xattr_fidp->fs.xattr.len); +@@ -3269,7 +3269,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) + xattr_fidp->fid_type = P9_FID_XATTR; + xattr_fidp->fs.xattr.xattrwalk_fid = true; + if (size) { +- xattr_fidp->fs.xattr.value = g_malloc(size); ++ xattr_fidp->fs.xattr.value = g_malloc0(size); + err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, + &name, xattr_fidp->fs.xattr.value, + xattr_fidp->fs.xattr.len); diff --git a/0106-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch b/0106-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch new file mode 100644 index 0000000..252e76a --- /dev/null +++ b/0106-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch @@ -0,0 +1,51 @@ +From: "Daniel P. Berrange" +Date: Mon, 9 Oct 2017 14:43:42 +0100 +Subject: [PATCH] io: monitor encoutput buffer size from websocket GSource + +The websocket GSource is monitoring the size of the rawoutput +buffer to determine if the channel can accepts more writes. +The rawoutput buffer, however, is merely a temporary staging +buffer before data is copied into the encoutput buffer. Thus +its size will always be zero when the GSource runs. + +This flaw causes the encoutput buffer to grow without bound +if the other end of the underlying data channel doesn't +read data being sent. This can be seen with VNC if a client +is on a slow WAN link and the guest OS is sending many screen +updates. A malicious VNC client can act like it is on a slow +link by playing a video in the guest and then reading data +very slowly, causing QEMU host memory to expand arbitrarily. + +This issue is assigned CVE-2017-15268, publically reported in + + https://bugs.launchpad.net/qemu/+bug/1718964 + +Reviewed-by: Eric Blake +Signed-off-by: Daniel P. Berrange +(cherry picked from commit a7b20a8efa28e5f22c26c06cd06c2f12bc863493) +--- + io/channel-websock.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/io/channel-websock.c b/io/channel-websock.c +index 5a3badbec2..c02c2a66c9 100644 +--- a/io/channel-websock.c ++++ b/io/channel-websock.c +@@ -26,7 +26,7 @@ + #include "trace.h" + + +-/* Max amount to allow in rawinput/rawoutput buffers */ ++/* Max amount to allow in rawinput/encoutput buffers */ + #define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192 + + #define QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN 24 +@@ -1006,7 +1006,7 @@ qio_channel_websock_source_prepare(GSource *source, + if (wsource->wioc->rawinput.offset) { + cond |= G_IO_IN; + } +- if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { ++ if (wsource->wioc->encoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { + cond |= G_IO_OUT; + } + diff --git a/1001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch b/1001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch deleted file mode 100644 index f4d92ab..0000000 --- a/1001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch +++ /dev/null @@ -1,384 +0,0 @@ -From 758d848ef10835108a75187d1a1a0418167f04b2 Mon Sep 17 00:00:00 2001 -From: "Daniel P. Berrange" -Date: Wed, 30 Aug 2017 14:53:59 +0100 -Subject: [PATCH 01/15] io: add new qio_channel_{readv, writev, read, - write}_all functions - -These functions wait until they are able to read / write the full -requested data buffer(s). - -Reviewed-by: Eric Blake -Signed-off-by: Daniel P. Berrange ---- - include/io/channel.h | 90 +++++++++++++++++++++++++++++++++++++++ - io/channel.c | 94 +++++++++++++++++++++++++++++++++++++++++ - tests/io-channel-helpers.c | 102 ++++----------------------------------------- - 3 files changed, 193 insertions(+), 93 deletions(-) - -diff --git a/include/io/channel.h b/include/io/channel.h -index db9bb022a1..e11a62ea50 100644 ---- a/include/io/channel.h -+++ b/include/io/channel.h -@@ -269,6 +269,58 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, - Error **errp); - - /** -+ * qio_channel_readv_all: -+ * @ioc: the channel object -+ * @iov: the array of memory regions to read data into -+ * @niov: the length of the @iov array -+ * @errp: pointer to a NULL-initialized error object -+ * -+ * Read data from the IO channel, storing it in the -+ * memory regions referenced by @iov. Each element -+ * in the @iov will be fully populated with data -+ * before the next one is used. The @niov parameter -+ * specifies the total number of elements in @iov. -+ * -+ * The function will wait for all requested data -+ * to be read, yielding from the current coroutine -+ * if required. -+ * -+ * If end-of-file occurs before all requested data -+ * has been read, an error will be reported. -+ * -+ * Returns: 0 if all bytes were read, or -1 on error -+ */ -+int qio_channel_readv_all(QIOChannel *ioc, -+ const struct iovec *iov, -+ size_t niov, -+ Error **errp); -+ -+ -+/** -+ * qio_channel_writev_all: -+ * @ioc: the channel object -+ * @iov: the array of memory regions to write data from -+ * @niov: the length of the @iov array -+ * @errp: pointer to a NULL-initialized error object -+ * -+ * Write data to the IO channel, reading it from the -+ * memory regions referenced by @iov. Each element -+ * in the @iov will be fully sent, before the next -+ * one is used. The @niov parameter specifies the -+ * total number of elements in @iov. -+ * -+ * The function will wait for all requested data -+ * to be written, yielding from the current coroutine -+ * if required. -+ * -+ * Returns: 0 if all bytes were written, or -1 on error -+ */ -+int qio_channel_writev_all(QIOChannel *ioc, -+ const struct iovec *iov, -+ size_t niov, -+ Error **erp); -+ -+/** - * qio_channel_readv: - * @ioc: the channel object - * @iov: the array of memory regions to read data into -@@ -331,6 +383,44 @@ ssize_t qio_channel_write(QIOChannel *ioc, - Error **errp); - - /** -+ * 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. If end-of-file -+ * occurs it will return an error rather than a short-read. Otherwise -+ * behaves as qio_channel_read(). -+ * -+ * Returns: 0 if all bytes were read, or -1 on error -+ */ -+int qio_channel_read_all(QIOChannel *ioc, -+ char *buf, -+ size_t buflen, -+ Error **errp); -+/** -+ * 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(). -+ * -+ * Returns: 0 if all bytes were written, or -1 on error -+ */ -+int 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..5e8c2f0a91 100644 ---- a/io/channel.c -+++ b/io/channel.c -@@ -22,6 +22,7 @@ - #include "io/channel.h" - #include "qapi/error.h" - #include "qemu/main-loop.h" -+#include "qemu/iov.h" - - bool qio_channel_has_feature(QIOChannel *ioc, - QIOChannelFeature feature) -@@ -85,6 +86,79 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, - } - - -+ -+int qio_channel_readv_all(QIOChannel *ioc, -+ const struct iovec *iov, -+ size_t niov, -+ Error **errp) -+{ -+ int ret = -1; -+ struct iovec *local_iov = g_new(struct iovec, niov); -+ struct iovec *local_iov_head = local_iov; -+ unsigned int nlocal_iov = niov; -+ -+ nlocal_iov = iov_copy(local_iov, nlocal_iov, -+ iov, niov, -+ 0, iov_size(iov, niov)); -+ -+ while (nlocal_iov > 0) { -+ ssize_t len; -+ len = qio_channel_readv(ioc, local_iov, nlocal_iov, errp); -+ if (len == QIO_CHANNEL_ERR_BLOCK) { -+ qio_channel_wait(ioc, G_IO_IN); -+ continue; -+ } else if (len < 0) { -+ goto cleanup; -+ } else if (len == 0) { -+ error_setg(errp, -+ "Unexpected end-of-file before all bytes were read"); -+ goto cleanup; -+ } -+ -+ iov_discard_front(&local_iov, &nlocal_iov, len); -+ } -+ -+ ret = 0; -+ -+ cleanup: -+ g_free(local_iov_head); -+ return ret; -+} -+ -+int qio_channel_writev_all(QIOChannel *ioc, -+ const struct iovec *iov, -+ size_t niov, -+ Error **errp) -+{ -+ int ret = -1; -+ struct iovec *local_iov = g_new(struct iovec, niov); -+ struct iovec *local_iov_head = local_iov; -+ unsigned int nlocal_iov = niov; -+ -+ nlocal_iov = iov_copy(local_iov, nlocal_iov, -+ iov, niov, -+ 0, iov_size(iov, niov)); -+ -+ while (nlocal_iov > 0) { -+ ssize_t len; -+ len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp); -+ if (len == QIO_CHANNEL_ERR_BLOCK) { -+ qio_channel_wait(ioc, G_IO_OUT); -+ continue; -+ } -+ if (len < 0) { -+ goto cleanup; -+ } -+ -+ iov_discard_front(&local_iov, &nlocal_iov, len); -+ } -+ -+ ret = 0; -+ cleanup: -+ g_free(local_iov_head); -+ return ret; -+} -+ - ssize_t qio_channel_readv(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, -@@ -123,6 +197,26 @@ ssize_t qio_channel_write(QIOChannel *ioc, - } - - -+int qio_channel_read_all(QIOChannel *ioc, -+ char *buf, -+ size_t buflen, -+ Error **errp) -+{ -+ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; -+ return qio_channel_readv_all(ioc, &iov, 1, errp); -+} -+ -+ -+int qio_channel_write_all(QIOChannel *ioc, -+ const char *buf, -+ size_t buflen, -+ Error **errp) -+{ -+ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; -+ return qio_channel_writev_all(ioc, &iov, 1, errp); -+} -+ -+ - int qio_channel_set_blocking(QIOChannel *ioc, - bool enabled, - Error **errp) -diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c -index 05e5579cf8..5430e1389d 100644 ---- a/tests/io-channel-helpers.c -+++ b/tests/io-channel-helpers.c -@@ -21,6 +21,7 @@ - #include "qemu/osdep.h" - #include "io-channel-helpers.h" - #include "qapi/error.h" -+#include "qemu/iov.h" - - struct QIOChannelTest { - QIOChannel *src; -@@ -37,77 +38,17 @@ struct QIOChannelTest { - }; - - --static void test_skip_iovec(struct iovec **iov, -- size_t *niov, -- size_t skip, -- struct iovec *old) --{ -- size_t offset = 0; -- size_t i; -- -- for (i = 0; i < *niov; i++) { -- if (skip < (*iov)[i].iov_len) { -- old->iov_len = (*iov)[i].iov_len; -- old->iov_base = (*iov)[i].iov_base; -- -- (*iov)[i].iov_len -= skip; -- (*iov)[i].iov_base += skip; -- break; -- } else { -- skip -= (*iov)[i].iov_len; -- -- if (i == 0 && old->iov_base) { -- (*iov)[i].iov_len = old->iov_len; -- (*iov)[i].iov_base = old->iov_base; -- old->iov_len = 0; -- old->iov_base = NULL; -- } -- -- offset++; -- } -- } -- -- *iov = *iov + offset; -- *niov -= offset; --} -- -- - /* This thread sends all data using iovecs */ - static gpointer test_io_thread_writer(gpointer opaque) - { - QIOChannelTest *data = opaque; -- struct iovec *iov = data->inputv; -- size_t niov = data->niov; -- struct iovec old = { 0 }; - - qio_channel_set_blocking(data->src, data->blocking, NULL); - -- while (niov) { -- ssize_t ret; -- ret = qio_channel_writev(data->src, -- iov, -- niov, -- &data->writeerr); -- if (ret == QIO_CHANNEL_ERR_BLOCK) { -- if (data->blocking) { -- error_setg(&data->writeerr, -- "Unexpected I/O blocking"); -- break; -- } else { -- qio_channel_wait(data->src, -- G_IO_OUT); -- continue; -- } -- } else if (ret < 0) { -- break; -- } else if (ret == 0) { -- error_setg(&data->writeerr, -- "Unexpected zero length write"); -- break; -- } -- -- test_skip_iovec(&iov, &niov, ret, &old); -- } -+ qio_channel_writev_all(data->src, -+ data->inputv, -+ data->niov, -+ &data->writeerr); - - return NULL; - } -@@ -117,38 +58,13 @@ static gpointer test_io_thread_writer(gpointer opaque) - static gpointer test_io_thread_reader(gpointer opaque) - { - QIOChannelTest *data = opaque; -- struct iovec *iov = data->outputv; -- size_t niov = data->niov; -- struct iovec old = { 0 }; - - qio_channel_set_blocking(data->dst, data->blocking, NULL); - -- while (niov) { -- ssize_t ret; -- -- ret = qio_channel_readv(data->dst, -- iov, -- niov, -- &data->readerr); -- -- if (ret == QIO_CHANNEL_ERR_BLOCK) { -- if (data->blocking) { -- error_setg(&data->readerr, -- "Unexpected I/O blocking"); -- break; -- } else { -- qio_channel_wait(data->dst, -- G_IO_IN); -- continue; -- } -- } else if (ret < 0) { -- break; -- } else if (ret == 0) { -- break; -- } -- -- test_skip_iovec(&iov, &niov, ret, &old); -- } -+ qio_channel_readv_all(data->dst, -+ data->outputv, -+ data->niov, -+ &data->readerr); - - return NULL; - } --- -2.13.5 - diff --git a/1002-io-Yield-rather-than-wait-when-already-in-coroutine.patch b/1002-io-Yield-rather-than-wait-when-already-in-coroutine.patch deleted file mode 100644 index db73177..0000000 --- a/1002-io-Yield-rather-than-wait-when-already-in-coroutine.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 5171f2cd3612ce2772b272e4dd8119fdb0c06124 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Tue, 5 Sep 2017 14:11:12 -0500 -Subject: [PATCH 02/15] io: Yield rather than wait when already in coroutine - -The new qio_channel_{read,write}{,v}_all functions are documented -as yielding until data is available. When used on a blocking -channel, this yield is done via qio_channel_wait() which spawns -a nested event loop under the hood (so it is that secondary loop -which yields as needed); but if we are already in a coroutine (at -which point QIO_CHANNEL_ERR_BLOCK is only possible if we are a -non-blocking channel), we want to yield the current coroutine -instead of spawning a nested event loop. - -Signed-off-by: Eric Blake -Message-Id: <20170905191114.5959-2-eblake@redhat.com> -Acked-by: Daniel P. Berrange -[commit message updated] -Signed-off-by: Eric Blake ---- - io/channel.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/io/channel.c b/io/channel.c -index 5e8c2f0a91..9e62794cab 100644 ---- a/io/channel.c -+++ b/io/channel.c -@@ -105,7 +105,11 @@ int qio_channel_readv_all(QIOChannel *ioc, - ssize_t len; - len = qio_channel_readv(ioc, local_iov, nlocal_iov, errp); - if (len == QIO_CHANNEL_ERR_BLOCK) { -- qio_channel_wait(ioc, G_IO_IN); -+ if (qemu_in_coroutine()) { -+ qio_channel_yield(ioc, G_IO_IN); -+ } else { -+ qio_channel_wait(ioc, G_IO_IN); -+ } - continue; - } else if (len < 0) { - goto cleanup; -@@ -143,7 +147,11 @@ int qio_channel_writev_all(QIOChannel *ioc, - ssize_t len; - len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp); - if (len == QIO_CHANNEL_ERR_BLOCK) { -- qio_channel_wait(ioc, G_IO_OUT); -+ if (qemu_in_coroutine()) { -+ qio_channel_yield(ioc, G_IO_OUT); -+ } else { -+ qio_channel_wait(ioc, G_IO_OUT); -+ } - continue; - } - if (len < 0) { --- -2.13.5 - diff --git a/1003-scsi-bus-correct-responses-for-INQUIRY-and-REQUEST-S.patch b/1003-scsi-bus-correct-responses-for-INQUIRY-and-REQUEST-S.patch deleted file mode 100644 index a2af733..0000000 --- a/1003-scsi-bus-correct-responses-for-INQUIRY-and-REQUEST-S.patch +++ /dev/null @@ -1,71 +0,0 @@ -From b07725e3e5e05610691815ee921a6b3307685815 Mon Sep 17 00:00:00 2001 -From: Hannes Reinecke -Date: Fri, 18 Aug 2017 11:37:02 +0200 -Subject: [PATCH 03/15] scsi-bus: correct responses for INQUIRY and REQUEST - SENSE - -According to SPC-3 INQUIRY and REQUEST SENSE should return GOOD -even on unsupported LUNS. - -Signed-off-by: Hannes Reinecke -Message-Id: <1503049022-14749-1-git-send-email-hare@suse.de> -Reported-by: Laszlo Ersek -Fixes: ded6ddc5a7b95217557fa360913d1213e12d4a6d -Cc: qemu-stable@nongnu.org -Signed-off-by: Paolo Bonzini -Signed-off-by: Hannes Reinecke ---- - hw/scsi/scsi-bus.c | 29 +++++++++++++++++++++++++---- - 1 file changed, 25 insertions(+), 4 deletions(-) - -diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c -index e364410a23..ade31c11f5 100644 ---- a/hw/scsi/scsi-bus.c -+++ b/hw/scsi/scsi-bus.c -@@ -516,8 +516,10 @@ static size_t scsi_sense_len(SCSIRequest *req) - static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) - { - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); -+ int fixed_sense = (req->cmd.buf[1] & 1) == 0; - -- if (req->lun != 0) { -+ if (req->lun != 0 && -+ buf[0] != INQUIRY && buf[0] != REQUEST_SENSE) { - scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; -@@ -535,9 +537,28 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) - break; - case REQUEST_SENSE: - scsi_target_alloc_buf(&r->req, scsi_sense_len(req)); -- r->len = scsi_device_get_sense(r->req.dev, r->buf, -- MIN(req->cmd.xfer, r->buf_len), -- (req->cmd.buf[1] & 1) == 0); -+ if (req->lun != 0) { -+ const struct SCSISense sense = SENSE_CODE(LUN_NOT_SUPPORTED); -+ -+ if (fixed_sense) { -+ r->buf[0] = 0x70; -+ r->buf[2] = sense.key; -+ r->buf[10] = 10; -+ r->buf[12] = sense.asc; -+ r->buf[13] = sense.ascq; -+ r->len = MIN(req->cmd.xfer, SCSI_SENSE_LEN); -+ } else { -+ r->buf[0] = 0x72; -+ r->buf[1] = sense.key; -+ r->buf[2] = sense.asc; -+ r->buf[3] = sense.ascq; -+ r->len = 8; -+ } -+ } else { -+ r->len = scsi_device_get_sense(r->req.dev, r->buf, -+ MIN(req->cmd.xfer, r->buf_len), -+ fixed_sense); -+ } - if (r->req.dev->sense_is_ua) { - scsi_device_unit_attention_reported(req->dev); - r->req.dev->sense_len = 0; --- -2.13.5 - diff --git a/1004-scsi-Refactor-scsi-sense-interpreting-code.patch b/1004-scsi-Refactor-scsi-sense-interpreting-code.patch deleted file mode 100644 index f1e02f5..0000000 --- a/1004-scsi-Refactor-scsi-sense-interpreting-code.patch +++ /dev/null @@ -1,194 +0,0 @@ -From f35ef58b2bac932bbd379602fe23e7a190530075 Mon Sep 17 00:00:00 2001 -From: Fam Zheng -Date: Mon, 21 Aug 2017 22:10:05 +0800 -Subject: [PATCH 04/15] scsi: Refactor scsi sense interpreting code - -So that it can be reused outside of iscsi.c. - -Also update MAINTAINERS to include the new files in SCSI section. - -Signed-off-by: Fam Zheng -Message-Id: <20170821141008.19383-2-famz@redhat.com> -Signed-off-by: Paolo Bonzini ---- - MAINTAINERS | 2 ++ - block/iscsi.c | 45 ++++----------------------------------------- - include/scsi/scsi.h | 19 +++++++++++++++++++ - util/Makefile.objs | 1 + - util/scsi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ - 5 files changed, 78 insertions(+), 41 deletions(-) - create mode 100644 include/scsi/scsi.h - create mode 100644 util/scsi.c - -diff --git a/MAINTAINERS b/MAINTAINERS -index ccee28b12d..2a4e5036ae 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -969,7 +969,9 @@ SCSI - M: Paolo Bonzini - S: Supported - F: include/hw/scsi/* -+F: include/scsi/* - F: hw/scsi/* -+F: util/scsi* - F: tests/virtio-scsi-test.c - T: git git://github.com/bonzini/qemu.git scsi-next - -diff --git a/block/iscsi.c b/block/iscsi.c -index d557c99668..4bed63cd6d 100644 ---- a/block/iscsi.c -+++ b/block/iscsi.c -@@ -40,6 +40,7 @@ - #include "qmp-commands.h" - #include "qapi/qmp/qstring.h" - #include "crypto/secret.h" -+#include "scsi/scsi.h" - - #include - #include -@@ -209,47 +210,9 @@ static inline unsigned exp_random(double mean) - - static int iscsi_translate_sense(struct scsi_sense *sense) - { -- int ret; -- -- switch (sense->key) { -- case SCSI_SENSE_NOT_READY: -- return -EBUSY; -- case SCSI_SENSE_DATA_PROTECTION: -- return -EACCES; -- case SCSI_SENSE_COMMAND_ABORTED: -- return -ECANCELED; -- case SCSI_SENSE_ILLEGAL_REQUEST: -- /* Parse ASCQ */ -- break; -- default: -- return -EIO; -- } -- switch (sense->ascq) { -- case SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR: -- case SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE: -- case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB: -- case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST: -- ret = -EINVAL; -- break; -- case SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE: -- ret = -ENOSPC; -- break; -- case SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED: -- ret = -ENOTSUP; -- break; -- case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT: -- case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED: -- case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN: -- ret = -ENOMEDIUM; -- break; -- case SCSI_SENSE_ASCQ_WRITE_PROTECTED: -- ret = -EACCES; -- break; -- default: -- ret = -EIO; -- break; -- } -- return ret; -+ return - scsi_sense_to_errno(sense->key, -+ (sense->ascq & 0xFF00) >> 8, -+ sense->ascq & 0xFF); - } - - /* Called (via iscsi_service) with QemuMutex held. */ -diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h -new file mode 100644 -index 0000000000..f894ace4bf ---- /dev/null -+++ b/include/scsi/scsi.h -@@ -0,0 +1,19 @@ -+/* -+ * SCSI helpers -+ * -+ * Copyright 2017 Red Hat, Inc. -+ * -+ * Authors: -+ * Fam Zheng -+ * -+ * 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; either version 2 of the License, or (at your option) -+ * any later version. -+ */ -+#ifndef QEMU_SCSI_H -+#define QEMU_SCSI_H -+ -+int scsi_sense_to_errno(int key, int asc, int ascq); -+ -+#endif -diff --git a/util/Makefile.objs b/util/Makefile.objs -index 50a55ecc75..c9e6c493d3 100644 ---- a/util/Makefile.objs -+++ b/util/Makefile.objs -@@ -45,3 +45,4 @@ util-obj-y += qht.o - util-obj-y += range.o - util-obj-y += stats64.o - util-obj-y += systemd.o -+util-obj-y += scsi.o -diff --git a/util/scsi.c b/util/scsi.c -new file mode 100644 -index 0000000000..a6710799fc ---- /dev/null -+++ b/util/scsi.c -@@ -0,0 +1,52 @@ -+/* -+ * SCSI helpers -+ * -+ * Copyright 2017 Red Hat, Inc. -+ * -+ * Authors: -+ * Fam Zheng -+ * -+ * 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; either version 2 of the License, or (at your option) -+ * any later version. -+ */ -+ -+#include "qemu/osdep.h" -+#include "scsi/scsi.h" -+ -+int scsi_sense_to_errno(int key, int asc, int ascq) -+{ -+ switch (key) { -+ case 0x02: /* NOT READY */ -+ return EBUSY; -+ case 0x07: /* DATA PROTECTION */ -+ return EACCES; -+ case 0x0b: /* COMMAND ABORTED */ -+ return ECANCELED; -+ case 0x05: /* ILLEGAL REQUEST */ -+ /* Parse ASCQ */ -+ break; -+ default: -+ return EIO; -+ } -+ switch ((asc << 8) | ascq) { -+ case 0x1a00: /* PARAMETER LIST LENGTH ERROR */ -+ case 0x2000: /* INVALID OPERATION CODE */ -+ case 0x2400: /* INVALID FIELD IN CDB */ -+ case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ -+ return EINVAL; -+ case 0x2100: /* LBA OUT OF RANGE */ -+ return ENOSPC; -+ case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ -+ return ENOTSUP; -+ case 0x3a00: /* MEDIUM NOT PRESENT */ -+ case 0x3a01: /* MEDIUM NOT PRESENT TRAY CLOSED */ -+ case 0x3a02: /* MEDIUM NOT PRESENT TRAY OPEN */ -+ return ENOMEDIUM; -+ case 0x2700: /* WRITE PROTECTED */ -+ return EACCES; -+ default: -+ return EIO; -+ } -+} --- -2.13.5 - diff --git a/1005-scsi-Improve-scsi_sense_to_errno.patch b/1005-scsi-Improve-scsi_sense_to_errno.patch deleted file mode 100644 index 07f9cb5..0000000 --- a/1005-scsi-Improve-scsi_sense_to_errno.patch +++ /dev/null @@ -1,61 +0,0 @@ -From a7dc92dac7cedb3ba6b6d724c7579f05399e2f2e Mon Sep 17 00:00:00 2001 -From: Fam Zheng -Date: Mon, 21 Aug 2017 22:10:06 +0800 -Subject: [PATCH 05/15] scsi: Improve scsi_sense_to_errno - -Tweak the errno mapping to return more accurate/appropriate values. - -Signed-off-by: Fam Zheng -Message-Id: <20170821141008.19383-3-famz@redhat.com> -Signed-off-by: Paolo Bonzini ---- - util/scsi.c | 16 ++++++++++++---- - 1 file changed, 12 insertions(+), 4 deletions(-) - -diff --git a/util/scsi.c b/util/scsi.c -index a6710799fc..472eb5bea5 100644 ---- a/util/scsi.c -+++ b/util/scsi.c -@@ -18,13 +18,16 @@ - int scsi_sense_to_errno(int key, int asc, int ascq) - { - switch (key) { -- case 0x02: /* NOT READY */ -- return EBUSY; -- case 0x07: /* DATA PROTECTION */ -- return EACCES; -+ case 0x00: /* NO SENSE */ -+ case 0x01: /* RECOVERED ERROR */ -+ case 0x06: /* UNIT ATTENTION */ -+ /* These sense keys are not errors */ -+ return 0; - case 0x0b: /* COMMAND ABORTED */ - return ECANCELED; -+ case 0x02: /* NOT READY */ - case 0x05: /* ILLEGAL REQUEST */ -+ case 0x07: /* DATA PROTECTION */ - /* Parse ASCQ */ - break; - default: -@@ -37,6 +40,7 @@ int scsi_sense_to_errno(int key, int asc, int ascq) - case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ - return EINVAL; - case 0x2100: /* LBA OUT OF RANGE */ -+ case 0x2707: /* SPACE ALLOC FAILED */ - return ENOSPC; - case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ - return ENOTSUP; -@@ -46,6 +50,10 @@ int scsi_sense_to_errno(int key, int asc, int ascq) - return ENOMEDIUM; - case 0x2700: /* WRITE PROTECTED */ - return EACCES; -+ case 0x0401: /* NOT READY, IN PROGRESS OF BECOMING READY */ -+ return EAGAIN; -+ case 0x0402: /* NOT READY, INITIALIZING COMMAND REQUIRED */ -+ return ENOTCONN; - default: - return EIO; - } --- -2.13.5 - diff --git a/1006-scsi-Introduce-scsi_sense_buf_to_errno.patch b/1006-scsi-Introduce-scsi_sense_buf_to_errno.patch deleted file mode 100644 index 4aa499c..0000000 --- a/1006-scsi-Introduce-scsi_sense_buf_to_errno.patch +++ /dev/null @@ -1,68 +0,0 @@ -From eadabcbc81d44ee0bc0d0d80697cc1142df61178 Mon Sep 17 00:00:00 2001 -From: Fam Zheng -Date: Mon, 21 Aug 2017 22:10:07 +0800 -Subject: [PATCH 06/15] scsi: Introduce scsi_sense_buf_to_errno - -This recognizes the "fixed" and "descriptor" format sense data, extracts -the sense key/asc/ascq fields then converts them to an errno. - -Signed-off-by: Fam Zheng -Message-Id: <20170821141008.19383-4-famz@redhat.com> -Signed-off-by: Paolo Bonzini ---- - include/scsi/scsi.h | 1 + - util/scsi.c | 30 ++++++++++++++++++++++++++++++ - 2 files changed, 31 insertions(+) - -diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h -index f894ace4bf..fe330385d8 100644 ---- a/include/scsi/scsi.h -+++ b/include/scsi/scsi.h -@@ -15,5 +15,6 @@ - #define QEMU_SCSI_H - - int scsi_sense_to_errno(int key, int asc, int ascq); -+int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size); - - #endif -diff --git a/util/scsi.c b/util/scsi.c -index 472eb5bea5..472293d59b 100644 ---- a/util/scsi.c -+++ b/util/scsi.c -@@ -58,3 +58,33 @@ int scsi_sense_to_errno(int key, int asc, int ascq) - return EIO; - } - } -+ -+int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size) -+{ -+ int key, asc, ascq; -+ if (sense_size < 1) { -+ return EIO; -+ } -+ switch (sense[0]) { -+ case 0x70: /* Fixed format sense data. */ -+ if (sense_size < 14) { -+ return EIO; -+ } -+ key = sense[2] & 0xF; -+ asc = sense[12]; -+ ascq = sense[13]; -+ break; -+ case 0x72: /* Descriptor format sense data. */ -+ if (sense_size < 4) { -+ return EIO; -+ } -+ key = sense[1] & 0xF; -+ asc = sense[2]; -+ ascq = sense[3]; -+ break; -+ default: -+ return EIO; -+ break; -+ } -+ return scsi_sense_to_errno(key, asc, ascq); -+} --- -2.13.5 - diff --git a/1007-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch b/1007-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch deleted file mode 100644 index 3607e2f..0000000 --- a/1007-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch +++ /dev/null @@ -1,92 +0,0 @@ -From ba83805030e07cade8d17b5d1b4bd1d296caff1b Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 22 Aug 2017 09:31:36 +0200 -Subject: [PATCH 07/15] scsi: rename scsi_build_sense to scsi_convert_sense -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -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. - -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Paolo Bonzini ---- - 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 ade31c11f5..fac360e20f 100644 ---- a/hw/scsi/scsi-bus.c -+++ b/hw/scsi/scsi-bus.c -@@ -790,7 +790,7 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) - return 0; - } - -- ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true); -+ ret = scsi_convert_sense(req->sense, req->sense_len, buf, len, true); - - /* - * FIXME: clearing unit attention conditions upon autosense should be done -@@ -811,7 +811,7 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) - - int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed) - { -- return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed); -+ return scsi_convert_sense(dev->sense, dev->sense_len, buf, len, fixed); - } - - void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) -@@ -1531,12 +1531,12 @@ const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { - }; - - /* -- * 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 = scsi_build_sense(NULL, 0, outbuf, r->buflen, -- (req->cmd.buf[1] & 1) == 0); -+ buflen = scsi_convert_sense(NULL, 0, outbuf, r->buflen, -+ (req->cmd.buf[1] & 1) == 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_FAILED; - 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); - - SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, - uint32_t tag, uint32_t lun, void *hba_private); --- -2.13.5 - diff --git a/1008-scsi-move-non-emulation-specific-code-to-scsi.patch b/1008-scsi-move-non-emulation-specific-code-to-scsi.patch deleted file mode 100644 index c1bb72b..0000000 --- a/1008-scsi-move-non-emulation-specific-code-to-scsi.patch +++ /dev/null @@ -1,1434 +0,0 @@ -From 6949ca76b864b54a0ef1d2aa321c3df4dc102c90 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 22 Aug 2017 07:08:27 +0200 -Subject: [PATCH 08/15] scsi: move non-emulation specific code to scsi/ -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -util/scsi.c includes some SCSI code that is shared by block/iscsi.c and -hw/scsi, but the introduction of the persistent reservation helper -will add many more instances of this. There is also include/block/scsi.h, -which actually is not part of the core block layer. - -The persistent reservation manager will also need a home. A scsi/ -directory provides one for both the aforementioned shared code and -the PR manager code. - -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Paolo Bonzini ---- - MAINTAINERS | 7 + - Makefile.objs | 2 +- - block/iscsi.c | 6 +- - 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/scsi.h | 20 -- - include/scsi/utils.h | 119 ++++++++++++ - scsi/Makefile.objs | 1 + - scsi/utils.c | 492 +++++++++++++++++++++++++++++++++++++++++++++++++ - util/Makefile.objs | 1 - - util/scsi.c | 90 --------- - 13 files changed, 626 insertions(+), 613 deletions(-) - delete mode 100644 include/scsi/scsi.h - create mode 100644 include/scsi/utils.h - create mode 100644 scsi/Makefile.objs - create mode 100644 scsi/utils.c - delete mode 100644 util/scsi.c - -diff --git a/MAINTAINERS b/MAINTAINERS -index 2a4e5036ae..074da8c976 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -1215,6 +1215,13 @@ F: migration/block* - F: include/block/aio.h - T: git git://github.com/stefanha/qemu.git block - -+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 = chardev/ - - block-obj-y += nbd/ - block-obj-y += block.o blockjob.o --block-obj-y += block/ -+block-obj-y += block/ scsi/ - block-obj-y += qemu-io-cmds.o - block-obj-$(CONFIG_REPLICATION) += replication.o - -diff --git a/block/iscsi.c b/block/iscsi.c -index 4bed63cd6d..40adc3c493 100644 ---- a/block/iscsi.c -+++ b/block/iscsi.c -@@ -40,10 +40,14 @@ - #include "qmp-commands.h" - #include "qapi/qmp/qstring.h" - #include "crypto/secret.h" --#include "scsi/scsi.h" -+#include "scsi/utils.h" - -+/* Conflict between scsi/utils.h and libiscsi! :( */ -+#define SCSI_XFER_NONE ISCSI_XFER_NONE - #include - #include -+#undef SCSI_XFER_NONE -+QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); - - #ifdef __linux__ - #include -diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c -index fac360e20f..42920d5422 100644 ---- a/hw/scsi/scsi-bus.c -+++ b/hw/scsi/scsi-bus.c -@@ -956,36 +956,6 @@ static int ata_passthrough_16_xfer(SCSIDevice *dev, uint8_t *buf) - return xfer * unit; - } - --uint32_t scsi_data_cdb_xfer(uint8_t *buf) --{ -- if ((buf[0] >> 5) == 0 && buf[4] == 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 = scsi_cdb_xfer(buf); -@@ -1298,53 +1268,6 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) - } - } - --static uint64_t scsi_cmd_lba(SCSICommand *cmd) --{ -- uint8_t *buf = cmd->buf; -- uint64_t lba; -- -- switch (buf[0] >> 5) { -- case 0: -- lba = ldl_be_p(&buf[0]) & 0x1fffff; -- break; -- case 1: -- case 2: -- case 5: -- lba = ldl_be_p(&buf[2]) & 0xffffffffULL; -- break; -- case 4: -- lba = ldq_be_p(&buf[2]); -- break; -- default: -- lba = -1; -- -- } -- return lba; --} -- --int scsi_cdb_length(uint8_t *buf) { -- int cdb_len; -- -- switch (buf[0] >> 5) { -- case 0: -- cdb_len = 6; -- break; -- case 1: -- case 2: -- cdb_len = 10; -- break; -- case 4: -- cdb_len = 16; -- break; -- case 5: -- cdb_len = 12; -- break; -- default: -- cdb_len = -1; -- } -- return cdb_len; --} -- - int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) - { - int rc; -@@ -1391,326 +1314,6 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense) - } - } - --/* -- * Predefined sense codes -- */ -- --/* No sense data available */ --const struct SCSISense sense_code_NO_SENSE = { -- .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 --}; -- --/* LUN not ready, Manual intervention required */ --const struct SCSISense sense_code_LUN_NOT_READY = { -- .key = NOT_READY, .asc = 0x04, .ascq = 0x03 --}; -- --/* LUN not ready, Medium not present */ --const struct SCSISense sense_code_NO_MEDIUM = { -- .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 --}; -- --/* LUN not ready, medium removal prevented */ --const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = { -- .key = NOT_READY, .asc = 0x53, .ascq = 0x02 --}; -- --/* Hardware error, internal target failure */ --const struct SCSISense sense_code_TARGET_FAILURE = { -- .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 --}; -- --/* Illegal request, invalid command operation code */ --const struct SCSISense sense_code_INVALID_OPCODE = { -- .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 --}; -- --/* Illegal request, LBA out of range */ --const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { -- .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 --}; -- --/* Illegal request, Invalid field in CDB */ --const struct SCSISense sense_code_INVALID_FIELD = { -- .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 --}; -- --/* Illegal request, Invalid field in parameter list */ --const struct SCSISense sense_code_INVALID_PARAM = { -- .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00 --}; -- --/* Illegal request, Parameter list length error */ --const struct SCSISense sense_code_INVALID_PARAM_LEN = { -- .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00 --}; -- --/* Illegal request, LUN not supported */ --const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { -- .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 --}; -- --/* Illegal request, Saving parameters not supported */ --const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { -- .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00 --}; -- --/* Illegal request, Incompatible medium installed */ --const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { -- .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 --}; -- --/* Illegal request, medium removal prevented */ --const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { -- .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 --}; -- --/* Illegal request, Invalid Transfer Tag */ --const struct SCSISense sense_code_INVALID_TAG = { -- .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 --}; -- --/* Command aborted, I/O process terminated */ --const struct SCSISense sense_code_IO_ERROR = { -- .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 --}; -- --/* Command aborted, I_T Nexus loss occurred */ --const struct SCSISense sense_code_I_T_NEXUS_LOSS = { -- .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 --}; -- --/* Command aborted, Logical Unit failure */ --const struct SCSISense sense_code_LUN_FAILURE = { -- .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 --}; -- --/* Command aborted, Overlapped Commands Attempted */ --const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { -- .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 --}; -- --/* Unit attention, Capacity data has changed */ --const struct SCSISense sense_code_CAPACITY_CHANGED = { -- .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 --}; -- --/* Unit attention, Power on, reset or bus device reset occurred */ --const struct SCSISense sense_code_RESET = { -- .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 --}; -- --/* Unit attention, No medium */ --const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { -- .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 --}; -- --/* Unit attention, Medium may have changed */ --const struct SCSISense sense_code_MEDIUM_CHANGED = { -- .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 --}; -- --/* Unit attention, Reported LUNs data has changed */ --const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = { -- .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e --}; -- --/* Unit attention, Device internal reset */ --const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { -- .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 --}; -- --/* Data Protection, Write Protected */ --const struct SCSISense sense_code_WRITE_PROTECTED = { -- .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 --}; -- --/* Data Protection, Space Allocation Failed Write Protect */ --const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { -- .key = DATA_PROTECT, .asc = 0x27, .ascq = 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 == 0) { -- sense.key = NO_SENSE; -- sense.asc = 0; -- sense.ascq = 0; -- } else { -- fixed_in = (in_buf[0] & 2) == 0; -- -- if (fixed == fixed_in) { -- memcpy(buf, in_buf, MIN(len, in_len)); -- return MIN(len, in_len); -- } -- -- if (fixed_in) { -- sense.key = in_buf[2]; -- sense.asc = in_buf[12]; -- sense.ascq = in_buf[13]; -- } else { -- sense.key = in_buf[1]; -- sense.asc = in_buf[2]; -- sense.ascq = in_buf[3]; -- } -- } -- -- memset(buf, 0, len); -- if (fixed) { -- /* Return fixed format sense buffer */ -- buf[0] = 0x70; -- buf[2] = sense.key; -- buf[7] = 10; -- buf[12] = sense.asc; -- buf[13] = sense.ascq; -- return MIN(len, SCSI_SENSE_LEN); -- } else { -- /* Return descriptor format sense buffer */ -- buf[0] = 0x72; -- buf[1] = sense.key; -- buf[2] = sense.asc; -- buf[3] = sense.ascq; -- return 8; -- } --} -- --const char *scsi_command_name(uint8_t cmd) --{ -- static const char *names[] = { -- [ TEST_UNIT_READY ] = "TEST_UNIT_READY", -- [ REWIND ] = "REWIND", -- [ REQUEST_SENSE ] = "REQUEST_SENSE", -- [ FORMAT_UNIT ] = "FORMAT_UNIT", -- [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", -- [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS", -- /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */ -- [ READ_6 ] = "READ_6", -- [ WRITE_6 ] = "WRITE_6", -- [ SET_CAPACITY ] = "SET_CAPACITY", -- [ READ_REVERSE ] = "READ_REVERSE", -- [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS", -- [ SPACE ] = "SPACE", -- [ INQUIRY ] = "INQUIRY", -- [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", -- [ MAINTENANCE_IN ] = "MAINTENANCE_IN", -- [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", -- [ MODE_SELECT ] = "MODE_SELECT", -- [ RESERVE ] = "RESERVE", -- [ RELEASE ] = "RELEASE", -- [ COPY ] = "COPY", -- [ ERASE ] = "ERASE", -- [ MODE_SENSE ] = "MODE_SENSE", -- [ START_STOP ] = "START_STOP/LOAD_UNLOAD", -- /* LOAD_UNLOAD and START_STOP use the same operation code */ -- [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", -- [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", -- [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", -- [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", -- [ READ_10 ] = "READ_10", -- [ WRITE_10 ] = "WRITE_10", -- [ SEEK_10 ] = "SEEK_10/POSITION_TO_ELEMENT", -- /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ -- [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", -- [ VERIFY_10 ] = "VERIFY_10", -- [ SEARCH_HIGH ] = "SEARCH_HIGH", -- [ SEARCH_EQUAL ] = "SEARCH_EQUAL", -- [ SEARCH_LOW ] = "SEARCH_LOW", -- [ SET_LIMITS ] = "SET_LIMITS", -- [ PRE_FETCH ] = "PRE_FETCH/READ_POSITION", -- /* READ_POSITION and PRE_FETCH use the same operation code */ -- [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", -- [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", -- [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE", -- /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */ -- [ MEDIUM_SCAN ] = "MEDIUM_SCAN", -- [ COMPARE ] = "COMPARE", -- [ COPY_VERIFY ] = "COPY_VERIFY", -- [ WRITE_BUFFER ] = "WRITE_BUFFER", -- [ READ_BUFFER ] = "READ_BUFFER", -- [ UPDATE_BLOCK ] = "UPDATE_BLOCK", -- [ READ_LONG_10 ] = "READ_LONG_10", -- [ WRITE_LONG_10 ] = "WRITE_LONG_10", -- [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", -- [ WRITE_SAME_10 ] = "WRITE_SAME_10", -- [ UNMAP ] = "UNMAP", -- [ READ_TOC ] = "READ_TOC", -- [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", -- [ SANITIZE ] = "SANITIZE", -- [ GET_CONFIGURATION ] = "GET_CONFIGURATION", -- [ LOG_SELECT ] = "LOG_SELECT", -- [ LOG_SENSE ] = "LOG_SENSE", -- [ MODE_SELECT_10 ] = "MODE_SELECT_10", -- [ RESERVE_10 ] = "RESERVE_10", -- [ RELEASE_10 ] = "RELEASE_10", -- [ MODE_SENSE_10 ] = "MODE_SENSE_10", -- [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", -- [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", -- [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", -- [ EXTENDED_COPY ] = "EXTENDED_COPY", -- [ ATA_PASSTHROUGH_16 ] = "ATA_PASSTHROUGH_16", -- [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", -- [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", -- [ READ_16 ] = "READ_16", -- [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE", -- [ WRITE_16 ] = "WRITE_16", -- [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", -- [ VERIFY_16 ] = "VERIFY_16", -- [ PRE_FETCH_16 ] = "PRE_FETCH_16", -- [ SYNCHRONIZE_CACHE_16 ] = "SPACE_16/SYNCHRONIZE_CACHE_16", -- /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ -- [ LOCATE_16 ] = "LOCATE_16", -- [ WRITE_SAME_16 ] = "ERASE_16/WRITE_SAME_16", -- /* ERASE_16 and WRITE_SAME_16 use the same operation code */ -- [ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16", -- [ WRITE_LONG_16 ] = "WRITE_LONG_16", -- [ REPORT_LUNS ] = "REPORT_LUNS", -- [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", -- [ MOVE_MEDIUM ] = "MOVE_MEDIUM", -- [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", -- [ READ_12 ] = "READ_12", -- [ WRITE_12 ] = "WRITE_12", -- [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE", -- /* ERASE_12 and GET_PERFORMANCE use the same operation code */ -- [ SERVICE_ACTION_IN_12 ] = "SERVICE_ACTION_IN_12", -- [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", -- [ VERIFY_12 ] = "VERIFY_12", -- [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", -- [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", -- [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", -- [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", -- [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG/SET_STREAMING", -- /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ -- [ READ_CD ] = "READ_CD", -- [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", -- [ READ_DVD_STRUCTURE ] = "READ_DVD_STRUCTURE", -- [ RESERVE_TRACK ] = "RESERVE_TRACK", -- [ SEND_CUE_SHEET ] = "SEND_CUE_SHEET", -- [ SEND_DVD_STRUCTURE ] = "SEND_DVD_STRUCTURE", -- [ SET_CD_SPEED ] = "SET_CD_SPEED", -- [ SET_READ_AHEAD ] = "SET_READ_AHEAD", -- [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", -- [ MECHANISM_STATUS ] = "MECHANISM_STATUS", -- [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", -- [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", -- }; -- -- if (cmd >= ARRAY_SIZE(names) || names[cmd] == 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" - --#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 - --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" - - #define MAX_SCSI_DEVS 255 - --#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; - --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 - --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, BlockBackend *blk, - void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated); - void scsi_legacy_handle_cmdline(void); - --/* -- * 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/scsi.h b/include/scsi/scsi.h -deleted file mode 100644 -index fe330385d8..0000000000 ---- a/include/scsi/scsi.h -+++ /dev/null -@@ -1,20 +0,0 @@ --/* -- * SCSI helpers -- * -- * Copyright 2017 Red Hat, Inc. -- * -- * Authors: -- * Fam Zheng -- * -- * 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; either version 2 of the License, or (at your option) -- * any later version. -- */ --#ifndef QEMU_SCSI_H --#define QEMU_SCSI_H -- --int scsi_sense_to_errno(int key, int asc, int ascq); --int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size); -- --#endif -diff --git a/include/scsi/utils.h b/include/scsi/utils.h -new file mode 100644 -index 0000000000..90bf4dce6e ---- /dev/null -+++ b/include/scsi/utils.h -@@ -0,0 +1,119 @@ -+#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_sense_to_errno(int key, int asc, int ascq); -+int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size); -+ -+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 += utils.o -diff --git a/scsi/utils.c b/scsi/utils.c -new file mode 100644 -index 0000000000..2327e06da0 ---- /dev/null -+++ b/scsi/utils.c -@@ -0,0 +1,492 @@ -+/* -+ * SCSI helpers -+ * -+ * Copyright 2017 Red Hat, Inc. -+ * -+ * Authors: -+ * Fam Zheng -+ * 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; either version 2 of the License, or (at your option) -+ * any later version. -+ */ -+ -+#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) == 0 && buf[4] == 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 = cmd->buf; -+ uint64_t lba; -+ -+ switch (buf[0] >> 5) { -+ case 0: -+ lba = ldl_be_p(&buf[0]) & 0x1fffff; -+ break; -+ case 1: -+ case 2: -+ case 5: -+ lba = ldl_be_p(&buf[2]) & 0xffffffffULL; -+ break; -+ case 4: -+ lba = ldq_be_p(&buf[2]); -+ break; -+ default: -+ lba = -1; -+ -+ } -+ return lba; -+} -+ -+int scsi_cdb_length(uint8_t *buf) -+{ -+ int cdb_len; -+ -+ switch (buf[0] >> 5) { -+ case 0: -+ cdb_len = 6; -+ break; -+ case 1: -+ case 2: -+ cdb_len = 10; -+ break; -+ case 4: -+ cdb_len = 16; -+ break; -+ case 5: -+ cdb_len = 12; -+ break; -+ default: -+ cdb_len = -1; -+ } -+ return cdb_len; -+} -+ -+/* -+ * Predefined sense codes -+ */ -+ -+/* No sense data available */ -+const struct SCSISense sense_code_NO_SENSE = { -+ .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 -+}; -+ -+/* LUN not ready, Manual intervention required */ -+const struct SCSISense sense_code_LUN_NOT_READY = { -+ .key = NOT_READY, .asc = 0x04, .ascq = 0x03 -+}; -+ -+/* LUN not ready, Medium not present */ -+const struct SCSISense sense_code_NO_MEDIUM = { -+ .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 -+}; -+ -+/* LUN not ready, medium removal prevented */ -+const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = { -+ .key = NOT_READY, .asc = 0x53, .ascq = 0x02 -+}; -+ -+/* Hardware error, internal target failure */ -+const struct SCSISense sense_code_TARGET_FAILURE = { -+ .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 -+}; -+ -+/* Illegal request, invalid command operation code */ -+const struct SCSISense sense_code_INVALID_OPCODE = { -+ .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 -+}; -+ -+/* Illegal request, LBA out of range */ -+const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { -+ .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 -+}; -+ -+/* Illegal request, Invalid field in CDB */ -+const struct SCSISense sense_code_INVALID_FIELD = { -+ .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 -+}; -+ -+/* Illegal request, Invalid field in parameter list */ -+const struct SCSISense sense_code_INVALID_PARAM = { -+ .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00 -+}; -+ -+/* Illegal request, Parameter list length error */ -+const struct SCSISense sense_code_INVALID_PARAM_LEN = { -+ .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00 -+}; -+ -+/* Illegal request, LUN not supported */ -+const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { -+ .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 -+}; -+ -+/* Illegal request, Saving parameters not supported */ -+const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { -+ .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00 -+}; -+ -+/* Illegal request, Incompatible medium installed */ -+const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { -+ .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 -+}; -+ -+/* Illegal request, medium removal prevented */ -+const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { -+ .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 -+}; -+ -+/* Illegal request, Invalid Transfer Tag */ -+const struct SCSISense sense_code_INVALID_TAG = { -+ .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 -+}; -+ -+/* Command aborted, I/O process terminated */ -+const struct SCSISense sense_code_IO_ERROR = { -+ .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 -+}; -+ -+/* Command aborted, I_T Nexus loss occurred */ -+const struct SCSISense sense_code_I_T_NEXUS_LOSS = { -+ .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 -+}; -+ -+/* Command aborted, Logical Unit failure */ -+const struct SCSISense sense_code_LUN_FAILURE = { -+ .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 -+}; -+ -+/* Command aborted, Overlapped Commands Attempted */ -+const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { -+ .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 -+}; -+ -+/* Unit attention, Capacity data has changed */ -+const struct SCSISense sense_code_CAPACITY_CHANGED = { -+ .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 -+}; -+ -+/* Unit attention, Power on, reset or bus device reset occurred */ -+const struct SCSISense sense_code_RESET = { -+ .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 -+}; -+ -+/* Unit attention, No medium */ -+const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { -+ .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 -+}; -+ -+/* Unit attention, Medium may have changed */ -+const struct SCSISense sense_code_MEDIUM_CHANGED = { -+ .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 -+}; -+ -+/* Unit attention, Reported LUNs data has changed */ -+const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = { -+ .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e -+}; -+ -+/* Unit attention, Device internal reset */ -+const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { -+ .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 -+}; -+ -+/* Data Protection, Write Protected */ -+const struct SCSISense sense_code_WRITE_PROTECTED = { -+ .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 -+}; -+ -+/* Data Protection, Space Allocation Failed Write Protect */ -+const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { -+ .key = DATA_PROTECT, .asc = 0x27, .ascq = 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 == 0) { -+ sense.key = NO_SENSE; -+ sense.asc = 0; -+ sense.ascq = 0; -+ } else { -+ fixed_in = (in_buf[0] & 2) == 0; -+ -+ if (fixed == fixed_in) { -+ memcpy(buf, in_buf, MIN(len, in_len)); -+ return MIN(len, in_len); -+ } -+ -+ if (fixed_in) { -+ sense.key = in_buf[2]; -+ sense.asc = in_buf[12]; -+ sense.ascq = in_buf[13]; -+ } else { -+ sense.key = in_buf[1]; -+ sense.asc = in_buf[2]; -+ sense.ascq = in_buf[3]; -+ } -+ } -+ -+ memset(buf, 0, len); -+ if (fixed) { -+ /* Return fixed format sense buffer */ -+ buf[0] = 0x70; -+ buf[2] = sense.key; -+ buf[7] = 10; -+ buf[12] = sense.asc; -+ buf[13] = sense.ascq; -+ return MIN(len, SCSI_SENSE_LEN); -+ } else { -+ /* Return descriptor format sense buffer */ -+ buf[0] = 0x72; -+ buf[1] = sense.key; -+ buf[2] = sense.asc; -+ buf[3] = sense.ascq; -+ return 8; -+ } -+} -+ -+int scsi_sense_to_errno(int key, int asc, int ascq) -+{ -+ switch (key) { -+ case 0x00: /* NO SENSE */ -+ case 0x01: /* RECOVERED ERROR */ -+ case 0x06: /* UNIT ATTENTION */ -+ /* These sense keys are not errors */ -+ return 0; -+ case 0x0b: /* COMMAND ABORTED */ -+ return ECANCELED; -+ case 0x02: /* NOT READY */ -+ case 0x05: /* ILLEGAL REQUEST */ -+ case 0x07: /* DATA PROTECTION */ -+ /* Parse ASCQ */ -+ break; -+ default: -+ return EIO; -+ } -+ switch ((asc << 8) | ascq) { -+ case 0x1a00: /* PARAMETER LIST LENGTH ERROR */ -+ case 0x2000: /* INVALID OPERATION CODE */ -+ case 0x2400: /* INVALID FIELD IN CDB */ -+ case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ -+ return EINVAL; -+ case 0x2100: /* LBA OUT OF RANGE */ -+ case 0x2707: /* SPACE ALLOC FAILED */ -+ return ENOSPC; -+ case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ -+ return ENOTSUP; -+ case 0x3a00: /* MEDIUM NOT PRESENT */ -+ case 0x3a01: /* MEDIUM NOT PRESENT TRAY CLOSED */ -+ case 0x3a02: /* MEDIUM NOT PRESENT TRAY OPEN */ -+ return ENOMEDIUM; -+ case 0x2700: /* WRITE PROTECTED */ -+ return EACCES; -+ case 0x0401: /* NOT READY, IN PROGRESS OF BECOMING READY */ -+ return EAGAIN; -+ case 0x0402: /* NOT READY, INITIALIZING COMMAND REQUIRED */ -+ return ENOTCONN; -+ default: -+ return EIO; -+ } -+} -+ -+int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size) -+{ -+ int key, asc, ascq; -+ if (sense_size < 1) { -+ return EIO; -+ } -+ switch (sense[0]) { -+ case 0x70: /* Fixed format sense data. */ -+ if (sense_size < 14) { -+ return EIO; -+ } -+ key = sense[2] & 0xF; -+ asc = sense[12]; -+ ascq = sense[13]; -+ break; -+ case 0x72: /* Descriptor format sense data. */ -+ if (sense_size < 4) { -+ return EIO; -+ } -+ key = sense[1] & 0xF; -+ asc = sense[2]; -+ ascq = sense[3]; -+ break; -+ default: -+ return EIO; -+ break; -+ } -+ return scsi_sense_to_errno(key, asc, ascq); -+} -+ -+const char *scsi_command_name(uint8_t cmd) -+{ -+ static const char *names[] = { -+ [ TEST_UNIT_READY ] = "TEST_UNIT_READY", -+ [ REWIND ] = "REWIND", -+ [ REQUEST_SENSE ] = "REQUEST_SENSE", -+ [ FORMAT_UNIT ] = "FORMAT_UNIT", -+ [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", -+ [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS", -+ /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */ -+ [ READ_6 ] = "READ_6", -+ [ WRITE_6 ] = "WRITE_6", -+ [ SET_CAPACITY ] = "SET_CAPACITY", -+ [ READ_REVERSE ] = "READ_REVERSE", -+ [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS", -+ [ SPACE ] = "SPACE", -+ [ INQUIRY ] = "INQUIRY", -+ [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", -+ [ MAINTENANCE_IN ] = "MAINTENANCE_IN", -+ [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", -+ [ MODE_SELECT ] = "MODE_SELECT", -+ [ RESERVE ] = "RESERVE", -+ [ RELEASE ] = "RELEASE", -+ [ COPY ] = "COPY", -+ [ ERASE ] = "ERASE", -+ [ MODE_SENSE ] = "MODE_SENSE", -+ [ START_STOP ] = "START_STOP/LOAD_UNLOAD", -+ /* LOAD_UNLOAD and START_STOP use the same operation code */ -+ [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", -+ [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", -+ [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", -+ [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", -+ [ READ_10 ] = "READ_10", -+ [ WRITE_10 ] = "WRITE_10", -+ [ SEEK_10 ] = "SEEK_10/POSITION_TO_ELEMENT", -+ /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ -+ [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", -+ [ VERIFY_10 ] = "VERIFY_10", -+ [ SEARCH_HIGH ] = "SEARCH_HIGH", -+ [ SEARCH_EQUAL ] = "SEARCH_EQUAL", -+ [ SEARCH_LOW ] = "SEARCH_LOW", -+ [ SET_LIMITS ] = "SET_LIMITS", -+ [ PRE_FETCH ] = "PRE_FETCH/READ_POSITION", -+ /* READ_POSITION and PRE_FETCH use the same operation code */ -+ [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", -+ [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", -+ [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE", -+ /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */ -+ [ MEDIUM_SCAN ] = "MEDIUM_SCAN", -+ [ COMPARE ] = "COMPARE", -+ [ COPY_VERIFY ] = "COPY_VERIFY", -+ [ WRITE_BUFFER ] = "WRITE_BUFFER", -+ [ READ_BUFFER ] = "READ_BUFFER", -+ [ UPDATE_BLOCK ] = "UPDATE_BLOCK", -+ [ READ_LONG_10 ] = "READ_LONG_10", -+ [ WRITE_LONG_10 ] = "WRITE_LONG_10", -+ [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", -+ [ WRITE_SAME_10 ] = "WRITE_SAME_10", -+ [ UNMAP ] = "UNMAP", -+ [ READ_TOC ] = "READ_TOC", -+ [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", -+ [ SANITIZE ] = "SANITIZE", -+ [ GET_CONFIGURATION ] = "GET_CONFIGURATION", -+ [ LOG_SELECT ] = "LOG_SELECT", -+ [ LOG_SENSE ] = "LOG_SENSE", -+ [ MODE_SELECT_10 ] = "MODE_SELECT_10", -+ [ RESERVE_10 ] = "RESERVE_10", -+ [ RELEASE_10 ] = "RELEASE_10", -+ [ MODE_SENSE_10 ] = "MODE_SENSE_10", -+ [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", -+ [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", -+ [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", -+ [ EXTENDED_COPY ] = "EXTENDED_COPY", -+ [ ATA_PASSTHROUGH_16 ] = "ATA_PASSTHROUGH_16", -+ [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", -+ [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", -+ [ READ_16 ] = "READ_16", -+ [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE", -+ [ WRITE_16 ] = "WRITE_16", -+ [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", -+ [ VERIFY_16 ] = "VERIFY_16", -+ [ PRE_FETCH_16 ] = "PRE_FETCH_16", -+ [ SYNCHRONIZE_CACHE_16 ] = "SPACE_16/SYNCHRONIZE_CACHE_16", -+ /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ -+ [ LOCATE_16 ] = "LOCATE_16", -+ [ WRITE_SAME_16 ] = "ERASE_16/WRITE_SAME_16", -+ /* ERASE_16 and WRITE_SAME_16 use the same operation code */ -+ [ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16", -+ [ WRITE_LONG_16 ] = "WRITE_LONG_16", -+ [ REPORT_LUNS ] = "REPORT_LUNS", -+ [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", -+ [ MOVE_MEDIUM ] = "MOVE_MEDIUM", -+ [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", -+ [ READ_12 ] = "READ_12", -+ [ WRITE_12 ] = "WRITE_12", -+ [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE", -+ /* ERASE_12 and GET_PERFORMANCE use the same operation code */ -+ [ SERVICE_ACTION_IN_12 ] = "SERVICE_ACTION_IN_12", -+ [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", -+ [ VERIFY_12 ] = "VERIFY_12", -+ [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", -+ [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", -+ [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", -+ [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", -+ [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG/SET_STREAMING", -+ /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ -+ [ READ_CD ] = "READ_CD", -+ [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", -+ [ READ_DVD_STRUCTURE ] = "READ_DVD_STRUCTURE", -+ [ RESERVE_TRACK ] = "RESERVE_TRACK", -+ [ SEND_CUE_SHEET ] = "SEND_CUE_SHEET", -+ [ SEND_DVD_STRUCTURE ] = "SEND_DVD_STRUCTURE", -+ [ SET_CD_SPEED ] = "SET_CD_SPEED", -+ [ SET_READ_AHEAD ] = "SET_READ_AHEAD", -+ [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", -+ [ MECHANISM_STATUS ] = "MECHANISM_STATUS", -+ [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", -+ [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", -+ }; -+ -+ if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) { -+ return "*UNKNOWN*"; -+ } -+ return names[cmd]; -+} -diff --git a/util/Makefile.objs b/util/Makefile.objs -index c9e6c493d3..50a55ecc75 100644 ---- a/util/Makefile.objs -+++ b/util/Makefile.objs -@@ -45,4 +45,3 @@ util-obj-y += qht.o - util-obj-y += range.o - util-obj-y += stats64.o - util-obj-y += systemd.o --util-obj-y += scsi.o -diff --git a/util/scsi.c b/util/scsi.c -deleted file mode 100644 -index 472293d59b..0000000000 ---- a/util/scsi.c -+++ /dev/null -@@ -1,90 +0,0 @@ --/* -- * SCSI helpers -- * -- * Copyright 2017 Red Hat, Inc. -- * -- * Authors: -- * Fam Zheng -- * -- * 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; either version 2 of the License, or (at your option) -- * any later version. -- */ -- --#include "qemu/osdep.h" --#include "scsi/scsi.h" -- --int scsi_sense_to_errno(int key, int asc, int ascq) --{ -- switch (key) { -- case 0x00: /* NO SENSE */ -- case 0x01: /* RECOVERED ERROR */ -- case 0x06: /* UNIT ATTENTION */ -- /* These sense keys are not errors */ -- return 0; -- case 0x0b: /* COMMAND ABORTED */ -- return ECANCELED; -- case 0x02: /* NOT READY */ -- case 0x05: /* ILLEGAL REQUEST */ -- case 0x07: /* DATA PROTECTION */ -- /* Parse ASCQ */ -- break; -- default: -- return EIO; -- } -- switch ((asc << 8) | ascq) { -- case 0x1a00: /* PARAMETER LIST LENGTH ERROR */ -- case 0x2000: /* INVALID OPERATION CODE */ -- case 0x2400: /* INVALID FIELD IN CDB */ -- case 0x2600: /* INVALID FIELD IN PARAMETER LIST */ -- return EINVAL; -- case 0x2100: /* LBA OUT OF RANGE */ -- case 0x2707: /* SPACE ALLOC FAILED */ -- return ENOSPC; -- case 0x2500: /* LOGICAL UNIT NOT SUPPORTED */ -- return ENOTSUP; -- case 0x3a00: /* MEDIUM NOT PRESENT */ -- case 0x3a01: /* MEDIUM NOT PRESENT TRAY CLOSED */ -- case 0x3a02: /* MEDIUM NOT PRESENT TRAY OPEN */ -- return ENOMEDIUM; -- case 0x2700: /* WRITE PROTECTED */ -- return EACCES; -- case 0x0401: /* NOT READY, IN PROGRESS OF BECOMING READY */ -- return EAGAIN; -- case 0x0402: /* NOT READY, INITIALIZING COMMAND REQUIRED */ -- return ENOTCONN; -- default: -- return EIO; -- } --} -- --int scsi_sense_buf_to_errno(const uint8_t *sense, size_t sense_size) --{ -- int key, asc, ascq; -- if (sense_size < 1) { -- return EIO; -- } -- switch (sense[0]) { -- case 0x70: /* Fixed format sense data. */ -- if (sense_size < 14) { -- return EIO; -- } -- key = sense[2] & 0xF; -- asc = sense[12]; -- ascq = sense[13]; -- break; -- case 0x72: /* Descriptor format sense data. */ -- if (sense_size < 4) { -- return EIO; -- } -- key = sense[1] & 0xF; -- asc = sense[2]; -- ascq = sense[3]; -- break; -- default: -- return EIO; -- break; -- } -- return scsi_sense_to_errno(key, asc, ascq); --} --- -2.13.5 - diff --git a/1009-scsi-introduce-scsi_build_sense.patch b/1009-scsi-introduce-scsi_build_sense.patch deleted file mode 100644 index 8b16369..0000000 --- a/1009-scsi-introduce-scsi_build_sense.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 82e922c8be1b32eeb0c8c22165cdeff39e12d3ef Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 22 Aug 2017 09:42:59 +0200 -Subject: [PATCH 09/15] scsi: introduce scsi_build_sense -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Move more knowledge of sense data format out of hw/scsi/scsi-bus.c -for reusability. - -Reviewed-by: Philippe Mathieu-Daudé -Reviewed-by: Stefan Hajnoczi -Signed-off-by: Paolo Bonzini ---- - 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 42920d5422..652ab046ab 100644 ---- a/hw/scsi/scsi-bus.c -+++ b/hw/scsi/scsi-bus.c -@@ -818,13 +818,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] = 0x70; -- req->sense[2] = sense.key; -- req->sense[7] = 10; -- req->sense[12] = sense.asc; -- req->sense[13] = sense.ascq; -- req->sense_len = 18; -+ req->sense_len = scsi_build_sense(req->sense, sense); - } - - static void scsi_req_enqueue_internal(SCSIRequest *req) -diff --git a/include/scsi/utils.h b/include/scsi/utils.h -index 90bf4dce6e..b49392d841 100644 ---- a/include/scsi/utils.h -+++ b/include/scsi/utils.h -@@ -30,6 +30,8 @@ typedef struct SCSISense { - uint8_t ascq; - } SCSISense; - -+int scsi_build_sense(uint8_t *buf, SCSISense sense); -+ - /* - * Predefined sense codes - */ -diff --git a/scsi/utils.c b/scsi/utils.c -index 2327e06da0..89d9167d9d 100644 ---- a/scsi/utils.c -+++ b/scsi/utils.c -@@ -96,6 +96,17 @@ int scsi_cdb_length(uint8_t *buf) - return cdb_len; - } - -+int scsi_build_sense(uint8_t *buf, SCSISense sense) -+{ -+ memset(buf, 0, 18); -+ buf[0] = 0x70; -+ buf[2] = sense.key; -+ buf[7] = 10; -+ buf[12] = sense.asc; -+ buf[13] = sense.ascq; -+ return 18; -+} -+ - /* - * Predefined sense codes - */ --- -2.13.5 - diff --git a/1010-scsi-introduce-sg_io_sense_from_errno.patch b/1010-scsi-introduce-sg_io_sense_from_errno.patch deleted file mode 100644 index 7cb02e1..0000000 --- a/1010-scsi-introduce-sg_io_sense_from_errno.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 1ebf7935eca7f2a1f5a1376ee3b234f4fce98023 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 22 Aug 2017 09:43:14 +0200 -Subject: [PATCH 10/15] scsi: introduce sg_io_sense_from_errno - -Move more knowledge of SG_IO out of hw/scsi/scsi-generic.c, for -reusability. - -Reviewed-by: Stefan Hajnoczi -Signed-off-by: Paolo Bonzini ---- - 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; - - assert(r->req.aiocb == NULL); - -@@ -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 = r->io_header.sb_len_wr; -- } -- -- if (ret != 0) { -- switch (ret) { -- case -EDOM: -- status = TASK_SET_FULL; -- break; -- case -ENOMEM: -- status = CHECK_CONDITION; -- scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); -- break; -- default: -- status = CHECK_CONDITION; -- scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); -- break; -- } -- } else { -- if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || -- r->io_header.host_status == SG_ERR_DID_BUS_BUSY || -- r->io_header.host_status == SG_ERR_DID_TIME_OUT || -- (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { -- status = BUSY; -- BADF("Driver Timeout\n"); -- } else if (r->io_header.host_status) { -- status = CHECK_CONDITION; -- scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); -- } else if (r->io_header.status) { -- status = r->io_header.status; -- } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { -- status = CHECK_CONDITION; -+ status = sg_io_sense_from_errno(-ret, &r->io_header, &sense); -+ if (status == CHECK_CONDITION) { -+ if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { -+ r->req.sense_len = r->io_header.sb_len_wr; - } else { -- status = GOOD; -+ scsi_req_build_sense(&r->req, sense); - } - } -+ - DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", - r, r->req.tag, status); - -diff --git a/include/scsi/utils.h b/include/scsi/utils.h -index b49392d841..d301b31768 100644 ---- a/include/scsi/utils.h -+++ b/include/scsi/utils.h -@@ -116,6 +116,9 @@ int scsi_cdb_length(uint8_t *buf); - #define SG_ERR_DID_TIME_OUT 0x03 - - #define SG_ERR_DRIVER_SENSE 0x08 -+ -+int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr, -+ SCSISense *sense); - #endif - - #endif -diff --git a/scsi/utils.c b/scsi/utils.c -index 89d9167d9d..6ee9f4095b 100644 ---- a/scsi/utils.c -+++ b/scsi/utils.c -@@ -501,3 +501,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 != 0) { -+ switch (errno_value) { -+ case EDOM: -+ return TASK_SET_FULL; -+ case ENOMEM: -+ *sense = SENSE_CODE(TARGET_FAILURE); -+ return CHECK_CONDITION; -+ default: -+ *sense = SENSE_CODE(IO_ERROR); -+ return CHECK_CONDITION; -+ } -+ } else { -+ if (io_hdr->host_status == SG_ERR_DID_NO_CONNECT || -+ io_hdr->host_status == SG_ERR_DID_BUS_BUSY || -+ io_hdr->host_status == SG_ERR_DID_TIME_OUT || -+ (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT)) { -+ return BUSY; -+ } else if (io_hdr->host_status) { -+ *sense = 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 --- -2.13.5 - diff --git a/1011-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch b/1011-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch deleted file mode 100644 index 1541323..0000000 --- a/1011-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch +++ /dev/null @@ -1,250 +0,0 @@ -From e2b560d9f9966d7256488d0a359200c65c2c07f8 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 22 Aug 2017 09:23:55 +0200 -Subject: [PATCH 11/15] scsi: move block/scsi.h to include/scsi/constants.h -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Complete the transition by renaming this header, which was -shared by block/iscsi.c and the SCSI emulation code. - -Reviewed-by: Philippe Mathieu-Daudé -Reviewed-by: Stefan Hajnoczi -Signed-off-by: Paolo Bonzini ---- - 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 => 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 => scsi/constants.h} (100%) - -diff --git a/block/iscsi.c b/block/iscsi.c -index 40adc3c493..c4586be720 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" - - #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 652ab046ab..977f7bce1f 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__); } while (0) - do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) - - #include --#include "block/scsi.h" -+#include "scsi/constants.h" - - #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-dataplane.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" - -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" - -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" - - /* --------------------------------------------------------------------- */ - -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" - - /* 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 6ee9f4095b..fab60bdf20 100644 ---- a/scsi/utils.c -+++ b/scsi/utils.c -@@ -14,7 +14,7 @@ - */ - - #include "qemu/osdep.h" --#include "block/scsi.h" -+#include "scsi/constants.h" - #include "scsi/utils.h" - #include "qemu/bswap.h" - -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 @@ - - #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" --- -2.13.5 - diff --git a/1012-scsi-file-posix-add-support-for-persistent-reservati.patch b/1012-scsi-file-posix-add-support-for-persistent-reservati.patch deleted file mode 100644 index 2780bb3..0000000 --- a/1012-scsi-file-posix-add-support-for-persistent-reservati.patch +++ /dev/null @@ -1,440 +0,0 @@ -From 5c4a4b825189c2e9f322c8673104add7f76e38d5 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Mon, 21 Aug 2017 18:58:56 +0200 -Subject: [PATCH 12/15] scsi, file-posix: add support for persistent - reservation management - -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=helper0,path=/var/run/qemu-pr-helper.sock - -drive if=none,id=hd,driver=raw,file.filename=/dev/sdb,file.pr-manager=helper0 - -device scsi-block,drive=hd - -or: - - $ qemu-system-x86_64 - -device virtio-scsi \ - -object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock - -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0 - -device scsi-block,drive=hd - -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 | 56 ++++++++++++++++++++++++ - qapi/block-core.json | 4 ++ - scsi/Makefile.objs | 2 + - scsi/pr-manager.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++ - scsi/trace-events | 3 ++ - vl.c | 3 +- - 9 files changed, 258 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 - create mode 100644 scsi/trace-events - -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 += qapi - trace-events-subdirs += accel/tcg - trace-events-subdirs += accel/kvm - trace-events-subdirs += nbd -+trace-events-subdirs += scsi - - trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) - -diff --git a/block/file-posix.c b/block/file-posix.c -index cb3bfce147..9cacf06685 100644 ---- a/block/file-posix.c -+++ b/block/file-posix.c -@@ -34,6 +34,9 @@ - #include "qapi/util.h" - #include "qapi/qmp/qstring.h" - -+#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; - - typedef struct BDRVRawReopenState { -@@ -403,6 +408,11 @@ static QemuOptsList raw_runtime_opts = { - .type = QEMU_OPT_STRING, - .help = "file locking mode (on/off/auto, default: auto)", - }, -+ { -+ .name = "pr-manager", -+ .type = QEMU_OPT_STRING, -+ .help = "id of persistent reservation manager object (default: none)", -+ }, - { /* end of list */ } - }, - }; -@@ -414,6 +424,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, - QemuOpts *opts; - Error *local_err = NULL; - const char *filename = NULL; -+ const char *str; - BlockdevAioOptions aio, aio_default; - int fd, ret; - struct stat st; -@@ -475,6 +486,16 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, - abort(); - } - -+ str = qemu_opt_get(opts, "pr-manager"); -+ if (str) { -+ s->pr_mgr = pr_manager_lookup(str, &local_err); -+ if (local_err) { -+ error_propagate(errp, local_err); -+ ret = -EINVAL; -+ goto fail; -+ } -+ } -+ - s->open_flags = open_flags; - raw_parse_flags(bdrv_flags, &s->open_flags); - -@@ -2597,6 +2618,15 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, - if (fd_open(bs) < 0) - return NULL; - -+ if (req == SG_IO && s->pr_mgr) { -+ struct sg_io_hdr *io_hdr = buf; -+ if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || -+ io_hdr->cmdp[0] == PERSISTENT_RESERVE_IN) { -+ return pr_manager_execute(s->pr_mgr, bdrv_get_aio_context(bs), -+ s->fd, io_hdr, cb, opaque); -+ } -+ } -+ - acb = g_new(RawPosixAIOData, 1); - acb->bs = bs; - acb->aio_type = 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 @@ -+====================================== -+Persistent reservation managers -+====================================== -+ -+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=helper0,path=/var/run/qemu-pr-helper.sock -+ -drive if=none,id=hd,driver=raw,file.filename=/dev/sdb,file.pr-manager=helper0 -+ -device scsi-block,drive=hd -+ -+Alternatively, using ``-blockdev``:: -+ -+ $ qemu-system-x86_64 -+ -device virtio-scsi \ -+ -object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock -+ -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0 -+ -device scsi-block,drive=hd -diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h -new file mode 100644 -index 0000000000..b2b37d63bc ---- /dev/null -+++ b/include/scsi/pr-manager.h -@@ -0,0 +1,56 @@ -+#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..1cf6ec8be7 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 id for the object that will handle persistent reservations -+# for this device (default: none, 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 enable - # 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' } } - -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 += utils.o -+ -+block-obj-$(CONFIG_LINUX) += pr-manager.o -diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c -new file mode 100644 -index 0000000000..87c45db5d4 ---- /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 -+ -+#include "qapi/error.h" -+#include "block/aio.h" -+#include "block/thread-pool.h" -+#include "scsi/pr-manager.h" -+#include "trace.h" -+ -+typedef struct PRManagerData { -+ PRManager *pr_mgr; -+ struct sg_io_hdr *hdr; -+ int fd; -+} PRManagerData; -+ -+static int pr_manager_worker(void *opaque) -+{ -+ PRManagerData *data = opaque; -+ PRManager *pr_mgr = data->pr_mgr; -+ PRManagerClass *pr_mgr_class = -+ PR_MANAGER_GET_CLASS(pr_mgr); -+ struct sg_io_hdr *hdr = data->hdr; -+ int fd = data->fd; -+ int r; -+ -+ g_free(data); -+ trace_pr_manager_run(fd, hdr->cmdp[0], hdr->cmdp[1]); -+ -+ /* The reference was taken in pr_manager_execute. */ -+ r = 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 = g_new(PRManagerData, 1); -+ ThreadPool *pool = aio_get_thread_pool(ctx); -+ -+ trace_pr_manager_execute(fd, hdr->cmdp[0], hdr->cmdp[1], opaque); -+ data->pr_mgr = pr_mgr; -+ data->fd = fd; -+ data->hdr = 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 = { -+ .parent = TYPE_OBJECT, -+ .name = TYPE_PR_MANAGER, -+ .class_size = sizeof(PRManagerClass), -+ .abstract = true, -+ .interfaces = (InterfaceInfo[]) { -+ { TYPE_USER_CREATABLE }, -+ { } -+ } -+}; -+ -+PRManager *pr_manager_lookup(const char *id, Error **errp) -+{ -+ Object *obj; -+ PRManager *pr_mgr; -+ -+ obj = 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 = (PRManager *) -+ object_dynamic_cast(obj, -+ TYPE_PR_MANAGER); -+ if (!pr_mgr) { -+ error_setg(errp, -+ "Object with id '%s' is not a persistent reservation manager", -+ 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/scsi/trace-events b/scsi/trace-events -new file mode 100644 -index 0000000000..45f5b6e49b ---- /dev/null -+++ b/scsi/trace-events -@@ -0,0 +1,3 @@ -+# scsi/pr-manager.c -+pr_manager_execute(int fd, int cmd, int sa, void *opaque) "fd=%d cmd=0x%02x service action=0x%02x opaque=%p" -+pr_manager_run(int fd, int cmd, int sa) "fd=%d cmd=0x%02x service action=0x%02x" -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; - } - --- -2.13.5 - diff --git a/1013-scsi-build-qemu-pr-helper.patch b/1013-scsi-build-qemu-pr-helper.patch deleted file mode 100644 index 795bb75..0000000 --- a/1013-scsi-build-qemu-pr-helper.patch +++ /dev/null @@ -1,1013 +0,0 @@ -From 226e00649123536737dcd20ccb5bd3d54d074338 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 22 Aug 2017 06:50:18 +0200 -Subject: [PATCH 13/15] scsi: build qemu-pr-helper - -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 | 4 +- - configure | 14 +- - docs/interop/pr-helper.rst | 83 +++++ - docs/pr-manager.rst | 33 ++ - scsi/pr-helper.h | 41 +++ - scsi/qemu-pr-helper.c | 735 +++++++++++++++++++++++++++++++++++++++++++++ - 6 files changed, 905 insertions(+), 5 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 eb831b98d1..8406aeb8cb 100644 ---- a/Makefile -+++ b/Makefile -@@ -372,6 +372,8 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) - fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) - fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap - -+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","$@") - -@@ -488,7 +490,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.* *.pod *~ */*~ -- 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 -diff --git a/configure b/configure -index cb0f7ed0ab..becc21a0fe 100755 ---- a/configure -+++ b/configure -@@ -5034,16 +5034,22 @@ if test "$want_tools" = "yes" ; then - fi - fi - if test "$softmmu" = yes ; then -- if test "$virtfs" != no ; then -- if test "$cap" = yes && test "$linux" = yes && test "$attr" = yes ; then -+ if test "$linux" = yes; then -+ if test "$virtfs" != no && test "$cap" = yes && test "$attr" = yes ; then - virtfs=yes - tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)" - else - if test "$virtfs" = 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=no - fi -+ tools="$tools scsi/qemu-pr-helper\$(EXESUF)" -+ else -+ if test "$virtfs" = yes; then -+ error_exit "VirtFS is supported only on Linux" -+ fi -+ virtfs=no - fi - fi - -@@ -6506,7 +6512,7 @@ fi - - # build tree in object directory in case the source is not in the current directory - DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests" --DIRS="$DIRS docs docs/interop fsdev" -+DIRS="$DIRS docs docs/interop fsdev scsi" - DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" - DIRS="$DIRS roms/seabios roms/vgabios" - DIRS="$DIRS qapi-generated" -diff --git a/docs/interop/pr-helper.rst b/docs/interop/pr-helper.rst -new file mode 100644 -index 0000000000..9f76d5bcf9 ---- /dev/null -+++ b/docs/interop/pr-helper.rst -@@ -0,0 +1,83 @@ -+.. -+ -+====================================== -+Persistent reservation helper protocol -+====================================== -+ -+QEMU's SCSI passthrough devices, ``scsi-block`` and ``scsi-generic``, -+can delegate implementation of persistent reservations to an external -+(and typically privileged) 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 -+----------------------------- -+ -+All data transmitted on the socket is big-endian. -+ -+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 reads it, -+then 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, -+the helper will not enable the feature. -+ -+Right now no feature is defined, so the two parties always write four -+zero bytes. -+ -+Command format -+-------------- -+ -+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. -+ -+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 length of the parameter list is taken from the CDB itself. -+ -+The helper's reply has the following structure: -+ -+- 4 bytes for the SCSI status -+ -+- 4 bytes for the payload size (nonzero only for PERSISTENT RESERVE IN -+ and only if the SCSI status is 0x00, i.e. GOOD) -+ -+- 96 bytes for the SCSI sense data -+ -+- if the size is nonzero, the payload follows -+ -+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). -+ -+The payload size is always less than or equal to the allocation length -+specified in the CDB for the PERSISTENT RESERVE IN command. -+ -+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=helper0,path=/var/run/qemu-pr-helper.sock - -blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0 - -device scsi-block,drive=hd -+ -+---------------------------------- -+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=path PID file when running as a daemon -+-k, --socket=path path to the socket -+-T, --trace=trace-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=/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=user user to drop privileges to -+-g, --group=group group to drop privileges to -diff --git a/scsi/pr-helper.h b/scsi/pr-helper.h -new file mode 100644 -index 0000000000..96c50a9e5f ---- /dev/null -+++ b/scsi/pr-helper.h -@@ -0,0 +1,41 @@ -+/* Definitions for QEMU's persistent reservation helper daemon -+ * -+ * Copyright (C) 2017 Red Hat, Inc. -+ * -+ * Author: -+ * Paolo Bonzini -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to -+ * deal in the Software without restriction, including without limitation the -+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -+ * sell copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+#ifndef QEMU_PR_HELPER_H -+#define QEMU_PR_HELPER_H 1 -+ -+#include -+ -+#define PR_HELPER_CDB_SIZE 16 -+#define PR_HELPER_SENSE_SIZE 96 -+#define PR_HELPER_DATA_SIZE 8192 -+ -+typedef struct PRHelperResponse { -+ int32_t result; -+ int32_t sz; -+ 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..e39efbd529 ---- /dev/null -+++ b/scsi/qemu-pr-helper.c -@@ -0,0 +1,735 @@ -+/* -+ * Privileged helper to handle persistent reservation commands for QEMU -+ * -+ * 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 -+#include -+#include -+ -+#ifdef CONFIG_LIBCAP -+#include -+#endif -+#include -+#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" -+ -+#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 = 1; -+static int verbose; -+ -+#ifdef CONFIG_LIBCAP -+static int uid = -1; -+static int gid = -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=PATH PID file when running as a daemon\n" -+" (default '%s')\n" -+" -k, --socket=PATH path to the unix socket\n" -+" (default '%s')\n" -+" -T, --trace [[enable=]][,events=][,file=]\n" -+" specify tracing options\n" -+#ifdef CONFIG_LIBCAP -+" -u, --user=USER user to drop privileges to\n" -+" -g, --group=GROUP 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 NO\n" -+"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" -+ , name); -+} -+ -+static void write_pidfile(void) -+{ -+ int pidfd; -+ char pidstr[32]; -+ -+ pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); -+ if (pidfd == -1) { -+ error_report("Cannot open pid file, %s", strerror(errno)); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (lockf(pidfd, F_TLOCK, 0)) { -+ error_report("Cannot lock pid file, %s", strerror(errno)); -+ goto fail; -+ } -+ if (ftruncate(pidfd, 0)) { -+ error_report("Failed to truncate pid file"); -+ goto fail; -+ } -+ -+ snprintf(pidstr, sizeof(pidstr), "%d\n", getpid()); -+ if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { -+ error_report("Failed to write pid file"); -+ goto fail; -+ } -+ return; -+ -+fail: -+ unlink(pidfile); -+ close(pidfd); -+ exit(EXIT_FAILURE); -+} -+ -+/* SG_IO support */ -+ -+typedef struct PRHelperSGIOData { -+ int fd; -+ const uint8_t *cdb; -+ uint8_t *sense; -+ uint8_t *buf; -+ int sz; /* input/output */ -+ int dir; -+} PRHelperSGIOData; -+ -+static int do_sgio_worker(void *opaque) -+{ -+ PRHelperSGIOData *data = 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 = 'S'; -+ io_hdr.cmd_len = PR_HELPER_CDB_SIZE; -+ io_hdr.cmdp = (uint8_t *)data->cdb; -+ io_hdr.sbp = data->sense; -+ io_hdr.mx_sb_len = PR_HELPER_SENSE_SIZE; -+ io_hdr.timeout = 1; -+ io_hdr.dxfer_direction = data->dir; -+ io_hdr.dxferp = (char *)data->buf; -+ io_hdr.dxfer_len = data->sz; -+ ret = ioctl(data->fd, SG_IO, &io_hdr); -+ status = sg_io_sense_from_errno(ret < 0 ? errno : 0, &io_hdr, -+ &sense_code); -+ if (status == GOOD) { -+ data->sz -= io_hdr.resid; -+ } else { -+ data->sz = 0; -+ } -+ -+ if (status == 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 = aio_get_thread_pool(qemu_get_aio_context()); -+ int r; -+ -+ PRHelperSGIOData data = { -+ .fd = fd, -+ .cdb = cdb, -+ .sense = sense, -+ .buf = buf, -+ .sz = *sz, -+ .dir = dir, -+ }; -+ -+ r = thread_pool_submit_co(pool, do_sgio_worker, &data); -+ *sz = data.sz; -+ return r; -+} -+ -+static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, -+ uint8_t *data, int *resp_sz) -+{ -+ return do_sgio(fd, cdb, sense, data, resp_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) -+{ -+ int resp_sz = sz; -+ return do_sgio(fd, cdb, sense, (uint8_t *)param, &resp_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 coroutine_fn prh_read(PRHelperClient *client, void *buf, int sz, -+ Error **errp) -+{ -+ int ret = 0; -+ -+ while (sz > 0) { -+ int *fds = NULL; -+ size_t nfds = 0; -+ int i; -+ struct iovec iov; -+ ssize_t n_read; -+ -+ iov.iov_base = buf; -+ iov.iov_len = sz; -+ n_read = qio_channel_readv_full(QIO_CHANNEL(client->ioc), &iov, 1, -+ &fds, &nfds, errp); -+ -+ if (n_read == QIO_CHANNEL_ERR_BLOCK) { -+ qio_channel_yield(QIO_CHANNEL(client->ioc), G_IO_IN); -+ continue; -+ } -+ if (n_read <= 0) { -+ ret = n_read ? n_read : -1; -+ goto err; -+ } -+ -+ /* Stash one file descriptor per request. */ -+ if (nfds) { -+ bool too_many = false; -+ for (i = 0; i < nfds; i++) { -+ if (client->fd == -1) { -+ client->fd = fds[i]; -+ } else { -+ close(fds[i]); -+ too_many = true; -+ } -+ } -+ g_free(fds); -+ if (too_many) { -+ ret = -1; -+ goto err; -+ } -+ } -+ -+ buf += n_read; -+ sz -= n_read; -+ } -+ -+ return 0; -+ -+err: -+ if (client->fd != -1) { -+ close(client->fd); -+ client->fd = -1; -+ } -+ return ret; -+} -+ -+static int coroutine_fn 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 == -1) { -+ error_setg(errp, "No file descriptor in request."); -+ return -1; -+ } -+ -+ if (req->cdb[0] != PERSISTENT_RESERVE_OUT && -+ req->cdb[0] != PERSISTENT_RESERVE_IN) { -+ error_setg(errp, "Invalid CDB, closing socket."); -+ goto out_close; -+ } -+ -+ sz = scsi_cdb_xfer(req->cdb); -+ if (sz > sizeof(client->data)) { -+ goto out_close; -+ } -+ -+ if (req->cdb[0] == PERSISTENT_RESERVE_OUT) { -+ if (qio_channel_read_all(QIO_CHANNEL(client->ioc), -+ (char *)client->data, sz, -+ errp) < 0) { -+ goto out_close; -+ } -+ if ((fcntl(client->fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { -+ scsi_build_sense(resp->sense, SENSE_CODE(INVALID_OPCODE)); -+ sz = 0; -+ } else if (sz < PR_OUT_FIXED_PARAM_SIZE) { -+ /* Illegal request, Parameter list length error. This isn't fatal; -+ * we have read the data, send an error without closing the socket. -+ */ -+ scsi_build_sense(resp->sense, SENSE_CODE(INVALID_PARAM_LEN)); -+ sz = 0; -+ } -+ if (sz == 0) { -+ resp->result = CHECK_CONDITION; -+ close(client->fd); -+ client->fd = -1; -+ } -+ } -+ -+ req->fd = client->fd; -+ req->sz = sz; -+ client->fd = -1; -+ return sz; -+ -+out_close: -+ close(client->fd); -+ client->fd = -1; -+ return -1; -+} -+ -+static int coroutine_fn prh_write_response(PRHelperClient *client, -+ PRHelperRequest *req, -+ PRHelperResponse *resp, Error **errp) -+{ -+ ssize_t r; -+ size_t sz; -+ -+ if (req->cdb[0] == PERSISTENT_RESERVE_IN && resp->result == GOOD) { -+ assert(resp->sz <= req->sz && resp->sz <= sizeof(client->data)); -+ } else { -+ assert(resp->sz == 0); -+ } -+ -+ sz = resp->sz; -+ -+ resp->result = cpu_to_be32(resp->result); -+ resp->sz = cpu_to_be32(resp->sz); -+ r = qio_channel_write_all(QIO_CHANNEL(client->ioc), -+ (char *) resp, sizeof(*resp), errp); -+ if (r < 0) { -+ return r; -+ } -+ -+ r = qio_channel_write_all(QIO_CHANNEL(client->ioc), -+ (char *) client->data, -+ sz, errp); -+ return r < 0 ? r : 0; -+} -+ -+static void coroutine_fn prh_co_entry(void *opaque) -+{ -+ PRHelperClient *client = opaque; -+ Error *local_err = 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 = cpu_to_be32(0); -+ r = qio_channel_write_all(QIO_CHANNEL(client->ioc), -+ (char *) &flags, sizeof(flags), NULL); -+ if (r < 0) { -+ goto out; -+ } -+ -+ r = qio_channel_read_all(QIO_CHANNEL(client->ioc), -+ (char *) &flags, sizeof(flags), NULL); -+ if (be32_to_cpu(flags) != 0 || r < 0) { -+ goto out; -+ } -+ -+ while (atomic_read(&state) == RUNNING) { -+ PRHelperRequest req; -+ PRHelperResponse resp; -+ int sz; -+ -+ sz = prh_read_request(client, &req, &resp, &local_err); -+ if (sz < 0) { -+ break; -+ } -+ -+ if (sz > 0) { -+ num_active_sockets++; -+ if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { -+ r = do_pr_out(req.fd, req.cdb, resp.sense, -+ client->data, sz); -+ resp.sz = 0; -+ } else { -+ resp.sz = sizeof(client->data); -+ r = do_pr_in(req.fd, req.cdb, resp.sense, -+ client->data, &resp.sz); -+ resp.sz = MIN(resp.sz, sz); -+ } -+ num_active_sockets--; -+ close(req.fd); -+ if (r == -1) { -+ break; -+ } -+ resp.result = r; -+ } -+ -+ if (prh_write_response(client, &req, &resp, &local_err) < 0) { -+ break; -+ } -+ } -+ -+ if (local_err) { -+ if (verbose == 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 = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), -+ NULL); -+ if (!cioc) { -+ return TRUE; -+ } -+ -+ prh = g_new(PRHelperClient, 1); -+ prh->ioc = cioc; -+ prh->fd = -1; -+ prh->co = 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 != NULL) { -+ return "Unix socket can't be set when using socket activation"; -+ } -+ -+ return NULL; -+} -+ -+static void compute_default_paths(void) -+{ -+ if (!socket_path) { -+ socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); -+ } -+} -+ -+static void termsig_handler(int signum) -+{ -+ atomic_cmpxchg(&state, RUNNING, TERMINATE); -+ qemu_notify_event(); -+} -+ -+static void close_server_socket(void) -+{ -+ assert(server_ioc); -+ -+ g_source_remove(server_watch); -+ server_watch = -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 descriptors -+ * 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 != -1 ? uid : getuid(), -+ gid != -1 ? gid : getgid(), -+ CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING)) { -+ return -1; -+ } -+ -+ return 0; -+} -+#endif -+ -+int main(int argc, char **argv) -+{ -+ const char *sopt = "hVk:fdT:u:g:q"; -+ struct option lopt[] = { -+ { "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 = 0; -+ int quiet = 0; -+ char ch; -+ Error *local_err = NULL; -+ char *trace_file = NULL; -+ bool daemonize = false; -+ unsigned socket_activation; -+ -+ struct sigaction sa_sigterm; -+ memset(&sa_sigterm, 0, sizeof(sa_sigterm)); -+ sa_sigterm.sa_handler = termsig_handler; -+ sigaction(SIGTERM, &sa_sigterm, NULL); -+ sigaction(SIGINT, &sa_sigterm, NULL); -+ sigaction(SIGHUP, &sa_sigterm, NULL); -+ -+ signal(SIGPIPE, SIG_IGN); -+ -+ module_call_init(MODULE_INIT_TRACE); -+ module_call_init(MODULE_INIT_QOM); -+ qemu_add_opts(&qemu_trace_opts); -+ qemu_init_exec_dir(argv[0]); -+ -+ pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); -+ -+ while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { -+ switch (ch) { -+ case 'k': -+ socket_path = optarg; -+ if (socket_path[0] != '/') { -+ error_report("socket path must be absolute"); -+ exit(EXIT_FAILURE); -+ } -+ break; -+ case 'f': -+ pidfile = optarg; -+ break; -+#ifdef CONFIG_LIBCAP -+ case 'u': { -+ unsigned long res; -+ struct passwd *userinfo = getpwnam(optarg); -+ if (userinfo) { -+ uid = userinfo->pw_uid; -+ } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && -+ (uid_t)res == res) { -+ uid = res; -+ } else { -+ error_report("invalid user '%s'", optarg); -+ exit(EXIT_FAILURE); -+ } -+ break; -+ } -+ case 'g': { -+ unsigned long res; -+ struct group *groupinfo = getgrnam(optarg); -+ if (groupinfo) { -+ gid = groupinfo->gr_gid; -+ } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && -+ (gid_t)res == res) { -+ gid = 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 = true; -+ break; -+ case 'q': -+ quiet = 1; -+ break; -+ case 'T': -+ g_free(trace_file); -+ trace_file = trace_opt_parse(optarg); -+ break; -+ case 'V': -+ version(argv[0]); -+ exit(EXIT_SUCCESS); -+ break; -+ case 'h': -+ usage(argv[0]); -+ exit(EXIT_SUCCESS); -+ break; -+ case '?': -+ error_report("Try `%s --help' for more information.", argv[0]); -+ exit(EXIT_FAILURE); -+ } -+ } -+ -+ /* set verbosity */ -+ verbose = !quiet; -+ -+ if (!trace_init_backends()) { -+ exit(EXIT_FAILURE); -+ } -+ trace_init_file(trace_file); -+ qemu_set_log(LOG_TRACE); -+ -+ socket_activation = check_socket_activation(); -+ if (socket_activation == 0) { -+ SocketAddress saddr; -+ compute_default_paths(); -+ saddr = (SocketAddress){ -+ .type = SOCKET_ADDRESS_TYPE_UNIX, -+ .u.q_unix.path = g_strdup(socket_path) -+ }; -+ server_ioc = 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 = socket_activation_validate_opts(); -+ if (err_msg != 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 LISTEN_FDS > 1", -+ argv[0]); -+ exit(EXIT_FAILURE); -+ } -+ server_ioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD, -+ &local_err); -+ if (server_ioc == NULL) { -+ error_report("Failed to use socket activation: %s", -+ error_get_pretty(local_err)); -+ exit(EXIT_FAILURE); -+ } -+ socket_path = NULL; -+ } -+ -+ if (qemu_init_main_loop(&local_err)) { -+ error_report_err(local_err); -+ exit(EXIT_FAILURE); -+ } -+ -+ server_watch = 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); -+ } -+ write_pidfile(); -+ } -+ -+ state = RUNNING; -+ do { -+ main_loop_wait(false); -+ if (state == TERMINATE) { -+ state = TERMINATING; -+ close_server_socket(); -+ } -+ } while (num_active_sockets > 0); -+ -+ exit(EXIT_SUCCESS); -+} --- -2.13.5 - diff --git a/1014-scsi-add-multipath-support-to-qemu-pr-helper.patch b/1014-scsi-add-multipath-support-to-qemu-pr-helper.patch deleted file mode 100644 index b1de8c1..0000000 --- a/1014-scsi-add-multipath-support-to-qemu-pr-helper.patch +++ /dev/null @@ -1,679 +0,0 @@ -From 43fedb8ae2c2b3bbb43023c118be708226e38179 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 22 Aug 2017 06:50:55 +0200 -Subject: [PATCH 14/15] scsi: add multipath support to qemu-pr-helper - -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 | 46 +++++++ - docs/pr-manager.rst | 27 ++++ - include/scsi/utils.h | 4 + - scsi/qemu-pr-helper.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++- - scsi/utils.c | 10 ++ - 6 files changed, 433 insertions(+), 3 deletions(-) - -diff --git a/Makefile b/Makefile -index 8406aeb8cb..4eb40376d2 100644 ---- a/Makefile -+++ b/Makefile -@@ -373,6 +373,9 @@ fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal - fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap - - 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 += -ludev -lmultipath -lmpathpersist -+endif - - 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 becc21a0fe..f6edc2a33f 100755 ---- a/configure -+++ b/configure -@@ -290,6 +290,7 @@ netmap="no" - sdl="" - sdlabi="" - virtfs="" -+mpath="" - vnc="yes" - sparse="no" - vde="" -@@ -936,6 +937,10 @@ for opt do - ;; - --enable-virtfs) virtfs="yes" - ;; -+ --disable-mpath) mpath="no" -+ ;; -+ --enable-mpath) mpath="yes" -+ ;; - --disable-vnc) vnc="no" - ;; - --enable-vnc) vnc="yes" -@@ -1479,6 +1484,7 @@ disabled with --disable-FEATURE, default is enabled if 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) -@@ -3300,6 +3306,38 @@ else - fi - - ########################################## -+# libmpathpersist probe -+ -+if test "$mpath" != "no" ; then -+ cat > $TMPC < -+#include -+unsigned mpath_mx_alloc_len = 1024; -+int logsink; -+static struct config *multipath_conf; -+extern struct udev *udev; -+extern struct config *get_multipath_config(void); -+extern void put_multipath_config(struct config *conf); -+struct udev *udev; -+struct config *get_multipath_config(void) { return multipath_conf; } -+void put_multipath_config(struct config *conf) { } -+ -+int main(void) { -+ udev = udev_new(); -+ multipath_conf = mpath_lib_init(); -+ return 0; -+} -+EOF -+ if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then -+ mpathpersist=yes -+ else -+ mpathpersist=no -+ fi -+else -+ mpathpersist=no -+fi -+ -+########################################## - # libcap probe - - if test "$cap" != "no" ; then -@@ -5044,12 +5074,24 @@ if test "$softmmu" = yes ; then - fi - virtfs=no - fi -+ if test "$mpath" != no && test "$mpathpersist" = yes ; then -+ mpath=yes -+ else -+ if test "$mpath" = yes; then -+ error_exit "Multipath requires libmpathpersist devel" -+ fi -+ mpath=no -+ fi - tools="$tools scsi/qemu-pr-helper\$(EXESUF)" - else - if test "$virtfs" = yes; then - error_exit "VirtFS is supported only on Linux" - fi - virtfs=no -+ if test "$mpath" = yes; then -+ error_exit "Multipath is supported only on Linux" -+ fi -+ mpath=no - fi - fi - -@@ -5295,6 +5337,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" = "yes" ; then - echo "VNC SASL support $vnc_sasl" -@@ -5738,6 +5781,9 @@ fi - if test "$virtfs" = "yes" ; then - echo "CONFIG_VIRTFS=y" >> $config_host_mak - fi -+if test "$mpath" = "yes" ; then -+ echo "CONFIG_MPATH=y" >> $config_host_mak -+fi - if test "$vhost_scsi" = "yes" ; then - echo "CONFIG_VHOST_SCSI=y" >> $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: - - -d, --daemon run in the background - -q, --quiet decrease verbosity -+-v, --verbose increase verbosity - -f, --pidfile=path PID file when running as a daemon - -k, --socket=path path to the socket - -T, --trace=trace-opts tracing options -@@ -82,3 +83,29 @@ its operation. To do this, add the following options: - - -u, --user=user user to drop privileges to - -g, --group=group 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 d301b31768..00a4bdb080 100644 ---- a/include/scsi/utils.h -+++ b/include/scsi/utils.h -@@ -72,10 +72,14 @@ extern const struct SCSISense sense_code_IO_ERROR; - 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, LUN Communication failure */ -+extern const struct SCSISense sense_code_LUN_COMM_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; -+/* Unit attention, SCSI bus reset */ -+extern const struct SCSISense sense_code_SCSI_BUS_RESET; - /* 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 */ -diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c -index e39efbd529..5f77c873e1 100644 ---- a/scsi/qemu-pr-helper.c -+++ b/scsi/qemu-pr-helper.c -@@ -30,6 +30,12 @@ - #include - #include - -+#ifdef CONFIG_MPATH -+#include -+#include -+#include -+#endif -+ - #include "qapi/error.h" - #include "qemu-common.h" - #include "qemu/cutils.h" -@@ -60,6 +66,7 @@ static enum { RUNNING, TERMINATE, TERMINATING } state; - static QIOChannelSocket *server_ioc; - static int server_watch; - static int num_active_sockets = 1; -+static int noisy; - static int verbose; - - #ifdef CONFIG_LIBCAP -@@ -204,9 +211,327 @@ static int do_sgio(int fd, const uint8_t *cdb, uint8_t *sense, - return r; - } - -+/* Device mapper interface */ -+ -+#ifdef CONFIG_MPATH -+#define CONTROL_PATH "/dev/mapper/control" -+ -+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] = DM_VERSION_MAJOR; -+ d.dm.version[1] = 0; -+ d.dm.version[2] = 0; -+ d.dm.data_size = 1024; -+ d.dm.data_start = 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 = fstat(fd, &st); -+ if (r < 0) { -+ perror("fstat"); -+ exit(1); -+ } -+ -+ dm->dev = st.st_rdev; -+ return dm_ioctl(ioc, dm); -+} -+ -+static void dm_init(void) -+{ -+ control_fd = open(CONTROL_PATH, O_RDWR); -+ if (control_fd < 0) { -+ perror("Cannot open " CONTROL_PATH); -+ exit(1); -+ } -+ struct dm_ioctl dm = { 0 }; -+ if (!dm_ioctl(DM_VERSION, &dm)) { -+ perror("ioctl"); -+ exit(1); -+ } -+ if (dm.version[0] != DM_VERSION_MAJOR) { -+ fprintf(stderr, "Unsupported device mapper interface"); -+ exit(1); -+ } -+} -+ -+/* Variables required by libmultipath and libmpathpersist. */ -+QEMU_BUILD_BUG_ON(PR_HELPER_DATA_SIZE > MPATH_MAX_PARAM_LEN); -+static struct config *multipath_conf; -+unsigned mpath_mx_alloc_len = PR_HELPER_DATA_SIZE; -+int logsink; -+struct udev *udev; -+ -+extern struct config *get_multipath_config(void); -+struct config *get_multipath_config(void) -+{ -+ return multipath_conf; -+} -+ -+extern void put_multipath_config(struct config *conf); -+void put_multipath_config(struct config *conf) -+{ -+} -+ -+static void multipath_pr_init(void) -+{ -+ udev = udev_new(); -+ multipath_conf = mpath_lib_init(); -+} -+ -+static int is_mpath(int fd) -+{ -+ struct dm_ioctl dm = { .flags = DM_NOFLUSH_FLAG }; -+ struct dm_target_spec *tgt; -+ -+ tgt = dm_dev_ioctl(fd, DM_TABLE_STATUS, &dm); -+ if (!tgt) { -+ if (errno == 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] = { TEST_UNIT_READY }; -+ int sz = 0; -+ return do_sgio(fd, cdb, sense, NULL, &sz, 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 = 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 = mpath_persistent_reserve_in(fd, rq_servact, &resp, noisy, verbose); -+ if (r == MPATH_PR_SUCCESS) { -+ switch (rq_servact) { -+ case MPATH_PRIN_RKEY_SA: -+ case MPATH_PRIN_RRES_SA: { -+ struct prin_readdescr *out = &resp.prin_descriptor.prin_readkeys; -+ assert(sz >= 8); -+ written = MIN(out->additional_length + 8, sz); -+ stl_be_p(&data[0], out->prgeneration); -+ stl_be_p(&data[4], out->additional_length); -+ memcpy(&data[8], out->key_list, written - 8); -+ break; -+ } -+ case MPATH_PRIN_RCAP_SA: { -+ struct prin_capdescr *out = &resp.prin_descriptor.prin_readcap; -+ assert(sz >= 6); -+ written = 6; -+ stw_be_p(&data[0], out->length); -+ data[2] = out->flags[0]; -+ data[3] = out->flags[1]; -+ stw_be_p(&data[4], out->pr_type_mask); -+ 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 = cdb[1]; -+ int rq_scope = cdb[2] >> 4; -+ int rq_type = cdb[2] & 0xf; -+ struct prout_param_descriptor paramp; -+ char transportids[PR_HELPER_DATA_SIZE]; -+ int r; -+ -+ 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: -+ break; -+ case MPATH_PROUT_REG_MOV_SA: -+ /* Not supported by struct prout_param_descriptor. */ -+ 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 = param[10]; -+ if (sz > PR_OUT_FIXED_PARAM_SIZE) { -+ size_t transportid_len; -+ int i, j; -+ if (sz < PR_OUT_FIXED_PARAM_SIZE + 4) { -+ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN)); -+ return CHECK_CONDITION; -+ } -+ transportid_len = ldl_be_p(¶m[24]) + PR_OUT_FIXED_PARAM_SIZE + 4; -+ if (transportid_len > sz) { -+ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); -+ return CHECK_CONDITION; -+ } -+ for (i = PR_OUT_FIXED_PARAM_SIZE + 4, j = 0; i < transportid_len; ) { -+ struct transportid *id = (struct transportid *) &transportids[j]; -+ int len; -+ -+ id->format_code = param[i] & 0xc0; -+ id->protocol_id = param[i] & 0x0f; -+ switch (param[i] & 0xcf) { -+ case 0: -+ /* FC transport. */ -+ if (i + 24 > transportid_len) { -+ goto illegal_req; -+ } -+ memcpy(id->n_port_name, ¶m[i + 8], 8); -+ j += offsetof(struct transportid, n_port_name[8]); -+ i += 24; -+ break; -+ case 3: -+ case 0x43: -+ /* iSCSI transport. */ -+ len = lduw_be_p(¶m[i + 2]); -+ if (len > 252 || (len & 3) || i + len + 4 > transportid_len) { -+ /* For format code 00, the standard says the maximum is 223 -+ * plus the NUL terminator. For format code 01 there is no -+ * maximum length, but libmpathpersist ignores the first -+ * byte of id->iscsi_name so our maximum is 252. -+ */ -+ goto illegal_req; -+ } -+ if (memchr(¶m[i + 4], 0, len) == NULL) { -+ goto illegal_req; -+ } -+ memcpy(id->iscsi_name, ¶m[i + 2], len + 2); -+ j += offsetof(struct transportid, iscsi_name[len + 2]); -+ i += len + 4; -+ break; -+ case 6: -+ /* SAS transport. */ -+ if (i + 24 > transportid_len) { -+ goto illegal_req; -+ } -+ memcpy(id->sas_address, ¶m[i + 4], 8); -+ j += offsetof(struct transportid, sas_address[8]); -+ i += 24; -+ break; -+ default: -+ illegal_req: -+ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); -+ return CHECK_CONDITION; -+ } -+ -+ paramp.trnptid_list[paramp.num_transportid++] = id; -+ } -+ } -+ -+ r = 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 *resp_sz) - { -+#ifdef CONFIG_MPATH -+ if (is_mpath(fd)) { -+ /* multipath_pr_in fills the whole input buffer. */ -+ return multipath_pr_in(fd, cdb, sense, data, *resp_sz); -+ } -+#endif -+ - return do_sgio(fd, cdb, sense, data, resp_sz, - SG_DXFER_FROM_DEV); - } -@@ -214,7 +528,14 @@ 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) - { -- int resp_sz = sz; -+ int resp_sz; -+#ifdef CONFIG_MPATH -+ if (is_mpath(fd)) { -+ return multipath_pr_out(fd, cdb, sense, param, sz); -+ } -+#endif -+ -+ resp_sz = sz; - return do_sgio(fd, cdb, sense, (uint8_t *)param, &resp_sz, - SG_DXFER_TO_DEV); - } -@@ -525,6 +846,14 @@ static int drop_privileges(void) - return -1; - } - -+#ifdef CONFIG_MPATH -+ /* For /dev/mapper/control ioctls */ -+ if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, -+ CAP_SYS_ADMIN) < 0) { -+ return -1; -+ } -+#endif -+ - /* Change user/group id, retaining the capabilities. Because file descriptors - * are passed via SCM_RIGHTS, we don't need supplementary groups (and in - * fact the helper can run as "nobody"). -@@ -541,7 +870,7 @@ static int drop_privileges(void) - - int main(int argc, char **argv) - { -- const char *sopt = "hVk:fdT:u:g:q"; -+ const char *sopt = "hVk:fdT:u:g:vq"; - struct option lopt[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, -@@ -551,10 +880,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 = 0; -+ int loglevel = 1; - int quiet = 0; - char ch; - Error *local_err = NULL; -@@ -631,6 +962,9 @@ int main(int argc, char **argv) - case 'q': - quiet = 1; - break; -+ case 'v': -+ ++loglevel; -+ break; - case 'T': - g_free(trace_file); - trace_file = trace_opt_parse(optarg); -@@ -650,7 +984,8 @@ int main(int argc, char **argv) - } - - /* set verbosity */ -- verbose = !quiet; -+ noisy = !quiet && (loglevel >= 3); -+ verbose = quiet ? 0 : MIN(loglevel, 3); - - if (!trace_init_backends()) { - exit(EXIT_FAILURE); -@@ -658,6 +993,11 @@ int main(int argc, char **argv) - trace_init_file(trace_file); - qemu_set_log(LOG_TRACE); - -+#ifdef CONFIG_MPATH -+ dm_init(); -+ multipath_pr_init(); -+#endif -+ - socket_activation = check_socket_activation(); - if (socket_activation == 0) { - SocketAddress saddr; -diff --git a/scsi/utils.c b/scsi/utils.c -index fab60bdf20..5684951b12 100644 ---- a/scsi/utils.c -+++ b/scsi/utils.c -@@ -206,6 +206,11 @@ const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { - .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 - }; - -+/* Command aborted, LUN Communication Failure */ -+const struct SCSISense sense_code_LUN_COMM_FAILURE = { -+ .key = ABORTED_COMMAND, .asc = 0x08, .ascq = 0x00 -+}; -+ - /* Unit attention, Capacity data has changed */ - const struct SCSISense sense_code_CAPACITY_CHANGED = { - .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 -@@ -216,6 +221,11 @@ const struct SCSISense sense_code_RESET = { - .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 - }; - -+/* Unit attention, SCSI bus reset */ -+const struct SCSISense sense_code_SCSI_BUS_RESET = { -+ .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x02 -+}; -+ - /* Unit attention, No medium */ - const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { - .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 --- -2.13.5 - diff --git a/1015-scsi-add-persistent-reservation-manager-using-qemu-p.patch b/1015-scsi-add-persistent-reservation-manager-using-qemu-p.patch deleted file mode 100644 index acc9e7f..0000000 --- a/1015-scsi-add-persistent-reservation-manager-using-qemu-p.patch +++ /dev/null @@ -1,335 +0,0 @@ -From 3caf122d29ecc3317671a9f651a236e8d02e2e90 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Mon, 21 Aug 2017 18:58:56 +0200 -Subject: [PATCH 15/15] scsi: add persistent reservation manager using - qemu-pr-helper - -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 | 302 +++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 303 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 += utils.o - --block-obj-$(CONFIG_LINUX) += pr-manager.o -+block-obj-$(CONFIG_LINUX) += 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..82ff6b6123 ---- /dev/null -+++ b/scsi/pr-manager-helper.c -@@ -0,0 +1,302 @@ -+/* -+ * Persistent reservation manager that talks to qemu-pr-helper -+ * -+ * Copyright (c) 2017 Red Hat, Inc. -+ * -+ * Author: Paolo Bonzini -+ * -+ * This code is licensed under the LGPL v2.1 or later. -+ * -+ */ -+ -+#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) \ -+ OBJECT_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 = qio_channel_read_all(pr_mgr->ioc, buf, sz, errp); -+ -+ if (r < 0) { -+ object_unref(OBJECT(pr_mgr->ioc)); -+ pr_mgr->ioc = NULL; -+ return -EINVAL; -+ } -+ -+ return 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 = (fd != -1); -+ while (sz > 0) { -+ struct iovec iov; -+ ssize_t n_written; -+ -+ iov.iov_base = (void *)buf; -+ iov.iov_len = sz; -+ n_written = qio_channel_writev_full(QIO_CHANNEL(pr_mgr->ioc), &iov, 1, -+ nfds ? &fd : NULL, nfds, errp); -+ -+ if (n_written <= 0) { -+ assert(n_written != QIO_CHANNEL_ERR_BLOCK); -+ object_unref(OBJECT(pr_mgr->ioc)); -+ return n_written < 0 ? -EINVAL : 0; -+ } -+ -+ nfds = 0; -+ buf += n_written; -+ sz -= n_written; -+ } -+ -+ return 0; -+} -+ -+/* Called with lock held. */ -+static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, -+ Error **errp) -+{ -+ char *path = g_strdup(pr_mgr->path); -+ SocketAddress saddr = { -+ .type = SOCKET_ADDRESS_TYPE_UNIX, -+ .u.q_unix.path = path -+ }; -+ QIOChannelSocket *sioc = qio_channel_socket_new(); -+ Error *local_err = NULL; -+ -+ uint32_t flags; -+ int r; -+ -+ assert(!pr_mgr->ioc); -+ qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper"); -+ qio_channel_socket_connect_sync(sioc, -+ &saddr, -+ &local_err); -+ g_free(path); -+ 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 = QIO_CHANNEL(sioc); -+ -+ /* A simple feature negotation protocol, even though there is -+ * no optional feature right now. -+ */ -+ r = pr_manager_helper_read(pr_mgr, &flags, sizeof(flags), errp); -+ if (r < 0) { -+ goto out_close; -+ } -+ -+ flags = 0; -+ r = pr_manager_helper_write(pr_mgr, -1, &flags, sizeof(flags), errp); -+ if (r < 0) { -+ goto out_close; -+ } -+ -+ return 0; -+ -+out_close: -+ object_unref(OBJECT(pr_mgr->ioc)); -+ pr_mgr->ioc = NULL; -+ return r; -+} -+ -+static int pr_manager_helper_run(PRManager *p, -+ int fd, struct sg_io_hdr *io_hdr) -+{ -+ PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(p); -+ -+ uint32_t len; -+ PRHelperResponse resp; -+ int ret; -+ int expected_dir; -+ int attempts; -+ uint8_t cdb[PR_HELPER_CDB_SIZE] = { 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] == PERSISTENT_RESERVE_OUT || cdb[0] == PERSISTENT_RESERVE_IN); -+ expected_dir = -+ (cdb[0] == PERSISTENT_RESERVE_OUT ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV); -+ if (io_hdr->dxfer_direction != expected_dir) { -+ return -EINVAL; -+ } -+ -+ len = scsi_cdb_xfer(cdb); -+ if (io_hdr->dxfer_len < len || len > PR_HELPER_DATA_SIZE) { -+ return -EINVAL; -+ } -+ -+ qemu_mutex_lock(&pr_mgr->lock); -+ -+ /* Try to reconnect while sending the CDB. */ -+ for (attempts = 0; attempts < PR_MAX_RECONNECT_ATTEMPTS; attempts++) { -+ if (!pr_mgr->ioc) { -+ ret = pr_manager_helper_initialize(pr_mgr, NULL); -+ if (ret < 0) { -+ qemu_mutex_unlock(&pr_mgr->lock); -+ g_usleep(G_USEC_PER_SEC); -+ qemu_mutex_lock(&pr_mgr->lock); -+ continue; -+ } -+ } -+ -+ ret = pr_manager_helper_write(pr_mgr, fd, cdb, ARRAY_SIZE(cdb), NULL); -+ if (ret >= 0) { -+ break; -+ } -+ } -+ if (ret < 0) { -+ 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 == SG_DXFER_TO_DEV) { -+ io_hdr->resid = io_hdr->dxfer_len - len; -+ ret = pr_manager_helper_write(pr_mgr, -1, io_hdr->dxferp, len, NULL); -+ if (ret < 0) { -+ goto out; -+ } -+ } -+ ret = pr_manager_helper_read(pr_mgr, &resp, sizeof(resp), NULL); -+ if (ret < 0) { -+ goto out; -+ } -+ -+ resp.result = be32_to_cpu(resp.result); -+ resp.sz = be32_to_cpu(resp.sz); -+ if (io_hdr->dxfer_direction == SG_DXFER_FROM_DEV) { -+ assert(resp.sz <= io_hdr->dxfer_len); -+ ret = pr_manager_helper_read(pr_mgr, io_hdr->dxferp, resp.sz, NULL); -+ if (ret < 0) { -+ goto out; -+ } -+ io_hdr->resid = io_hdr->dxfer_len - resp.sz; -+ } else { -+ assert(resp.sz == 0); -+ } -+ -+ io_hdr->status = resp.result; -+ if (resp.result == CHECK_CONDITION) { -+ io_hdr->driver_status = SG_ERR_DRIVER_SENSE; -+ io_hdr->sb_len_wr = 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 = scsi_build_sense(io_hdr->sbp, -+ SENSE_CODE(LUN_COMM_FAILURE)); -+ io_hdr->driver_status = SG_ERR_DRIVER_SENSE; -+ io_hdr->sb_len_wr = MIN(io_hdr->mx_sb_len, sense_len); -+ io_hdr->status = CHECK_CONDITION; -+ } -+ qemu_mutex_unlock(&pr_mgr->lock); -+ return ret; -+} -+ -+static void pr_manager_helper_complete(UserCreatable *uc, Error **errp) -+{ -+ PRManagerHelper *pr_mgr = 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 = PR_MANAGER_HELPER(obj); -+ -+ return g_strdup(pr_mgr->path); -+} -+ -+static void set_path(Object *obj, const char *str, Error **errp) -+{ -+ PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); -+ -+ g_free(pr_mgr->path); -+ pr_mgr->path = g_strdup(str); -+} -+ -+static void pr_manager_helper_instance_finalize(Object *obj) -+{ -+ PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj); -+ -+ object_unref(OBJECT(pr_mgr->ioc)); -+ qemu_mutex_destroy(&pr_mgr->lock); -+} -+ -+static void pr_manager_helper_instance_init(Object *obj) -+{ -+ PRManagerHelper *pr_mgr = 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 = PR_MANAGER_CLASS(klass); -+ UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); -+ -+ object_class_property_add_str(klass, "path", get_path, set_path, -+ &error_abort); -+ uc_klass->complete = pr_manager_helper_complete; -+ prmgr_klass->run = pr_manager_helper_run; -+} -+ -+static const TypeInfo pr_manager_helper_info = { -+ .parent = TYPE_PR_MANAGER, -+ .name = TYPE_PR_MANAGER_HELPER, -+ .instance_size = sizeof(PRManagerHelper), -+ .instance_init = pr_manager_helper_instance_init, -+ .instance_finalize = pr_manager_helper_instance_finalize, -+ .class_init = 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); --- -2.13.5 - diff --git a/1016-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch b/1016-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch deleted file mode 100644 index 7815618..0000000 --- a/1016-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 23c1595b0297e6ca8f37559af6f0b8533aa1fd99 Mon Sep 17 00:00:00 2001 -From: "Daniel P. Berrange" -Date: Tue, 29 Aug 2017 17:03:30 +0100 -Subject: [PATCH] crypto: fix test cert generation to not use SHA1 algorithm - -GNUTLS 3.6.0 marked SHA1 as untrusted for certificates. -Unfortunately the gnutls_x509_crt_sign() method we are -using to create certificates in the test suite is fixed -to always use SHA1. We must switch to a different method -and explicitly ask for SHA256. - -Reviewed-by: Eric Blake -Signed-off-by: Daniel P. Berrange ---- - tests/crypto-tls-x509-helpers.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c -index 64073d3bd3..173d4e28fb 100644 ---- a/tests/crypto-tls-x509-helpers.c -+++ b/tests/crypto-tls-x509-helpers.c -@@ -406,7 +406,8 @@ test_tls_generate_cert(QCryptoTLSTestCertReq *req, - * If no 'ca' is set then we are self signing - * the cert. This is done for the root CA certs - */ -- err = gnutls_x509_crt_sign(crt, ca ? ca : crt, privkey); -+ err = gnutls_x509_crt_sign2(crt, ca ? ca : crt, privkey, -+ GNUTLS_DIG_SHA256, 0); - if (err < 0) { - g_critical("Failed to sign certificate %s", - gnutls_strerror(err)); --- -2.13.5 - diff --git a/1017-io-fix-check-for-handshake-completion-in-TLS-test.patch b/1017-io-fix-check-for-handshake-completion-in-TLS-test.patch deleted file mode 100644 index 536119d..0000000 --- a/1017-io-fix-check-for-handshake-completion-in-TLS-test.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 689ed13e73bdb5a5ca3366524475e3065fae854a Mon Sep 17 00:00:00 2001 -From: "Daniel P. Berrange" -Date: Tue, 29 Aug 2017 17:04:52 +0100 -Subject: [PATCH] io: fix check for handshake completion in TLS test - -The TLS I/O channel test had mistakenly used && instead -of || when checking for handshake completion. As a -result it could terminate the handshake process before -it had actually completed. This was harmless before but -changes in GNUTLS 3.6.0 exposed this bug and caused the -test suite to fail. - -Reviewed-by: Eric Blake -Signed-off-by: Daniel P. Berrange ---- - tests/test-io-channel-tls.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c -index ff96877323..a210d01ba5 100644 ---- a/tests/test-io-channel-tls.c -+++ b/tests/test-io-channel-tls.c -@@ -218,7 +218,7 @@ static void test_io_channel_tls(const void *opaque) - mainloop = g_main_context_default(); - do { - g_main_context_iteration(mainloop, TRUE); -- } while (!clientHandshake.finished && -+ } while (!clientHandshake.finished || - !serverHandshake.finished); - - g_assert(clientHandshake.failed == data->expectClientFail); --- -2.13.5 - diff --git a/1018-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch b/1018-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch deleted file mode 100644 index d4718ea..0000000 --- a/1018-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch +++ /dev/null @@ -1,34 +0,0 @@ -From d4adf9675801cd90e66ecfcd6a54ca1abc5a6698 Mon Sep 17 00:00:00 2001 -From: "Daniel P. Berrange" -Date: Fri, 21 Jul 2017 12:47:39 +0100 -Subject: [PATCH] io: fix temp directory used by test-io-channel-tls test - -The test-io-channel-tls test was mistakenly using two of the -same directories as test-crypto-tlssession. This causes a -sporadic failure when using make -j$BIGNUM. - -Reported-by: Dr. David Alan Gilbert -Reviewed-by: Dr. David Alan Gilbert -Signed-off-by: Daniel P. Berrange ---- - tests/test-io-channel-tls.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c -index 8eaa208e1b..ff96877323 100644 ---- a/tests/test-io-channel-tls.c -+++ b/tests/test-io-channel-tls.c -@@ -127,8 +127,8 @@ static void test_io_channel_tls(const void *opaque) - /* We'll use this for our fake client-server connection */ - g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); - --#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" --#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" -+#define CLIENT_CERT_DIR "tests/test-io-channel-tls-client/" -+#define SERVER_CERT_DIR "tests/test-io-channel-tls-server/" - mkdir(CLIENT_CERT_DIR, 0700); - mkdir(SERVER_CERT_DIR, 0700); - --- -2.13.5 - diff --git a/qemu.spec b/qemu.spec index e87fbb0..6b0dafb 100644 --- a/qemu.spec +++ b/qemu.spec @@ -106,8 +106,8 @@ Requires: %{name}-block-ssh = %{epoch}:%{version}-%{release} Summary: QEMU is a FAST! processor emulator Name: qemu -Version: 2.10.0 -Release: 7%{?rcrel}%{?dist} +Version: 2.10.1 +Release: 1%{?rcrel}%{?dist} Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD Group: Development/Tools @@ -142,24 +142,34 @@ Source21: 50-kvm-s390x.conf # /etc/security/limits.d/95-kvm-ppc64-memlock.conf Source22: 95-kvm-ppc64-memlock.conf -Patch1001: 1001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch -Patch1002: 1002-io-Yield-rather-than-wait-when-already-in-coroutine.patch -Patch1003: 1003-scsi-bus-correct-responses-for-INQUIRY-and-REQUEST-S.patch -Patch1004: 1004-scsi-Refactor-scsi-sense-interpreting-code.patch -Patch1005: 1005-scsi-Improve-scsi_sense_to_errno.patch -Patch1006: 1006-scsi-Introduce-scsi_sense_buf_to_errno.patch -Patch1007: 1007-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch -Patch1008: 1008-scsi-move-non-emulation-specific-code-to-scsi.patch -Patch1009: 1009-scsi-introduce-scsi_build_sense.patch -Patch1010: 1010-scsi-introduce-sg_io_sense_from_errno.patch -Patch1011: 1011-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch -Patch1012: 1012-scsi-file-posix-add-support-for-persistent-reservati.patch -Patch1013: 1013-scsi-build-qemu-pr-helper.patch -Patch1014: 1014-scsi-add-multipath-support-to-qemu-pr-helper.patch -Patch1015: 1015-scsi-add-persistent-reservation-manager-using-qemu-p.patch -Patch1016: 1016-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch -Patch1017: 1017-io-fix-check-for-handshake-completion-in-TLS-test.patch -Patch1018: 1018-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch +# Backport persistent reservation manager in preparation for SELinux work +Patch0001: 0001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch +Patch0002: 0002-io-Yield-rather-than-wait-when-already-in-coroutine.patch +Patch0003: 0003-scsi-Refactor-scsi-sense-interpreting-code.patch +Patch0004: 0004-scsi-Improve-scsi_sense_to_errno.patch +Patch0005: 0005-scsi-Introduce-scsi_sense_buf_to_errno.patch +Patch0006: 0006-scsi-rename-scsi_build_sense-to-scsi_convert_sense.patch +Patch0007: 0007-scsi-move-non-emulation-specific-code-to-scsi.patch +Patch0008: 0008-scsi-introduce-scsi_build_sense.patch +Patch0009: 0009-scsi-introduce-sg_io_sense_from_errno.patch +Patch0010: 0010-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch +Patch0011: 0011-scsi-file-posix-add-support-for-persistent-reservati.patch +Patch0012: 0012-scsi-build-qemu-pr-helper.patch +Patch0013: 0013-scsi-add-multipath-support-to-qemu-pr-helper.patch +Patch0014: 0014-scsi-add-persistent-reservation-manager-using-qemu-p.patch + +# Add patches from git master to fix TLS test suite with new GNUTLS +Patch0101: 0101-crypto-fix-test-cert-generation-to-not-use-SHA1-algo.patch +Patch0102: 0102-io-fix-check-for-handshake-completion-in-TLS-test.patch +Patch0103: 0103-io-fix-temp-directory-used-by-test-io-channel-tls-te.patch +# Fix ppc64 KVM failure (bz #1501936) +Patch0104: 0104-spapr-fallback-to-raw-mode-if-best-compat-mode-canno.patch +# CVE-2017-15038: 9p: information disclosure when reading extended +# attributes (bz #1499111) +Patch0105: 0105-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch +# CVE-2017-15268: potential memory exhaustion via websock connection to VNC +# (bz #1496882) +Patch0106: 0106-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch # documentation deps BuildRequires: texinfo @@ -2029,6 +2039,13 @@ getent passwd qemu >/dev/null || \ %changelog +* Thu Oct 19 2017 Cole Robinson - 2:2.10.1-1 +- Fix ppc64 KVM failure (bz #1501936) +- CVE-2017-15038: 9p: information disclosure when reading extended + attributes (bz #1499111) +- CVE-2017-15268: potential memory exhaustion via websock connection to VNC + (bz #1496882) + * Tue Oct 17 2017 Paolo Bonzini - 2:2.10.0-7 - Update patch 1014 for new libmultipath/libmpathpersist API - Force build to fail if multipath is not available diff --git a/sources b/sources index 34ef406..fa076e8 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (qemu-2.10.0.tar.xz) = 67891e78a0df8538838c5fc6d5208e1e0c23f608013818ea8f2f6b7dcbf80404113559b4d33aea32daf25bd43c2d8b5befbebf9fab16adf74a50218239cead53 +SHA512 (qemu-2.10.1.tar.xz) = 62e9717ede71a49f3ffd9b86c321470f64c90e575be1a9da01078cfc9466b0aeb08adf5d05bab7ee1e89dfce75def7b276af01f77b7151d406999e7af21b6711