Blame SOURCES/kvm-scsi-add-multipath-support-to-qemu-pr-helper.patch

4a2fec
From d9ebb2728bc92d055f178f1c45f82979c542a9aa Mon Sep 17 00:00:00 2001
4a2fec
From: Paolo Bonzini <pbonzini@redhat.com>
4a2fec
Date: Sat, 2 Dec 2017 12:19:50 +0100
4a2fec
Subject: [PATCH 24/36] scsi: add multipath support to qemu-pr-helper
4a2fec
4a2fec
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
4a2fec
Message-id: <20171202121953.13317-15-pbonzini@redhat.com>
4a2fec
Patchwork-id: 78083
4a2fec
O-Subject: [RHEL7.4 qemu-kvm-rhev PATCH 14/17] scsi: add multipath support to qemu-pr-helper
4a2fec
Bugzilla: 1464908
4a2fec
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
4a2fec
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
4a2fec
RH-Acked-by: John Snow <jsnow@redhat.com>
4a2fec
4a2fec
Proper support of persistent reservation for multipath devices requires
4a2fec
communication with the multipath daemon, so that the reservation is
4a2fec
registered and applied when a path comes up.  The device mapper
4a2fec
utilities provide a library to do so; this patch makes qemu-pr-helper.c
4a2fec
detect multipath devices and, when one is found, delegate the operation
4a2fec
to libmpathpersist.
4a2fec
4a2fec
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
4a2fec
(cherry picked from commit fe8fc5ae5c808e037fa4746cbfeb3c07ffe0af81)
4a2fec
4a2fec
[RHEL: do not use {0} because GCC does not like it; will be changed
4a2fec
 upstream, but squash it in here so that all commits compile - Paolo]
4a2fec
4a2fec
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
4a2fec
---
4a2fec
 Makefile              |   3 +
4a2fec
 configure             |  46 +++++++
4a2fec
 docs/pr-manager.rst   |  27 ++++
4a2fec
 include/scsi/utils.h  |   4 +
4a2fec
 scsi/qemu-pr-helper.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++-
4a2fec
 scsi/utils.c          |  10 ++
4a2fec
 6 files changed, 433 insertions(+), 3 deletions(-)
4a2fec
4a2fec
diff --git a/Makefile b/Makefile
4a2fec
index 3e76953..ae48405 100644
4a2fec
--- a/Makefile
4a2fec
+++ b/Makefile
4a2fec
@@ -387,6 +387,9 @@ fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal
4a2fec
 fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
4a2fec
 
4a2fec
 scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
4a2fec
+ifdef CONFIG_MPATH
4a2fec
+scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
4a2fec
+endif
4a2fec
 
4a2fec
 qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
4a2fec
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
4a2fec
diff --git a/configure b/configure
4a2fec
index 2df1b42..97ee507 100755
4a2fec
--- a/configure
4a2fec
+++ b/configure
4a2fec
@@ -291,6 +291,7 @@ pixman=""
4a2fec
 sdl=""
4a2fec
 sdlabi=""
4a2fec
 virtfs=""
4a2fec
+mpath=""
4a2fec
 vnc="yes"
4a2fec
 sparse="no"
4a2fec
 vde=""
4a2fec
@@ -956,6 +957,10 @@ for opt do
4a2fec
   ;;
4a2fec
   --enable-virtfs) virtfs="yes"
4a2fec
   ;;
4a2fec
+  --disable-mpath) mpath="no"
4a2fec
+  ;;
4a2fec
+  --enable-mpath) mpath="yes"
4a2fec
+  ;;
4a2fec
   --disable-vnc) vnc="no"
4a2fec
   ;;
4a2fec
   --enable-vnc) vnc="yes"
4a2fec
@@ -1510,6 +1515,7 @@ disabled with --disable-FEATURE, default is enabled if available:
4a2fec
   vnc-png         PNG compression for VNC server
4a2fec
   cocoa           Cocoa UI (Mac OS X only)
4a2fec
   virtfs          VirtFS
4a2fec
+  mpath           Multipath persistent reservation passthrough
4a2fec
   xen             xen backend driver support
4a2fec
   xen-pci-passthrough
4a2fec
   brlapi          BrlAPI (Braile)
4a2fec
@@ -3358,6 +3364,30 @@ else
4a2fec
 fi
4a2fec
 
