Compare commits
3 Commits
master
...
private-bo
Author | SHA1 | Date | |
---|---|---|---|
|
d170b998a2 | ||
|
3b6075a0ff | ||
|
2a7a602870 |
384
1001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch
Normal file
384
1001-io-add-new-qio_channel_-readv-writev-read-write-_all.patch
Normal file
@ -0,0 +1,384 @@
|
||||
From 758d848ef10835108a75187d1a1a0418167f04b2 Mon Sep 17 00:00:00 2001
|
||||
From: "Daniel P. Berrange" <berrange@redhat.com>
|
||||
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 <eblake@redhat.com>
|
||||
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -0,0 +1,56 @@
|
||||
From 5171f2cd3612ce2772b272e4dd8119fdb0c06124 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
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 <eblake@redhat.com>
|
||||
Message-Id: <20170905191114.5959-2-eblake@redhat.com>
|
||||
Acked-by: Daniel P. Berrange <berrange@redhat.com>
|
||||
[commit message updated]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -0,0 +1,71 @@
|
||||
From b07725e3e5e05610691815ee921a6b3307685815 Mon Sep 17 00:00:00 2001
|
||||
From: Hannes Reinecke <hare@suse.de>
|
||||
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 <hare@suse.com>
|
||||
Message-Id: <1503049022-14749-1-git-send-email-hare@suse.de>
|
||||
Reported-by: Laszlo Ersek <lersek@redhat.com>
|
||||
Fixes: ded6ddc5a7b95217557fa360913d1213e12d4a6d
|
||||
Cc: qemu-stable@nongnu.org
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Signed-off-by: Hannes Reinecke <hare@suse.de>
|
||||
---
|
||||
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
|
||||
|
194
1004-scsi-Refactor-scsi-sense-interpreting-code.patch
Normal file
194
1004-scsi-Refactor-scsi-sense-interpreting-code.patch
Normal file
@ -0,0 +1,194 @@
|
||||
From f35ef58b2bac932bbd379602fe23e7a190530075 Mon Sep 17 00:00:00 2001
|
||||
From: Fam Zheng <famz@redhat.com>
|
||||
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 <famz@redhat.com>
|
||||
Message-Id: <20170821141008.19383-2-famz@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
---
|
||||
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 <pbonzini@redhat.com>
|
||||
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 <iscsi/iscsi.h>
|
||||
#include <iscsi/scsi-lowlevel.h>
|
||||
@@ -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 <famz@redhat.com>
|
||||
+ *
|
||||
+ * 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 <famz@redhat.com>
|
||||
+ *
|
||||
+ * 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
|
||||
|
61
1005-scsi-Improve-scsi_sense_to_errno.patch
Normal file
61
1005-scsi-Improve-scsi_sense_to_errno.patch
Normal file
@ -0,0 +1,61 @@
|
||||
From a7dc92dac7cedb3ba6b6d724c7579f05399e2f2e Mon Sep 17 00:00:00 2001
|
||||
From: Fam Zheng <famz@redhat.com>
|
||||
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 <famz@redhat.com>
|
||||
Message-Id: <20170821141008.19383-3-famz@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
---
|
||||
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
|
||||
|
68
1006-scsi-Introduce-scsi_sense_buf_to_errno.patch
Normal file
68
1006-scsi-Introduce-scsi_sense_buf_to_errno.patch
Normal file
@ -0,0 +1,68 @@
|
||||
From eadabcbc81d44ee0bc0d0d80697cc1142df61178 Mon Sep 17 00:00:00 2001
|
||||
From: Fam Zheng <famz@redhat.com>
|
||||
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 <famz@redhat.com>
|
||||
Message-Id: <20170821141008.19383-4-famz@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
---
|
||||
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
|
||||
|
@ -0,0 +1,92 @@
|
||||
From ba83805030e07cade8d17b5d1b4bd1d296caff1b Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
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é <f4bug@amsat.org>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
---
|
||||
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
|
||||
|
1434
1008-scsi-move-non-emulation-specific-code-to-scsi.patch
Normal file
1434
1008-scsi-move-non-emulation-specific-code-to-scsi.patch
Normal file
File diff suppressed because it is too large
Load Diff
77
1009-scsi-introduce-scsi_build_sense.patch
Normal file
77
1009-scsi-introduce-scsi_build_sense.patch
Normal file
@ -0,0 +1,77 @@
|
||||
From 82e922c8be1b32eeb0c8c22165cdeff39e12d3ef Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
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é <f4bug@amsat.org>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
---
|
||||
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
|
||||
|
137
1010-scsi-introduce-sg_io_sense_from_errno.patch
Normal file
137
1010-scsi-introduce-sg_io_sense_from_errno.patch
Normal file
@ -0,0 +1,137 @@
|
||||
From 1ebf7935eca7f2a1f5a1376ee3b234f4fce98023 Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
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 <stefanha@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
---
|
||||
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
|
||||
|
250
1011-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch
Normal file
250
1011-scsi-move-block-scsi.h-to-include-scsi-constants.h.patch
Normal file
@ -0,0 +1,250 @@
|
||||
From e2b560d9f9966d7256488d0a359200c65c2c07f8 Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
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é <f4bug@amsat.org>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
---
|
||||
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 <scsi/sg.h>
|
||||
#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 <scsi/sg.h>
|
||||
-#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
|
||||
|
440
1012-scsi-file-posix-add-support-for-persistent-reservati.patch
Normal file
440
1012-scsi-file-posix-add-support-for-persistent-reservati.patch
Normal file
@ -0,0 +1,440 @@
|
||||
From 5c4a4b825189c2e9f322c8673104add7f76e38d5 Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
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 <pbonzini@redhat.com>
|
||||
---
|
||||
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 <paths.h>
|
||||
#include <sys/param.h>
|
||||
@@ -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 {
|
||||
+ /* <private> */
|
||||
+ Object parent;
|
||||
+} PRManager;
|
||||
+
|
||||
+/**
|
||||
+ * PRManagerClass:
|
||||
+ * @parent_class: the base class
|
||||
+ * @run: callback invoked in thread pool context
|
||||
+ */
|
||||
+typedef struct PRManagerClass {
|
||||
+ /* <private> */
|
||||
+ ObjectClass parent_class;
|
||||
+
|
||||
+ /* <public> */
|
||||
+ 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 <pbonzini@redhat.com>
|
||||
+ *
|
||||
+ * This code is licensed under the LGPL.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include "qemu/osdep.h"
|
||||
+#include <scsi/sg.h>
|
||||
+
|
||||
+#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
|
||||
|
1013
1013-scsi-build-qemu-pr-helper.patch
Normal file
1013
1013-scsi-build-qemu-pr-helper.patch
Normal file
File diff suppressed because it is too large
Load Diff
660
1014-scsi-add-multipath-support-to-qemu-pr-helper.patch
Normal file
660
1014-scsi-add-multipath-support-to-qemu-pr-helper.patch
Normal file
@ -0,0 +1,660 @@
|
||||
From 43fedb8ae2c2b3bbb43023c118be708226e38179 Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
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 <pbonzini@redhat.com>
|
||||
---
|
||||
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,30 @@ else
|
||||
fi
|
||||
|
||||
##########################################
|
||||
+# libmpathpersist probe
|
||||
+
|
||||
+if test "$mpath" != "no" ; then
|
||||
+ cat > $TMPC <<EOF
|
||||
+#include <libudev.h>
|
||||
+#include <mpath_persist.h>
|
||||
+unsigned mpath_mx_alloc_len = 1024;
|
||||
+int logsink;
|
||||
+int main(void) {
|
||||
+ struct udev *udev = udev_new();
|
||||
+ mpath_lib_init(udev);
|
||||
+ 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 <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
+#ifdef CONFIG_MPATH
|
||||
+#include <libudev.h>
|
||||
+#include <mpath_cmd.h>
|
||||
+#include <mpath_persist.h>
|
||||
+#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,316 @@ 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);
|
||||
+unsigned mpath_mx_alloc_len = PR_HELPER_DATA_SIZE;
|
||||
+int logsink;
|
||||
+
|
||||
+static void multipath_pr_init(void)
|
||||
+{
|
||||
+ static struct udev *udev;
|
||||
+
|
||||
+ udev = udev_new();
|
||||
+ mpath_lib_init(udev);
|
||||
+}
|
||||
+
|
||||
+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
|
||||
|
335
1015-scsi-add-persistent-reservation-manager-using-qemu-p.patch
Normal file
335
1015-scsi-add-persistent-reservation-manager-using-qemu-p.patch
Normal file
@ -0,0 +1,335 @@
|
||||
From 3caf122d29ecc3317671a9f651a236e8d02e2e90 Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
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 <pbonzini@redhat.com>
|
||||
---
|
||||
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 <pbonzini@redhat.com>
|
||||
+ *
|
||||
+ * 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 <scsi/sg.h>
|
||||
+
|
||||
+#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 {
|
||||
+ /* <private> */
|
||||
+ 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
|
||||
|
15
qemu-pr-helper.service
Normal file
15
qemu-pr-helper.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Persistent Reservation Daemon for QEMU
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/tmp
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/qemu-pr-helper
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/var/run
|
||||
RestrictAddressFamilies=AF_UNIX
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
|
||||
[Install]
|
8
qemu-pr-helper.socket
Normal file
8
qemu-pr-helper.socket
Normal file
@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Persistent Reservation Daemon for QEMU
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/qemu-pr-helper.sock
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
37
qemu.spec
37
qemu.spec
@ -107,7 +107,7 @@ Requires: %{name}-block-ssh = %{epoch}:%{version}-%{release}
|
||||
Summary: QEMU is a FAST! processor emulator
|
||||
Name: qemu
|
||||
Version: 2.10.0
|
||||
Release: 2%{?rcrel}%{?dist}
|
||||
Release: 3%{?rcrel}%{?dist}
|
||||
Epoch: 2
|
||||
License: GPLv2+ and LGPLv2+ and BSD
|
||||
Group: Development/Tools
|
||||
@ -132,6 +132,9 @@ Source11: 99-qemu-guest-agent.rules
|
||||
Source12: bridge.conf
|
||||
# qemu-kvm back compat wrapper installed as /usr/bin/qemu-kvm
|
||||
Source13: qemu-kvm.sh
|
||||
# PR manager service
|
||||
Source14: qemu-pr-helper.service
|
||||
Source15: qemu-pr-helper.socket
|
||||
# /etc/modprobe.d/kvm.conf
|
||||
Source20: kvm.conf
|
||||
# /etc/sysctl.d/50-kvm-s390x.conf
|
||||
@ -139,6 +142,22 @@ 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
|
||||
|
||||
# documentation deps
|
||||
BuildRequires: texinfo
|
||||
# For /usr/bin/pod2man
|
||||
@ -165,6 +184,9 @@ BuildRequires: libaio-devel
|
||||
BuildRequires: pulseaudio-libs-devel
|
||||
# alsa audio output
|
||||
BuildRequires: alsa-lib-devel
|
||||
# qemu-pr-helper multipath support (requires libudev too)
|
||||
BuildRequires: device-mapper-multipath-devel
|
||||
BuildRequires: systemd-devel
|
||||
# iscsi drive support
|
||||
BuildRequires: libiscsi-devel
|
||||
# NFS drive support
|
||||
@ -178,7 +200,7 @@ BuildRequires: ncurses-devel
|
||||
# used by 9pfs
|
||||
BuildRequires: libattr-devel
|
||||
BuildRequires: libcap-devel
|
||||
# used by qemu-bridge-helper
|
||||
# used by qemu-bridge-helper and qemu-pr-helper
|
||||
BuildRequires: libcap-ng-devel
|
||||
# spice usb redirection support
|
||||
BuildRequires: usbredir-devel >= 0.5.2
|
||||
@ -1197,6 +1219,10 @@ install -D -p -m 0644 %{_sourcedir}/kvm.conf %{buildroot}%{_sysconfdir}/modprobe
|
||||
install -m 0644 %{_sourcedir}/qemu-guest-agent.service %{buildroot}%{_unitdir}
|
||||
install -m 0644 %{_sourcedir}/99-qemu-guest-agent.rules %{buildroot}%{_udevdir}
|
||||
|
||||
# Install qemu-pr-helper service
|
||||
install -m 0644 %{_sourcedir}/qemu-pr-helper.service %{buildroot}%{_unitdir}
|
||||
install -m 0644 %{_sourcedir}/qemu-pr-helper.socket %{buildroot}%{_unitdir}
|
||||
|
||||
%ifarch s390x
|
||||
install -d %{buildroot}%{_sysconfdir}/sysctl.d
|
||||
install -m 0644 %{_sourcedir}/50-kvm-s390x.conf %{buildroot}%{_sysconfdir}/sysctl.d
|
||||
@ -1513,6 +1539,9 @@ getent passwd qemu >/dev/null || \
|
||||
%{_mandir}/man7/qemu-ga-ref.7*
|
||||
%{_mandir}/man7/qemu-qmp-ref.7*
|
||||
%{_bindir}/virtfs-proxy-helper
|
||||
%{_bindir}/qemu-pr-helper
|
||||
%{_unitdir}/qemu-pr-helper.service
|
||||
%{_unitdir}/qemu-pr-helper.socket
|
||||
%attr(4755, root, root) %{_libexecdir}/qemu-bridge-helper
|
||||
%config(noreplace) %{_sysconfdir}/sasl2/qemu.conf
|
||||
%config(noreplace) %{_sysconfdir}/modprobe.d/kvm.conf
|
||||
@ -2000,6 +2029,10 @@ getent passwd qemu >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Fri Sep 22 2017 Paolo Bonzini <pbonzini@redhat.com> - 2:2.10.0-3
|
||||
- Backport persistent reservation manager in preparation for SELinux work
|
||||
- Fix previous patch
|
||||
|
||||
* Mon Sep 18 2017 Nathaniel McCallum <npmccallum@redhat.com> - 2:2.10.0-2
|
||||
- Fix endianness of e_type in the ppc64le binfmt
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user