4a2fec
 ##########################################
4a2fec
+# libmpathpersist probe
4a2fec
+
4a2fec
+if test "$mpath" != "no" ; then
4a2fec
+  cat > $TMPC <
4a2fec
+#include <libudev.h>
4a2fec
+#include <mpath_persist.h>
4a2fec
+unsigned mpath_mx_alloc_len = 1024;
4a2fec
+int logsink;
4a2fec
+int main(void) {
4a2fec
+    struct udev *udev = udev_new();
4a2fec
+    mpath_lib_init(udev);
4a2fec
+    return 0;
4a2fec
+}
4a2fec
+EOF
4a2fec
+  if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then
4a2fec
+    mpathpersist=yes
4a2fec
+  else
4a2fec
+    mpathpersist=no
4a2fec
+  fi
4a2fec
+else
4a2fec
+  mpathpersist=no
4a2fec
+fi
4a2fec
+
4a2fec
+##########################################
4a2fec
 # libcap probe
4a2fec
 
4a2fec
 if test "$cap" != "no" ; then
4a2fec
@@ -5075,12 +5105,24 @@ if test "$softmmu" = yes ; then
4a2fec
       fi
4a2fec
       virtfs=no
4a2fec
     fi
4a2fec
+    if test "$mpath" != no && test "$mpathpersist" = yes ; then
4a2fec
+      mpath=yes
4a2fec
+    else
4a2fec
+      if test "$mpath" = yes; then
4a2fec
+        error_exit "Multipath requires libmpathpersist devel"
4a2fec
+      fi
4a2fec
+      mpath=no
4a2fec
+    fi
4a2fec
     tools="$tools scsi/qemu-pr-helper\$(EXESUF)"
4a2fec
   else
4a2fec
     if test "$virtfs" = yes; then
4a2fec
       error_exit "VirtFS is supported only on Linux"
4a2fec
     fi
4a2fec
     virtfs=no
4a2fec
+    if test "$mpath" = yes; then
4a2fec
+      error_exit "Multipath is supported only on Linux"
4a2fec
+    fi
4a2fec
+    mpath=no
4a2fec
   fi
4a2fec
 fi
4a2fec
 
4a2fec
@@ -5327,6 +5369,7 @@ echo "Audio drivers     $audio_drv_list"
4a2fec
 echo "Block whitelist (rw) $block_drv_rw_whitelist"
4a2fec
 echo "Block whitelist (ro) $block_drv_ro_whitelist"
4a2fec
 echo "VirtFS support    $virtfs"
4a2fec
+echo "Multipath support $mpath"
4a2fec
 echo "VNC support       $vnc"
4a2fec
 if test "$vnc" = "yes" ; then
4a2fec
     echo "VNC SASL support  $vnc_sasl"
4a2fec
@@ -5777,6 +5820,9 @@ fi
4a2fec
 if test "$virtfs" = "yes" ; then
4a2fec
   echo "CONFIG_VIRTFS=y" >> $config_host_mak
4a2fec
 fi
4a2fec
+if test "$mpath" = "yes" ; then
4a2fec
+  echo "CONFIG_MPATH=y" >> $config_host_mak
4a2fec
+fi
4a2fec
 if test "$vhost_scsi" = "yes" ; then
4a2fec
   echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak
4a2fec
 fi
4a2fec
diff --git a/docs/pr-manager.rst b/docs/pr-manager.rst
4a2fec
index 7107e59..9b1de19 100644
4a2fec
--- a/docs/pr-manager.rst
4a2fec
+++ b/docs/pr-manager.rst
4a2fec
@@ -60,6 +60,7 @@ system service and supports the following option:
4a2fec
 
4a2fec
 -d, --daemon              run in the background
4a2fec
 -q, --quiet               decrease verbosity
4a2fec
+-v, --verbose             increase verbosity
4a2fec
 -f, --pidfile=path        PID file when running as a daemon
4a2fec
 -k, --socket=path         path to the socket
4a2fec
 -T, --trace=trace-opts    tracing options
4a2fec
@@ -82,3 +83,29 @@ its operation.  To do this, add the following options:
4a2fec
 
4a2fec
 -u, --user=user           user to drop privileges to
4a2fec
 -g, --group=group         group to drop privileges to
4a2fec
+
4a2fec
+---------------------------------------------
4a2fec
+Multipath devices and persistent reservations
4a2fec
+---------------------------------------------
4a2fec
+
4a2fec
+Proper support of persistent reservation for multipath devices requires
4a2fec
+communication with the multipath daemon, so that the reservation is
4a2fec
+registered and applied when a path is newly discovered or becomes online
4a2fec
+again.  :command:`qemu-pr-helper` can do this if the ``libmpathpersist``
4a2fec
+library was available on the system at build time.
4a2fec
+
4a2fec
+As of August 2017, a reservation key must be specified in ``multipath.conf``
4a2fec
+for ``multipathd`` to check for persistent reservation for newly
4a2fec
+discovered paths or reinstated paths.  The attribute can be added
4a2fec
+to the ``defaults`` section or the ``multipaths`` section; for example::
4a2fec
+
4a2fec
+    multipaths {
4a2fec
+        multipath {
4a2fec
+            wwid   XXXXXXXXXXXXXXXX
4a2fec
+            alias      yellow
4a2fec
+            reservation_key  0x123abc
4a2fec
+        }
4a2fec
+    }
4a2fec
+
4a2fec
+Linking :program:`qemu-pr-helper` to ``libmpathpersist`` does not impede
4a2fec
+its usage on regular SCSI devices.
4a2fec
diff --git a/include/scsi/utils.h b/include/scsi/utils.h
4a2fec
index d301b31..00a4bdb 100644
4a2fec
--- a/include/scsi/utils.h
4a2fec
+++ b/include/scsi/utils.h
4a2fec
@@ -72,10 +72,14 @@ extern const struct SCSISense sense_code_IO_ERROR;
4a2fec
 extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
4a2fec
 /* Command aborted, Logical Unit failure */
4a2fec
 extern const struct SCSISense sense_code_LUN_FAILURE;
4a2fec
+/* Command aborted, LUN Communication failure */
4a2fec
+extern const struct SCSISense sense_code_LUN_COMM_FAILURE;
4a2fec
 /* Command aborted, Overlapped Commands Attempted */
4a2fec
 extern const struct SCSISense sense_code_OVERLAPPED_COMMANDS;
4a2fec
 /* LUN not ready, Capacity data has changed */
4a2fec
 extern const struct SCSISense sense_code_CAPACITY_CHANGED;
4a2fec
+/* Unit attention, SCSI bus reset */
4a2fec
+extern const struct SCSISense sense_code_SCSI_BUS_RESET;
4a2fec
 /* LUN not ready, Medium not present */
4a2fec
 extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM;
4a2fec
 /* Unit attention, Power on, reset or bus device reset occurred */
4a2fec
diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c
4a2fec
index f46266f..42bc2cf 100644
4a2fec
--- a/scsi/qemu-pr-helper.c
4a2fec
+++ b/scsi/qemu-pr-helper.c
4a2fec
@@ -30,6 +30,12 @@
4a2fec
 #include <pwd.h>
4a2fec
 #include <grp.h>
4a2fec
 
4a2fec
+#ifdef CONFIG_MPATH
4a2fec
+#include <libudev.h>
4a2fec
+#include <mpath_cmd.h>
4a2fec
+#include <mpath_persist.h>
4a2fec
+#endif
4a2fec
+
4a2fec
 #include "qapi/error.h"
4a2fec
 #include "qemu-common.h"
4a2fec
 #include "qemu/cutils.h"
4a2fec
@@ -60,6 +66,7 @@ static enum { RUNNING, TERMINATE, TERMINATING } state;
4a2fec
 static QIOChannelSocket *server_ioc;
4a2fec
 static int server_watch;
4a2fec
 static int num_active_sockets = 1;
4a2fec
+static int noisy;
4a2fec
 static int verbose;
4a2fec
 
4a2fec
 #ifdef CONFIG_LIBCAP
4a2fec
@@ -204,9 +211,316 @@ static int do_sgio(int fd, const uint8_t *cdb, uint8_t *sense,
4a2fec
     return r;
4a2fec
 }
4a2fec
 
4a2fec
+/* Device mapper interface */
4a2fec
+
4a2fec
+#ifdef CONFIG_MPATH
4a2fec
+#define CONTROL_PATH "/dev/mapper/control"
4a2fec
+
4a2fec
+typedef struct DMData {
4a2fec
+    struct dm_ioctl dm;
4a2fec
+    uint8_t data[1024];
4a2fec
+} DMData;
4a2fec
+
4a2fec
+static int control_fd;
4a2fec
+
4a2fec
+static void *dm_ioctl(int ioc, struct dm_ioctl *dm)
4a2fec
+{
4a2fec
+    static DMData d;
4a2fec
+    memcpy(&d.dm, dm, sizeof(d.dm));
4a2fec
+    QEMU_BUILD_BUG_ON(sizeof(d.data) < sizeof(struct dm_target_spec));
4a2fec
+
4a2fec
+    d.dm.version[0] = DM_VERSION_MAJOR;
4a2fec
+    d.dm.version[1] = 0;
4a2fec
+    d.dm.version[2] = 0;
4a2fec
+    d.dm.data_size = 1024;
4a2fec
+    d.dm.data_start = offsetof(DMData, data);
4a2fec
+    if (ioctl(control_fd, ioc, &d) < 0) {
4a2fec
+        return NULL;
4a2fec
+    }
4a2fec
+    memcpy(dm, &d.dm, sizeof(d.dm));
4a2fec
+    return &d.data;
4a2fec
+}
4a2fec
+
4a2fec
+static void *dm_dev_ioctl(int fd, int ioc, struct dm_ioctl *dm)
4a2fec
+{
4a2fec
+    struct stat st;
4a2fec
+    int r;
4a2fec
+
4a2fec
+    r = fstat(fd, &st);
4a2fec
+    if (r < 0) {
4a2fec
+        perror("fstat");
4a2fec
+        exit(1);
4a2fec
+    }
4a2fec
+
4a2fec
+    dm->dev = st.st_rdev;
4a2fec
+    return dm_ioctl(ioc, dm);
4a2fec
+}
4a2fec
+
4a2fec
+static void dm_init(void)
4a2fec
+{
4a2fec
+    control_fd = open(CONTROL_PATH, O_RDWR);
4a2fec
+    if (control_fd < 0) {
4a2fec
+        perror("Cannot open " CONTROL_PATH);
4a2fec
+        exit(1);
4a2fec
+    }
4a2fec
+    struct dm_ioctl dm = {};
4a2fec
+    if (!dm_ioctl(DM_VERSION, &dm)) {
4a2fec
+        perror("ioctl");
4a2fec
+        exit(1);
4a2fec
+    }
4a2fec
+    if (dm.version[0] != DM_VERSION_MAJOR) {
4a2fec
+        fprintf(stderr, "Unsupported device mapper interface");
4a2fec
+        exit(1);
4a2fec
+    }
4a2fec
+}
4a2fec
+
4a2fec
+/* Variables required by libmultipath and libmpathpersist.  */
4a2fec
+QEMU_BUILD_BUG_ON(PR_HELPER_DATA_SIZE > MPATH_MAX_PARAM_LEN);
4a2fec
+unsigned mpath_mx_alloc_len = PR_HELPER_DATA_SIZE;
4a2fec
+int logsink;
4a2fec
+
4a2fec
+static void multipath_pr_init(void)
4a2fec
+{
4a2fec
+    static struct udev *udev;
4a2fec
+
4a2fec
+    udev = udev_new();
4a2fec
+    mpath_lib_init(udev);
4a2fec
+}
4a2fec
+
4a2fec
+static int is_mpath(int fd)
4a2fec
+{
4a2fec
+    struct dm_ioctl dm = { .flags = DM_NOFLUSH_FLAG };
4a2fec
+    struct dm_target_spec *tgt;
4a2fec
+
4a2fec
+    tgt = dm_dev_ioctl(fd, DM_TABLE_STATUS, &dm;;
4a2fec
+    if (!tgt) {
4a2fec
+        if (errno == ENXIO) {
4a2fec
+            return 0;
4a2fec
+        }
4a2fec
+        perror("ioctl");
4a2fec
+        exit(EXIT_FAILURE);
4a2fec
+    }
4a2fec
+    return !strncmp(tgt->target_type, "multipath", DM_MAX_TYPE_NAME);
4a2fec
+}
4a2fec
+
4a2fec
+static int mpath_reconstruct_sense(int fd, int r, uint8_t *sense)
4a2fec
+{
4a2fec
+    switch (r) {
4a2fec
+    case MPATH_PR_SUCCESS:
4a2fec
+        return GOOD;
4a2fec
+    case MPATH_PR_SENSE_NOT_READY:
4a2fec
+    case MPATH_PR_SENSE_MEDIUM_ERROR:
4a2fec
+    case MPATH_PR_SENSE_HARDWARE_ERROR:
4a2fec
+    case MPATH_PR_SENSE_ABORTED_COMMAND:
4a2fec
+        {
4a2fec
+            /* libmpathpersist ate the exact sense.  Try to find it by
4a2fec
+             * issuing TEST UNIT READY.
4a2fec
+             */
4a2fec
+            uint8_t cdb[6] = { TEST_UNIT_READY };
4a2fec
+            int sz = 0;
4a2fec
+            return do_sgio(fd, cdb, sense, NULL, &sz, SG_DXFER_NONE);
4a2fec
+        }
4a2fec
+
4a2fec
+    case MPATH_PR_SENSE_UNIT_ATTENTION:
4a2fec
+        /* Congratulations libmpathpersist, you ruined the Unit Attention...
4a2fec
+         * Return a heavyweight one.
4a2fec
+         */
4a2fec
+        scsi_build_sense(sense, SENSE_CODE(SCSI_BUS_RESET));
4a2fec
+        return CHECK_CONDITION;
4a2fec
+    case MPATH_PR_SENSE_INVALID_OP:
4a2fec
+        /* Only one valid sense.  */
4a2fec
+        scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE));
4a2fec
+        return CHECK_CONDITION;
4a2fec
+    case MPATH_PR_ILLEGAL_REQ:
4a2fec
+        /* Guess.  */
4a2fec
+        scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM));
4a2fec
+        return CHECK_CONDITION;
4a2fec
+    case MPATH_PR_NO_SENSE:
4a2fec
+        scsi_build_sense(sense, SENSE_CODE(NO_SENSE));
4a2fec
+        return CHECK_CONDITION;
4a2fec
+
4a2fec
+    case MPATH_PR_RESERV_CONFLICT:
4a2fec
+        return RESERVATION_CONFLICT;
4a2fec
+
4a2fec
+    case MPATH_PR_OTHER:
4a2fec
+    default:
4a2fec
+        scsi_build_sense(sense, SENSE_CODE(LUN_COMM_FAILURE));
4a2fec
+        return CHECK_CONDITION;
4a2fec
+    }
4a2fec
+}
4a2fec
+
4a2fec
+static int multipath_pr_in(int fd, const uint8_t *cdb, uint8_t *sense,
4a2fec
+                           uint8_t *data, int sz)
4a2fec
+{
4a2fec
+    int rq_servact = cdb[1];
4a2fec
+    struct prin_resp resp;
4a2fec
+    size_t written;
4a2fec
+    int r;
4a2fec
+
4a2fec
+    switch (rq_servact) {
4a2fec
+    case MPATH_PRIN_RKEY_SA:
4a2fec
+    case MPATH_PRIN_RRES_SA:
4a2fec
+    case MPATH_PRIN_RCAP_SA:
4a2fec
+        break;
4a2fec
+    case MPATH_PRIN_RFSTAT_SA:
4a2fec
+        /* Nobody implements it anyway, so bail out. */
4a2fec
+    default:
4a2fec
+        /* Cannot parse any other output.  */
4a2fec
+        scsi_build_sense(sense, SENSE_CODE(INVALID_FIELD));
4a2fec
+        return CHECK_CONDITION;
4a2fec
+    }
4a2fec
+
4a2fec
+    r = mpath_persistent_reserve_in(fd, rq_servact, &resp, noisy, verbose);
4a2fec
+    if (r == MPATH_PR_SUCCESS) {
4a2fec
+        switch (rq_servact) {
4a2fec
+        case MPATH_PRIN_RKEY_SA:
4a2fec
+        case MPATH_PRIN_RRES_SA: {
4a2fec
+            struct prin_readdescr *out = &resp.prin_descriptor.prin_readkeys;
4a2fec
+            assert(sz >= 8);
4a2fec
+            written = MIN(out->additional_length + 8, sz);
4a2fec
+            stl_be_p(&data[0], out->prgeneration);
4a2fec
+            stl_be_p(&data[4], out->additional_length);
4a2fec
+            memcpy(&data[8], out->key_list, written - 8);
4a2fec
+            break;
4a2fec
+        }
4a2fec
+        case MPATH_PRIN_RCAP_SA: {
4a2fec
+            struct prin_capdescr *out = &resp.prin_descriptor.prin_readcap;
4a2fec
+            assert(sz >= 6);
4a2fec
+            written = 6;
4a2fec
+            stw_be_p(&data[0], out->length);
4a2fec
+            data[2] = out->flags[0];
4a2fec
+            data[3] = out->flags[1];
4a2fec
+            stw_be_p(&data[4], out->pr_type_mask);
4a2fec
+            break;
4a2fec
+        }
4a2fec
+        default:
4a2fec
+            scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE));
4a2fec
+            return CHECK_CONDITION;
4a2fec
+        }
4a2fec
+        assert(written <= sz);
4a2fec
+        memset(data + written, 0, sz - written);
4a2fec
+    }
4a2fec
+
4a2fec
+    return mpath_reconstruct_sense(fd, r, sense);
4a2fec
+}
4a2fec
+
4a2fec
+static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense,
4a2fec
+                            const uint8_t *param, int sz)
4a2fec
+{
4a2fec
+    int rq_servact = cdb[1];
4a2fec
+    int rq_scope = cdb[2] >> 4;
4a2fec
+    int rq_type = cdb[2] & 0xf;
4a2fec
+    struct prout_param_descriptor paramp;
4a2fec
+    char transportids[PR_HELPER_DATA_SIZE];
4a2fec
+    int r;
4a2fec
+
4a2fec
+    switch (rq_servact) {
4a2fec
+    case MPATH_PROUT_REG_SA:
4a2fec
+    case MPATH_PROUT_RES_SA:
4a2fec
+    case MPATH_PROUT_REL_SA:
4a2fec
+    case MPATH_PROUT_CLEAR_SA:
4a2fec
+    case MPATH_PROUT_PREE_SA:
4a2fec
+    case MPATH_PROUT_PREE_AB_SA:
4a2fec
+    case MPATH_PROUT_REG_IGN_SA:
4a2fec
+        break;
4a2fec
+    case MPATH_PROUT_REG_MOV_SA:
4a2fec
+        /* Not supported by struct prout_param_descriptor.  */
4a2fec
+    default:
4a2fec
+        /* Cannot parse any other input.  */
4a2fec
+        scsi_build_sense(sense, SENSE_CODE(INVALID_FIELD));
4a2fec
+        return CHECK_CONDITION;
4a2fec
+    }
4a2fec
+
4a2fec
+    /* Convert input data, especially transport IDs, to the structs
4a2fec
+     * used by libmpathpersist (which, of course, will immediately
4a2fec
+     * do the opposite).
4a2fec
+     */
4a2fec
+    memset(&paramp, 0, sizeof(paramp));
4a2fec
+    memcpy(&paramp.key, &param[0], 8);
4a2fec
+    memcpy(&paramp.sa_key, &param[8], 8);
4a2fec
+    paramp.sa_flags = param[10];
4a2fec
+    if (sz > PR_OUT_FIXED_PARAM_SIZE) {
4a2fec
+        size_t transportid_len;
4a2fec
+        int i, j;
4a2fec
+        if (sz < PR_OUT_FIXED_PARAM_SIZE + 4) {
4a2fec
+            scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN));
4a2fec
+            return CHECK_CONDITION;
4a2fec
+        }
4a2fec
+        transportid_len = ldl_be_p(&param[24]) + PR_OUT_FIXED_PARAM_SIZE + 4;
4a2fec
+        if (transportid_len > sz) {
4a2fec
+            scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM));
4a2fec
+            return CHECK_CONDITION;
4a2fec
+        }
4a2fec
+        for (i = PR_OUT_FIXED_PARAM_SIZE + 4, j = 0; i < transportid_len; ) {
4a2fec
+            struct transportid *id = (struct transportid *) &transportids[j];
4a2fec
+            int len;
4a2fec
+
4a2fec
+            id->format_code = param[i] & 0xc0;
4a2fec
+            id->protocol_id = param[i] & 0x0f;
4a2fec
+            switch (param[i] & 0xcf) {
4a2fec
+            case 0:
4a2fec
+                /* FC transport.  */
4a2fec
+                if (i + 24 > transportid_len) {
4a2fec
+                    goto illegal_req;
4a2fec
+                }
4a2fec
+                memcpy(id->n_port_name, &param[i + 8], 8);
4a2fec
+                j += offsetof(struct transportid, n_port_name[8]);
4a2fec
+                i += 24;
4a2fec
+                break;
4a2fec
+            case 3:
4a2fec
+            case 0x43:
4a2fec
+                /* iSCSI transport.  */
4a2fec
+                len = lduw_be_p(&param[i + 2]);
4a2fec
+                if (len > 252 || (len & 3) || i + len + 4 > transportid_len) {
4a2fec
+                    /* For format code 00, the standard says the maximum is 223
4a2fec
+                     * plus the NUL terminator.  For format code 01 there is no
4a2fec
+                     * maximum length, but libmpathpersist ignores the first
4a2fec
+                     * byte of id->iscsi_name so our maximum is 252.
4a2fec
+                     */
4a2fec
+                    goto illegal_req;
4a2fec
+                }
4a2fec
+                if (memchr(&param[i + 4], 0, len) == NULL) {
4a2fec
+                    goto illegal_req;
4a2fec
+                }
4a2fec
+                memcpy(id->iscsi_name, &param[i + 2], len + 2);
4a2fec
+                j += offsetof(struct transportid, iscsi_name[len + 2]);
4a2fec
+                i += len + 4;
4a2fec
+                break;
4a2fec
+            case 6:
4a2fec
+                /* SAS transport.  */
4a2fec
+                if (i + 24 > transportid_len) {
4a2fec
+                    goto illegal_req;
4a2fec
+                }
4a2fec
+                memcpy(id->sas_address, &param[i + 4], 8);
4a2fec
+                j += offsetof(struct transportid, sas_address[8]);
4a2fec
+                i += 24;
4a2fec
+                break;
4a2fec
+            default:
4a2fec
+            illegal_req:
4a2fec
+                scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM));
4a2fec
+                return CHECK_CONDITION;
4a2fec
+            }
4a2fec
+
4a2fec
+            paramp.trnptid_list[paramp.num_transportid++] = id;
4a2fec
+        }
4a2fec
+    }
4a2fec
+
4a2fec
+    r = mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type,
4a2fec
+                                     &paramp, noisy, verbose);
4a2fec
+    return mpath_reconstruct_sense(fd, r, sense);
4a2fec
+}
4a2fec
+#endif
4a2fec
+
4a2fec
 static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense,
4a2fec
                     uint8_t *data, int *resp_sz)
4a2fec
 {
4a2fec
+#ifdef CONFIG_MPATH
4a2fec
+    if (is_mpath(fd)) {
4a2fec
+        /* multipath_pr_in fills the whole input buffer.  */
4a2fec
+        return multipath_pr_in(fd, cdb, sense, data, *resp_sz);
4a2fec
+    }
4a2fec
+#endif
4a2fec
+
4a2fec
     return do_sgio(fd, cdb, sense, data, resp_sz,
4a2fec
                    SG_DXFER_FROM_DEV);
4a2fec
 }
4a2fec
@@ -214,7 +528,14 @@ static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense,
4a2fec
 static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense,
4a2fec
                      const uint8_t *param, int sz)
4a2fec
 {
4a2fec
-    int resp_sz = sz;
4a2fec
+    int resp_sz;
4a2fec
+#ifdef CONFIG_MPATH
4a2fec
+    if (is_mpath(fd)) {
4a2fec
+        return multipath_pr_out(fd, cdb, sense, param, sz);
4a2fec
+    }
4a2fec
+#endif
4a2fec
+
4a2fec
+    resp_sz = sz;
4a2fec
     return do_sgio(fd, cdb, sense, (uint8_t *)param, &resp_sz,
4a2fec
                    SG_DXFER_TO_DEV);
4a2fec
 }
4a2fec
@@ -525,6 +846,14 @@ static int drop_privileges(void)
4a2fec
         return -1;
4a2fec
     }
4a2fec
 
4a2fec
+#ifdef CONFIG_MPATH
4a2fec
+    /* For /dev/mapper/control ioctls */
4a2fec
+    if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
4a2fec
+                     CAP_SYS_ADMIN) < 0) {
4a2fec
+        return -1;
4a2fec
+    }
4a2fec
+#endif
4a2fec
+
4a2fec
     /* Change user/group id, retaining the capabilities.  Because file descriptors
4a2fec
      * are passed via SCM_RIGHTS, we don't need supplementary groups (and in
4a2fec
      * fact the helper can run as "nobody").
4a2fec
@@ -541,7 +870,7 @@ static int drop_privileges(void)
4a2fec
 
4a2fec
 int main(int argc, char **argv)
4a2fec
 {
4a2fec
-    const char *sopt = "hVk:fdT:u:g:q";
4a2fec
+    const char *sopt = "hVk:fdT:u:g:vq";
4a2fec
     struct option lopt[] = {
4a2fec
         { "help", no_argument, NULL, 'h' },
4a2fec
         { "version", no_argument, NULL, 'V' },
4a2fec
@@ -551,10 +880,12 @@ int main(int argc, char **argv)
4a2fec
         { "trace", required_argument, NULL, 'T' },
4a2fec
         { "user", required_argument, NULL, 'u' },
4a2fec
         { "group", required_argument, NULL, 'g' },
4a2fec
+        { "verbose", no_argument, NULL, 'v' },
4a2fec
         { "quiet", no_argument, NULL, 'q' },
4a2fec
         { NULL, 0, NULL, 0 }
4a2fec
     };
4a2fec
     int opt_ind = 0;
4a2fec
+    int loglevel = 1;
4a2fec
     int quiet = 0;
4a2fec
     int ch;
4a2fec
     Error *local_err = NULL;
4a2fec
@@ -631,6 +962,9 @@ int main(int argc, char **argv)
4a2fec
         case 'q':
4a2fec
             quiet = 1;
4a2fec
             break;
4a2fec
+        case 'v':
4a2fec
+            ++loglevel;
4a2fec
+            break;
4a2fec
         case 'T':
4a2fec
             g_free(trace_file);
4a2fec
             trace_file = trace_opt_parse(optarg);
4a2fec
@@ -650,7 +984,8 @@ int main(int argc, char **argv)
4a2fec
     }
4a2fec
 
4a2fec
     /* set verbosity */
4a2fec
-    verbose = !quiet;
4a2fec
+    noisy = !quiet && (loglevel >= 3);
4a2fec
+    verbose = quiet ? 0 : MIN(loglevel, 3);
4a2fec
 
4a2fec
     if (!trace_init_backends()) {
4a2fec
         exit(EXIT_FAILURE);
4a2fec
@@ -658,6 +993,11 @@ int main(int argc, char **argv)
4a2fec
     trace_init_file(trace_file);
4a2fec
     qemu_set_log(LOG_TRACE);
4a2fec
 
4a2fec
+#ifdef CONFIG_MPATH
4a2fec
+    dm_init();
4a2fec
+    multipath_pr_init();
4a2fec
+#endif
4a2fec
+
4a2fec
     socket_activation = check_socket_activation();
4a2fec
     if (socket_activation == 0) {
4a2fec
         SocketAddress saddr;
4a2fec
diff --git a/scsi/utils.c b/scsi/utils.c
4a2fec
index fab60bd..5684951 100644
4a2fec
--- a/scsi/utils.c
4a2fec
+++ b/scsi/utils.c
4a2fec
@@ -206,6 +206,11 @@ const struct SCSISense sense_code_OVERLAPPED_COMMANDS = {
4a2fec
     .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00
4a2fec
 };
4a2fec
 
4a2fec
+/* Command aborted, LUN Communication Failure */
4a2fec
+const struct SCSISense sense_code_LUN_COMM_FAILURE = {
4a2fec
+    .key = ABORTED_COMMAND, .asc = 0x08, .ascq = 0x00
4a2fec
+};
4a2fec
+
4a2fec
 /* Unit attention, Capacity data has changed */
4a2fec
 const struct SCSISense sense_code_CAPACITY_CHANGED = {
4a2fec
     .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09
4a2fec
@@ -216,6 +221,11 @@ const struct SCSISense sense_code_RESET = {
4a2fec
     .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00
4a2fec
 };
4a2fec
 
4a2fec
+/* Unit attention, SCSI bus reset */
4a2fec
+const struct SCSISense sense_code_SCSI_BUS_RESET = {
4a2fec
+    .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x02
4a2fec
+};
4a2fec
+
4a2fec
 /* Unit attention, No medium */
4a2fec
 const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = {
4a2fec
     .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00
4a2fec
-- 
4a2fec
1.8.3.1
4a2fec