summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Marzinski <bmarzins@redhat.com>2017-04-10 18:57:22 -0400
committerBenjamin Marzinski <bmarzins@redhat.com>2017-04-10 18:57:22 -0400
commitf9dfbb37ac3932ad97b93ae00dc9da03a44acbf3 (patch)
treeecd2d96dbbbdc2fdd23b374d0123106741948986
parent8e9e3b7467947f8aada42f17e23e98b8be76285f (diff)
downloaddevice-mapper-multipath-f9dfbb37ac3932ad97b93ae00dc9da03a44acbf3.tar.gz
device-mapper-multipath-f9dfbb37ac3932ad97b93ae00dc9da03a44acbf3.tar.xz
device-mapper-multipath-f9dfbb37ac3932ad97b93ae00dc9da03a44acbf3.zip
device-mapper-multipath-0.4.9-86
Modify 0136-RHBZ-1304687-wait-for-map-add.patch * switch to missing_uev_wait_timeout to stop waiting for uev Refresh 0137-RHBZ-1280524-clear-chkr-msg.patch Refresh 0150-RHBZ-1253913-fix-startup-msg.patch Refresh 0154-UPBZ-1291406-disable-reinstate.patch Refresh 0156-UPBZ-1313324-dont-fail-discovery.patch Refresh 0161-RHBZ-1311659-no-kpartx.patch Refresh 0167-RHBZ-1335176-fix-show-cmds.patch Add 0173-RH-update-man-page.patch Add 0174-RHBZ-1362396-modprobe.patch * make starting the multipathd service modprobe dm-multipath in the sysvinit scripts Add 0175-RHBZ-1357382-ordering.patch * force multipathd.service to start after systemd-udev-trigger.service Add 0176-RHBZ-1363830-fix-rename.patch * initialized a variable to make dm_rename not fail randomly Add 0177-libmultipath-correctly-initialize-pp-sg_id.patch * This and all the following patches add the rbd patch checker Add 0178-libmultipath-add-rbd-discovery.patch Add 0179-multipath-tools-add-checker-callout-to-repair-path.patch Add 0180-multipath-tools-Add-rbd-checker.patch Add 0181-multipath-tools-Add-rbd-to-the-hwtable.patch Add 0182-multipath-tools-check-for-initialized-checker-before.patch Add 0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch Add 0184-rbd-fix-sync-repair-support.patch Add 0185-rbd-check-for-nonshared-clients.patch Add 0186-rbd-check-for-exclusive-lock-enabled.patch Add 0187-rbd-fixup-log-messages.patch Add 0188-RHBZ-1368501-dont-exit.patch * make multipathd not exit if it encounters recoverable errors on startup Add 0189-RHBZ-1368211-remove-retries.patch * add "remove_retries" multipath.conf parameter to make multiple attempts to remove a multipath device if it is busy. Add 0190-RHBZ-1380602-rbd-lock-on-read.patch * pass lock_on_read when remapping image Add 0191-RHBZ-1169168-disable-changed-paths.patch * add "disabled_changed_wwids" multipath.conf parameter to disable paths whose wwid changes Add 0192-RHBZ-1362409-infinibox-config.patch Add 0194-RHBZ-1351964-kpartx-recurse.patch * fix recursion on corrupt dos partitions Add 0195-RHBZ-1359510-no-daemon-msg.patch * print a messages when multipathd isn't running Add 0196-RHBZ-1239173-dont-set-flag.patch * don't set reload flag on reloads when you gain your first valid path Add 0197-RHBZ-1394059-max-sectors-kb.patch * add "max_sectors_kb" multipath.conf parameter to set max_sectors_kb on a multipath device and all its path devices Add 0198-RHBZ-1372032-detect-path-checker.patch * add "detect_checker" multipath.conf parameter to detect ALUA arrays and set the path checker to TUR Add 0199-RHBZ-1279355-3pardata-config.patch Add 0200-RHBZ-1402092-orphan-status.patch * clear status on orphan paths Add 0201-RHBZ-1403552-silence-warning.patch Add 0202-RHBZ-1362120-skip-prio.patch * don't run prio on failed paths Add 0203-RHBZ-1363718-add-msgs.patch Add 0204-RHBZ-1406226-nimble-config.patch Add 0205-RHBZ-1416569-reset-stats.patch * add "reset maps stats" and "reset map <map> stats" multipathd interactive commands to reset the stats tracked by multipathd Add 0206-RHBZ-1239173-pt2-no-paths.patch * make multipath correctly disable scanning and rules running when it gets a uevent and there are not valid paths. Add 0207-UP-add-libmpathcmd.patch * New shared library, libmpathcmd, that sends and receives messages from multipathd. device-mapper-multipath now uses this library internally. Add 0208-UPBZ-1430097-multipathd-IPC-changes.patch * validation that modifying commands are coming from root. Add 0209-UPBZ-1430097-multipath-C-API.patch * New shared library. libdmmp, that presents the information from multipathd in a structured manner to make it easier for callers to use Add 0210-RH-fix-uninstall.patch * Minor compilation fixes Add 0211-RH-strlen-fix.patch * checks that variables are not NULL before passing them to strlen Add 0212-RHBZ-1431562-for-read-only.patch Make 3 new subpackages * device-mapper-multipath-devel, libdmmp, and libdmmp-devel. libmpathcmd and libmpathprio are in device-mapper-multipath-libs and device-mapper-multipath-devel. libdmmp is in its own subpackages Move libmpathprio devel files to device-mapper-multipath-devel Added BuildRequires on librados2-devel
-rw-r--r--0136-RHBZ-1304687-wait-for-map-add.patch83
-rw-r--r--0137-RHBZ-1280524-clear-chkr-msg.patch2
-rw-r--r--0139-RHBZ-1273173-queue-no-daemon-doc.patch22
-rw-r--r--0150-RHBZ-1253913-fix-startup-msg.patch14
-rw-r--r--0154-UPBZ-1291406-disable-reinstate.patch8
-rw-r--r--0156-UPBZ-1313324-dont-fail-discovery.patch4
-rw-r--r--0161-RHBZ-1311659-no-kpartx.patch12
-rw-r--r--0167-RHBZ-1335176-fix-show-cmds.patch2
-rw-r--r--0173-RH-update-man-page.patch99
-rw-r--r--0173-RHBZ-1239173-dont-set-flag.patch38
-rw-r--r--0174-RHBZ-1362396-modprobe.patch16
-rw-r--r--0175-RHBZ-1357382-ordering.patch17
-rw-r--r--0176-RHBZ-1363830-fix-rename.patch17
-rw-r--r--0177-libmultipath-correctly-initialize-pp-sg_id.patch38
-rw-r--r--0178-libmultipath-add-rbd-discovery.patch219
-rw-r--r--0179-multipath-tools-add-checker-callout-to-repair-path.patch250
-rw-r--r--0180-multipath-tools-Add-rbd-checker.patch746
-rw-r--r--0181-multipath-tools-Add-rbd-to-the-hwtable.patch51
-rw-r--r--0182-multipath-tools-check-for-initialized-checker-before.patch49
-rw-r--r--0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch91
-rw-r--r--0184-rbd-fix-sync-repair-support.patch29
-rw-r--r--0185-rbd-check-for-nonshared-clients.patch32
-rw-r--r--0186-rbd-check-for-exclusive-lock-enabled.patch56
-rw-r--r--0187-rbd-fixup-log-messages.patch238
-rw-r--r--0188-RHBZ-1368501-dont-exit.patch208
-rw-r--r--0189-RHBZ-1368211-remove-retries.patch168
-rw-r--r--0190-RHBZ-1380602-rbd-lock-on-read.patch38
-rw-r--r--0191-RHBZ-1169168-disable-changed-paths.patch210
-rw-r--r--0192-RHBZ-1362409-infinibox-config.patch34
-rw-r--r--0194-RHBZ-1351964-kpartx-recurse.patch17
-rw-r--r--0195-RHBZ-1359510-no-daemon-msg.patch111
-rw-r--r--0196-RHBZ-1239173-dont-set-flag.patch38
-rw-r--r--0197-RHBZ-1394059-max-sectors-kb.patch474
-rw-r--r--0198-RHBZ-1372032-detect-path-checker.patch377
-rw-r--r--0199-RHBZ-1279355-3pardata-config.patch17
-rw-r--r--0200-RHBZ-1402092-orphan-status.patch29
-rw-r--r--0201-RHBZ-1403552-silence-warning.patch59
-rw-r--r--0202-RHBZ-1362120-skip-prio.patch18
-rw-r--r--0203-RHBZ-1363718-add-msgs.patch24
-rw-r--r--0204-RHBZ-1406226-nimble-config.patch28
-rw-r--r--0205-RHBZ-1416569-reset-stats.patch108
-rw-r--r--0206-RHBZ-1239173-pt2-no-paths.patch121
-rw-r--r--0207-UP-add-libmpathcmd.patch840
-rw-r--r--0208-UPBZ-1430097-multipathd-IPC-changes.patch280
-rw-r--r--0209-UPBZ-1430097-multipath-C-API.patch5632
-rw-r--r--0210-RH-fix-uninstall.patch25
-rw-r--r--0211-RH-strlen-fix.patch35
-rw-r--r--0212-RHBZ-1431562-for-read-only.patch56
-rw-r--r--device-mapper-multipath.spec248
49 files changed, 11253 insertions, 75 deletions
diff --git a/0136-RHBZ-1304687-wait-for-map-add.patch b/0136-RHBZ-1304687-wait-for-map-add.patch
index cd9cdee..dbada98 100644
--- a/0136-RHBZ-1304687-wait-for-map-add.patch
+++ b/0136-RHBZ-1304687-wait-for-map-add.patch
@@ -3,14 +3,14 @@
libmultipath/config.h | 2
libmultipath/configure.c | 4 +
libmultipath/defaults.h | 1
- libmultipath/dict.c | 25 +++++++++
+ libmultipath/dict.c | 25 ++++++++
libmultipath/structs.h | 2
multipath.conf.defaults | 1
- multipath/multipath.conf.5 | 8 +++
- multipathd/cli_handlers.c | 65 ++++++++++++++++++++----
- multipathd/main.c | 119 +++++++++++++++++++++++++++++++++++++++++++--
+ multipath/multipath.conf.5 | 8 ++
+ multipathd/cli_handlers.c | 65 ++++++++++++++++++----
+ multipathd/main.c | 132 +++++++++++++++++++++++++++++++++++++++++++--
multipathd/main.h | 1
- 11 files changed, 216 insertions(+), 13 deletions(-)
+ 11 files changed, 229 insertions(+), 13 deletions(-)
Index: multipath-tools-130222/libmultipath/configure.c
===================================================================
@@ -22,7 +22,7 @@ Index: multipath-tools-130222/libmultipath/configure.c
mpp->action = ACT_NOTHING;
+ else {
+ mpp->wait_for_udev = 1;
-+ mpp->uev_msg_tick = conf->uev_msg_delay;
++ mpp->uev_wait_tick = conf->uev_wait_timeout;
+ }
}
dm_setgeometry(mpp);
@@ -36,7 +36,7 @@ Index: multipath-tools-130222/libmultipath/structs.h
int queuedio;
int action;
+ int wait_for_udev;
-+ int uev_msg_tick;
++ int uev_wait_tick;
int pgfailback;
int failback_tick;
int rr_weight;
@@ -175,7 +175,7 @@ Index: multipath-tools-130222/libmultipath/config.h
int retrigger_delay;
int new_bindings_in_boot;
+ int delayed_reconfig;
-+ int uev_msg_delay;
++ int uev_wait_timeout;
unsigned int version[3];
char * dev;
@@ -293,28 +293,41 @@ Index: multipath-tools-130222/multipathd/main.c
retval = reload_map(vecs, pp->mpp, 0);
condlog(2, "%s: map %s reloaded (retval %d)",
-@@ -1063,6 +1136,20 @@ followover_should_failback(struct path *
+@@ -1063,6 +1136,33 @@ followover_should_failback(struct path *
}
static void
-+missing_uev_message_tick(vector mpvec)
++missing_uev_wait_tick(struct vectors *vecs)
+{
+ struct multipath * mpp;
+ unsigned int i;
++ int timed_out = 0;
+
-+ vector_foreach_slot (mpvec, mpp, i) {
-+ if (mpp->wait_for_udev && --mpp->uev_msg_tick <= 0) {
-+ condlog(0, "%s: startup incomplete. Still waiting on udev", mpp->alias);
-+ mpp->uev_msg_tick = conf->uev_msg_delay;
++ vector_foreach_slot (vecs->mpvec, mpp, i) {
++ if (mpp->wait_for_udev && --mpp->uev_wait_tick <= 0) {
++ timed_out = 1;
++ condlog(0, "%s: timeout waiting on creation uevent. enabling reloads", mpp->alias);
++ if (mpp->wait_for_udev > 1 && update_map(mpp, vecs)) {
++ /* update_map removed map */
++ i--;
++ continue;
++ }
++ mpp->wait_for_udev = 0;
+ }
+ }
++
++ if (timed_out && conf->delayed_reconfig &&
++ !need_to_delay_reconfig(vecs)) {
++ condlog(2, "reconfigure (delayed)");
++ reconfigure(vecs);
++ }
+}
+
+static void
defered_failback_tick (vector mpvec)
{
struct multipath * mpp;
-@@ -1316,6 +1403,9 @@ check_path (struct vectors * vecs, struc
+@@ -1316,6 +1416,9 @@ check_path (struct vectors * vecs, struc
pp->state = newstate;
@@ -324,15 +337,15 @@ Index: multipath-tools-130222/multipathd/main.c
/*
* path prio refreshing
*/
-@@ -1369,6 +1459,7 @@ checkerloop (void *ap)
+@@ -1369,6 +1472,7 @@ checkerloop (void *ap)
if (vecs->mpvec) {
defered_failback_tick(vecs->mpvec);
retry_count_tick(vecs->mpvec);
-+ missing_uev_message_tick(vecs->mpvec);
++ missing_uev_wait_tick(vecs);
}
if (count)
count--;
-@@ -1465,6 +1556,22 @@ configure (struct vectors * vecs, int st
+@@ -1465,6 +1569,22 @@ configure (struct vectors * vecs, int st
}
int
@@ -355,7 +368,7 @@ Index: multipath-tools-130222/multipathd/main.c
reconfigure (struct vectors * vecs)
{
struct config * old = conf;
-@@ -1544,12 +1651,18 @@ void
+@@ -1544,12 +1664,18 @@ void
handle_signals(void)
{
if (reconfig_sig && running_state == DAEMON_RUNNING) {
@@ -396,7 +409,7 @@ Index: multipath-tools-130222/libmultipath/config.c
conf->retrigger_tries = DEFAULT_RETRIGGER_TRIES;
conf->retrigger_delay = DEFAULT_RETRIGGER_DELAY;
conf->new_bindings_in_boot = 0;
-+ conf->uev_msg_delay = DEFAULT_UEV_MSG_DELAY;
++ conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;
/*
* preload default hwtable
@@ -408,7 +421,7 @@ Index: multipath-tools-130222/libmultipath/defaults.h
#define DEFAULT_DELAY_CHECKS DELAY_CHECKS_OFF
#define DEFAULT_RETRIGGER_DELAY 10
#define DEFAULT_RETRIGGER_TRIES 3
-+#define DEFAULT_UEV_MSG_DELAY 30
++#define DEFAULT_UEV_WAIT_TIMEOUT 30
#define DEFAULT_CHECKINT 5
#define MAX_CHECKINT(a) (a << 2)
@@ -420,7 +433,7 @@ Index: multipath-tools-130222/libmultipath/dict.c
}
static int
-+def_uev_msg_delay_handler(vector strvec)
++def_uev_wait_timeout_handler(vector strvec)
+{
+ char *buff;
+
@@ -429,9 +442,9 @@ Index: multipath-tools-130222/libmultipath/dict.c
+ if (!buff)
+ return 1;
+
-+ conf->uev_msg_delay = atoi(buff);
-+ if (conf->uev_msg_delay <= 0)
-+ conf->uev_msg_delay = DEFAULT_UEV_MSG_DELAY;
++ conf->uev_wait_timeout = atoi(buff);
++ if (conf->uev_wait_timeout <= 0)
++ conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;
+ FREE(buff);
+
+ return 0;
@@ -445,9 +458,9 @@ Index: multipath-tools-130222/libmultipath/dict.c
}
static int
-+snprint_def_uev_msg_delay (char * buff, int len, void * data)
++snprint_def_uev_wait_timeout (char * buff, int len, void * data)
+{
-+ return snprintf(buff, len, "%i", conf->uev_msg_delay);
++ return snprintf(buff, len, "%i", conf->uev_wait_timeout);
+}
+
+static int
@@ -458,7 +471,7 @@ Index: multipath-tools-130222/libmultipath/dict.c
install_keyword("delay_wait_checks", &def_delay_wait_checks_handler, &snprint_def_delay_wait_checks);
install_keyword("retrigger_tries", &def_retrigger_tries_handler, &snprint_def_retrigger_tries);
install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay);
-+ install_keyword("missing_uev_msg_delay", &def_uev_msg_delay_handler, &snprint_def_uev_msg_delay);
++ install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout);
install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot);
__deprecated install_keyword("default_selector", &def_selector_handler, NULL);
__deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
@@ -470,7 +483,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# config_dir "/etc/multipath/conf.d"
# delay_watch_checks no
# delay_wait_checks no
-+# missing_uev_msg_delay 30
++# missing_uev_wait_timeout 30
#}
#blacklist {
# devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
@@ -483,12 +496,12 @@ Index: multipath-tools-130222/multipath/multipath.conf.5
checks. Default is
.I no
+.TP
-+.B missing_uev_msg_delay
-+Controls how long multipathd will wait, after a new multipath device is created,
-+to receive a change event from udev for the device, before printing a warning
-+message. This warning message will print every
-+.I missing_uev_msg_delay
-+seconds until the uevent is received. the default is
++.B missing_uev_wait_timeout
++Controls how many seconds multipathd will wait, after a new multipath device
++is created, to receive a change event from udev for the device, before
++automatically enabling device reloads. Usually multipathd will delay reloads
++on a device until it receives a change uevent from the initial table load. The
++default is
+.I 30
.
.SH "blacklist section"
diff --git a/0137-RHBZ-1280524-clear-chkr-msg.patch b/0137-RHBZ-1280524-clear-chkr-msg.patch
index b7cd118..c05e28e 100644
--- a/0137-RHBZ-1280524-clear-chkr-msg.patch
+++ b/0137-RHBZ-1280524-clear-chkr-msg.patch
@@ -6,7 +6,7 @@ Index: multipath-tools-130222/multipathd/main.c
===================================================================
--- multipath-tools-130222.orig/multipathd/main.c
+++ multipath-tools-130222/multipathd/main.c
-@@ -1257,6 +1257,8 @@ check_path (struct vectors * vecs, struc
+@@ -1270,6 +1270,8 @@ check_path (struct vectors * vecs, struc
newstate = path_offline(pp);
if (newstate == PATH_UP)
newstate = get_state(pp, 1);
diff --git a/0139-RHBZ-1273173-queue-no-daemon-doc.patch b/0139-RHBZ-1273173-queue-no-daemon-doc.patch
index 4531c1d..1488b14 100644
--- a/0139-RHBZ-1273173-queue-no-daemon-doc.patch
+++ b/0139-RHBZ-1273173-queue-no-daemon-doc.patch
@@ -1,7 +1,7 @@
---
- multipath.conf.defaults | 57 +++++++++++++++++++++++++++++++++++++--------
+ multipath.conf.defaults | 56 +++++++++++++++++++++++++++++++++++++--------
multipath/multipath.conf.5 | 2 -
- 2 files changed, 48 insertions(+), 11 deletions(-)
+ 2 files changed, 48 insertions(+), 10 deletions(-)
Index: multipath-tools-130222/multipath/multipath.conf.5
===================================================================
@@ -34,7 +34,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# path_selector "service-time 0"
# path_grouping_policy "failover"
# uid_attribute "ID_SERIAL"
-@@ -12,28 +15,35 @@
+@@ -12,28 +15,36 @@
# features "0"
# path_checker "directio"
# alias_prefix "mpath"
@@ -62,9 +62,9 @@ Index: multipath-tools-130222/multipath.conf.defaults
# config_dir "/etc/multipath/conf.d"
# delay_watch_checks no
# delay_wait_checks no
--# missing_uev_msg_delay 30
+# retrigger_tries 3
+# retrigger_delay 10
+ # missing_uev_wait_timeout 30
+# new_bindings_in_boot no
#}
#blacklist {
@@ -74,7 +74,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# devnode "^dcssblk[0-9]*"
# device {
# vendor "DGC"
-@@ -68,7 +78,7 @@
+@@ -68,7 +79,7 @@
# product "Universal Xport"
# }
# device {
@@ -83,7 +83,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# product "Universal Xport"
# }
#}
-@@ -666,7 +676,7 @@
+@@ -666,7 +677,7 @@
# features "2 pg_init_retries 50"
# hardware_handler "1 rdac"
# prio "rdac"
@@ -92,7 +92,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# rr_weight "uniform"
# no_path_retry 15
# }
-@@ -679,7 +689,7 @@
+@@ -679,7 +690,7 @@
# features "2 pg_init_retries 50"
# hardware_handler "1 rdac"
# prio "rdac"
@@ -101,7 +101,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# rr_weight "uniform"
# no_path_retry 15
# }
-@@ -696,6 +706,7 @@
+@@ -696,6 +707,7 @@
# rr_min_io 128
# flush_on_last_del "yes"
# dev_loss_tmo "infinity"
@@ -109,7 +109,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# retain_attached_hw_handler yes
# detect_prio yes
# }
-@@ -876,7 +887,7 @@
+@@ -876,7 +888,7 @@
# rr_min_io_rq 1
# }
# device {
@@ -118,7 +118,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# product "INF-01-00"
# product_blacklist "Universal Xport"
# path_grouping_policy "group_by_prio"
-@@ -886,7 +897,9 @@
+@@ -886,7 +898,9 @@
# prio "rdac"
# failback immediate
# rr_weight "uniform"
@@ -129,7 +129,7 @@ Index: multipath-tools-130222/multipath.conf.defaults
# }
# device {
# vendor "STK"
-@@ -925,6 +938,30 @@
+@@ -925,6 +939,30 @@
# rr_weight "uniform"
# no_path_retry "queue"
# }
diff --git a/0150-RHBZ-1253913-fix-startup-msg.patch b/0150-RHBZ-1253913-fix-startup-msg.patch
index 7dc5125..8a020ce 100644
--- a/0150-RHBZ-1253913-fix-startup-msg.patch
+++ b/0150-RHBZ-1253913-fix-startup-msg.patch
@@ -14,7 +14,7 @@ Index: multipath-tools-130222/multipathd/main.c
static sem_t exit_sem;
/*
-@@ -1705,6 +1706,12 @@ sigusr2 (int sig)
+@@ -1718,6 +1719,12 @@ sigusr2 (int sig)
}
static void
@@ -27,7 +27,7 @@ Index: multipath-tools-130222/multipathd/main.c
signal_init(void)
{
sigset_t set;
-@@ -1807,6 +1814,9 @@ child (void * param)
+@@ -1820,6 +1827,9 @@ child (void * param)
}
running_state = DAEMON_START;
@@ -37,7 +37,7 @@ Index: multipath-tools-130222/multipathd/main.c
condlog(2, "--------start up--------");
condlog(2, "read " DEFAULT_CONFIGFILE);
-@@ -1898,8 +1908,6 @@ child (void * param)
+@@ -1911,8 +1921,6 @@ child (void * param)
}
pthread_attr_destroy(&misc_attr);
@@ -46,7 +46,7 @@ Index: multipath-tools-130222/multipathd/main.c
update_timestamp(1);
/* Ignore errors, we can live without */
-@@ -1979,7 +1987,10 @@ daemonize(void)
+@@ -1992,7 +2000,10 @@ daemonize(void)
{
int pid;
int dev_null_fd;
@@ -57,7 +57,7 @@ Index: multipath-tools-130222/multipathd/main.c
if( (pid = fork()) < 0){
fprintf(stderr, "Failed first fork : %s\n", strerror(errno));
return -1;
-@@ -1987,10 +1998,13 @@ daemonize(void)
+@@ -2000,10 +2011,13 @@ daemonize(void)
else if (pid != 0)
return pid;
@@ -72,7 +72,7 @@ Index: multipath-tools-130222/multipathd/main.c
else if (pid != 0)
_exit(0);
-@@ -2001,30 +2015,34 @@ daemonize(void)
+@@ -2014,30 +2028,34 @@ daemonize(void)
if (dev_null_fd < 0){
fprintf(stderr, "cannot open /dev/null for input & output : %s\n",
strerror(errno));
@@ -111,7 +111,7 @@ Index: multipath-tools-130222/multipathd/main.c
}
int
-@@ -2103,10 +2121,12 @@ main (int argc, char *argv[])
+@@ -2116,10 +2134,12 @@ main (int argc, char *argv[])
if (err < 0)
/* error */
exit(1);
diff --git a/0154-UPBZ-1291406-disable-reinstate.patch b/0154-UPBZ-1291406-disable-reinstate.patch
index a652e5c..2da4e3f 100644
--- a/0154-UPBZ-1291406-disable-reinstate.patch
+++ b/0154-UPBZ-1291406-disable-reinstate.patch
@@ -88,7 +88,7 @@ Index: multipath-tools-130222/multipathd/main.c
/*
* libcheckers
-@@ -1235,6 +1236,7 @@ check_path (struct vectors * vecs, struc
+@@ -1248,6 +1249,7 @@ check_path (struct vectors * vecs, struc
int newstate;
int new_path_up = 0;
int chkr_new_path_up = 0;
@@ -96,7 +96,7 @@ Index: multipath-tools-130222/multipathd/main.c
int oldchkrstate = pp->chkrstate;
if (!pp->mpp && (pp->missing_udev_info != INFO_MISSING ||
-@@ -1299,6 +1301,16 @@ check_path (struct vectors * vecs, struc
+@@ -1312,6 +1314,16 @@ check_path (struct vectors * vecs, struc
pp->wait_checks = 0;
}
@@ -113,7 +113,7 @@ Index: multipath-tools-130222/multipathd/main.c
pp->chkrstate = newstate;
if (newstate != pp->state) {
int oldstate = pp->state;
-@@ -1354,15 +1366,17 @@ check_path (struct vectors * vecs, struc
+@@ -1367,15 +1379,17 @@ check_path (struct vectors * vecs, struc
/*
* reinstate this path
*/
@@ -140,7 +140,7 @@ Index: multipath-tools-130222/multipathd/main.c
}
new_path_up = 1;
-@@ -1377,8 +1391,9 @@ check_path (struct vectors * vecs, struc
+@@ -1390,8 +1404,9 @@ check_path (struct vectors * vecs, struc
enable_group(pp);
}
else if (newstate == PATH_UP || newstate == PATH_GHOST) {
diff --git a/0156-UPBZ-1313324-dont-fail-discovery.patch b/0156-UPBZ-1313324-dont-fail-discovery.patch
index 36262f7..7e33cfe 100644
--- a/0156-UPBZ-1313324-dont-fail-discovery.patch
+++ b/0156-UPBZ-1313324-dont-fail-discovery.patch
@@ -214,7 +214,7 @@ Index: multipath-tools-130222/multipathd/main.c
===================================================================
--- multipath-tools-130222.orig/multipathd/main.c
+++ multipath-tools-130222/multipathd/main.c
-@@ -1502,7 +1502,7 @@ configure (struct vectors * vecs, int st
+@@ -1515,7 +1515,7 @@ configure (struct vectors * vecs, int st
struct multipath * mpp;
struct path * pp;
vector mpvec;
@@ -223,7 +223,7 @@ Index: multipath-tools-130222/multipathd/main.c
if (!vecs->pathvec && !(vecs->pathvec = vector_alloc()))
return 1;
-@@ -1516,7 +1516,9 @@ configure (struct vectors * vecs, int st
+@@ -1529,7 +1529,9 @@ configure (struct vectors * vecs, int st
/*
* probe for current path (from sysfs) and map (from dm) sets
*/
diff --git a/0161-RHBZ-1311659-no-kpartx.patch b/0161-RHBZ-1311659-no-kpartx.patch
index 0bce7f6..8656548 100644
--- a/0161-RHBZ-1311659-no-kpartx.patch
+++ b/0161-RHBZ-1311659-no-kpartx.patch
@@ -37,7 +37,7 @@ Index: multipath-tools-130222/libmultipath/config.c
@@ -677,6 +679,7 @@ load_config (char * file, struct udev *u
conf->retrigger_delay = DEFAULT_RETRIGGER_DELAY;
conf->new_bindings_in_boot = 0;
- conf->uev_msg_delay = DEFAULT_UEV_MSG_DELAY;
+ conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;
+ conf->skip_kpartx = DEFAULT_SKIP_KPARTX;
/*
@@ -65,7 +65,7 @@ Index: multipath-tools-130222/libmultipath/config.h
@@ -143,6 +145,7 @@ struct config {
int new_bindings_in_boot;
int delayed_reconfig;
- int uev_msg_delay;
+ int uev_wait_timeout;
+ int skip_kpartx;
unsigned int version[3];
@@ -138,7 +138,7 @@ Index: multipath-tools-130222/libmultipath/defaults.h
@@ -24,6 +24,7 @@
#define DEFAULT_RETRIGGER_DELAY 10
#define DEFAULT_RETRIGGER_TRIES 3
- #define DEFAULT_UEV_MSG_DELAY 30
+ #define DEFAULT_UEV_WAIT_TIMEOUT 30
+#define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF
#define DEFAULT_CHECKINT 5
@@ -585,9 +585,9 @@ Index: multipath-tools-130222/multipath/multipath.conf.5
===================================================================
--- multipath-tools-130222.orig/multipath/multipath.conf.5
+++ multipath-tools-130222/multipath/multipath.conf.5
-@@ -505,6 +505,12 @@ message. This warning message will print
- .I missing_uev_msg_delay
- seconds until the uevent is received. the default is
+@@ -505,6 +505,12 @@ automatically enabling device reloads. U
+ on a device until it receives a change uevent from the initial table load. The
+ default is
.I 30
+.TP
+.B skip_kpartx
diff --git a/0167-RHBZ-1335176-fix-show-cmds.patch b/0167-RHBZ-1335176-fix-show-cmds.patch
index 55154b6..cbc5817 100644
--- a/0167-RHBZ-1335176-fix-show-cmds.patch
+++ b/0167-RHBZ-1335176-fix-show-cmds.patch
@@ -128,7 +128,7 @@ Index: multipath-tools-130222/multipathd/main.c
dm_queue_if_no_path(mpp->alias, 0);
}
if (!flush_map(mpp, vecs, 1)) {
-@@ -1184,6 +1185,7 @@ retry_count_tick(vector mpvec)
+@@ -1197,6 +1198,7 @@ retry_count_tick(vector mpvec)
mpp->stat_total_queueing_time++;
condlog(4, "%s: Retrying.. No active path", mpp->alias);
if(--mpp->retry_tick == 0) {
diff --git a/0173-RH-update-man-page.patch b/0173-RH-update-man-page.patch
new file mode 100644
index 0000000..80c6dbf
--- /dev/null
+++ b/0173-RH-update-man-page.patch
@@ -0,0 +1,99 @@
+---
+ multipath/multipath.conf.5 | 56 ++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 51 insertions(+), 5 deletions(-)
+
+Index: multipath-tools-130222/multipath/multipath.conf.5
+===================================================================
+--- multipath-tools-130222.orig/multipath/multipath.conf.5
++++ multipath-tools-130222/multipath/multipath.conf.5
+@@ -240,18 +240,21 @@ Specify any device-mapper features to be
+ .I num list
+ where
+ .I num
+-is the number of features in
++is the number, between 0 and 6, of features in
+ .I list.
+-Possible values for the feature list are
++Possible values for the feature list are:
+ .RS
+ .TP 12
+-.B queue_if_no_path
++.I queue_if_no_path
+ Queue IO if no path is active; identical to the
+ .I no_path_retry
+ keyword.
+ .TP
+-.B no_partitions
+-Disable automatic partitions generation via kpartx.
++.I pg_init_retries
++Number of times to retry pg_init, it must be between 1 and 50.
++.TP
++.I pg_init_delay_msecs
++Number of msecs before pg_init retry, it must be between 0 and 60000.
+ .RE
+ .TP
+ .B path_checker
+@@ -511,6 +514,45 @@ If set to
+ .I yes
+ , kpartx will not automatically create partitions on the device. The default is
+ .I no
++.TP
++.B ignore_new_boot_devs
++If set to
++.I yes
++, multipath will never attempt to create a multipath device whose wwid is not
++listed in /etc/multipath/wwids, while running in the initramfs. This keeps
++multipath from adding new devices during the initramfs portion of bootup. The
++default is
++.I no
++.TP
++.B retrigger_tries
++This sets how many times multipathd will reissue change uevents on block
++devices that are not blacklisted, but have no wwid set by udev. Multipath
++assumes that any devices that should not report a wwid are blacklisted. This
++means that if a non-blacklisted device has no wwid, it is likely that udev
++timed out while processing it. Multipathd will wait for a while, and then
++reissue a change uevent to give udev another chance to set the wwid. The
++default is
++.I 3
++.TP
++.B retrigger_delay
++This sets how long multipathd should wait, after receiving a uevent for a
++non-blacklisted device without a wwid set by udev, before reissuing a
++change uevent. The goal of this delay is to give udev a chance to finish
++processing its current batch of uevents before sending more, to hopefully
++avoid it timing out. The default is
++.I 10
++.TP
++.B new_bindings_in_boot
++If set to
++.I yes
++, multipath will allow new user_friendly_names bindings to be created while
++running in the initramfs. Otherwise, multipath will not create
++user_friendly_names bindings while running in the initramfs. Instead, it will
++use the WWID for the name of a device that was configured to use
++user_friendly_names. When multipathd is restarted later in boot on the
++regular filesystem, the device will be renamed to a user_friendly_name. The
++default is
++.I no
+ .
+ .SH "blacklist section"
+ The
+@@ -603,6 +645,8 @@ section:
+ .TP
+ .B flush_on_last_del
+ .TP
++.B user_friendly_names
++.TP
+ .B no_path_retry
+ .TP
+ .B rr_min_io
+@@ -697,6 +741,8 @@ section:
+ .TP
+ .B no_path_retry
+ .TP
++.B user_friendly_names
++.TP
+ .B rr_min_io
+ .TP
+ .B rr_min_io_rq
diff --git a/0173-RHBZ-1239173-dont-set-flag.patch b/0173-RHBZ-1239173-dont-set-flag.patch
new file mode 100644
index 0000000..269edc8
--- /dev/null
+++ b/0173-RHBZ-1239173-dont-set-flag.patch
@@ -0,0 +1,38 @@
+---
+ libmultipath/configure.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/configure.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.c
++++ multipath-tools-130222/libmultipath/configure.c
+@@ -257,7 +257,7 @@ extern int
+ setup_map (struct multipath * mpp, char * params, int params_size)
+ {
+ struct pathgroup * pgp;
+- int i;
++ int i, old_nr_active;
+
+ /*
+ * don't bother if devmap size is unknown
+@@ -311,8 +311,12 @@ setup_map (struct multipath * mpp, char
+ if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp))
+ return 1;
+
++ old_nr_active = mpp->nr_active;
+ mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
+
++ if (mpp->nr_active && !old_nr_active)
++ mpp->force_udev_reload = 1;
++
+ /*
+ * ponders each path group and determine highest prio pg
+ * to switch over (default to first)
+@@ -445,7 +449,6 @@ select_action (struct multipath * mpp, v
+ mpp->alias);
+ return;
+ }
+- mpp->force_udev_reload = !pathcount(mpp, PATH_WILD);
+ if (cmpp->size != mpp->size) {
+ mpp->force_udev_reload = 1;
+ mpp->action = ACT_RESIZE;
diff --git a/0174-RHBZ-1362396-modprobe.patch b/0174-RHBZ-1362396-modprobe.patch
new file mode 100644
index 0000000..509510c
--- /dev/null
+++ b/0174-RHBZ-1362396-modprobe.patch
@@ -0,0 +1,16 @@
+---
+ multipathd/multipathd.init.redhat | 1 +
+ 1 file changed, 1 insertion(+)
+
+Index: multipath-tools-130222/multipathd/multipathd.init.redhat
+===================================================================
+--- multipath-tools-130222.orig/multipathd/multipathd.init.redhat
++++ multipath-tools-130222/multipathd/multipathd.init.redhat
+@@ -67,6 +67,7 @@ popd > /dev/null
+ start() {
+ test -x $DAEMON || exit 5
+ echo -n $"Starting $prog daemon: "
++ modprobe dm-multipath >/dev/null 2>&1
+ daemon $DAEMON
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch $lockdir/$prog
diff --git a/0175-RHBZ-1357382-ordering.patch b/0175-RHBZ-1357382-ordering.patch
new file mode 100644
index 0000000..c778596
--- /dev/null
+++ b/0175-RHBZ-1357382-ordering.patch
@@ -0,0 +1,17 @@
+---
+ multipathd/multipathd.service | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: multipath-tools-130222/multipathd/multipathd.service
+===================================================================
+--- multipath-tools-130222.orig/multipathd/multipathd.service
++++ multipath-tools-130222/multipathd/multipathd.service
+@@ -2,7 +2,7 @@
+ Description=Device-Mapper Multipath Device Controller
+ Wants=blk-availability.service
+ Before=iscsi.service iscsid.service lvm2-activation-early.service
+-After=syslog.target
++After=syslog.target systemd-udev-trigger.service
+ ConditionPathExists=/etc/multipath.conf
+ ConditionKernelCommandLine=!nompath
+ DefaultDependencies=no
diff --git a/0176-RHBZ-1363830-fix-rename.patch b/0176-RHBZ-1363830-fix-rename.patch
new file mode 100644
index 0000000..428b2f1
--- /dev/null
+++ b/0176-RHBZ-1363830-fix-rename.patch
@@ -0,0 +1,17 @@
+---
+ libmultipath/devmapper.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: multipath-tools-130222/libmultipath/devmapper.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/devmapper.c
++++ multipath-tools-130222/libmultipath/devmapper.c
+@@ -1387,7 +1387,7 @@ dm_rename (const char * old, char * new,
+ {
+ int r = 0;
+ struct dm_task *dmt;
+- uint32_t cookie;
++ uint32_t cookie = 0;
+ uint16_t udev_flags = ((conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0) | ((skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0);
+
+ if (dm_rename_partmaps(old, new))
diff --git a/0177-libmultipath-correctly-initialize-pp-sg_id.patch b/0177-libmultipath-correctly-initialize-pp-sg_id.patch
new file mode 100644
index 0000000..1c927be
--- /dev/null
+++ b/0177-libmultipath-correctly-initialize-pp-sg_id.patch
@@ -0,0 +1,38 @@
+From e2b87038125c79089e0bd4c6fd905667c5108740 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Tue, 9 Aug 2016 13:36:04 -0500
+Subject: [PATCH 01/11] libmultipath: correctly initialize pp->sg_id
+
+For BZ 1348372 from upstream:
+
+commit b4d9ca8dc8bbfbd3782bf4cf2cb1a440685ccd07
+Author: Hannes Reinecke <hare@suse.de>
+Date: Wed Nov 11 13:38:57 2015 +0100
+
+ libmultipath: correctly initialize pp->sg_id
+
+ The default SCSI protocol is 'SCSI_PROTOCOL_UNSPEC';
+ '0' is SCSI_PROTOCOL_FCP.
+
+ Signed-off-by: Hannes Reinecke <hare@suse.de>
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/structs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/libmultipath/structs.c b/libmultipath/structs.c
+index 30d247d..26a6a3b 100644
+--- a/libmultipath/structs.c
++++ b/libmultipath/structs.c
+@@ -94,6 +94,7 @@ alloc_path (void)
+ pp->sg_id.channel = -1;
+ pp->sg_id.scsi_id = -1;
+ pp->sg_id.lun = -1;
++ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
+ pp->fd = -1;
+ pp->priority = PRIO_UNDEF;
+ }
+--
+1.8.3.1
+
diff --git a/0178-libmultipath-add-rbd-discovery.patch b/0178-libmultipath-add-rbd-discovery.patch
new file mode 100644
index 0000000..6930e46
--- /dev/null
+++ b/0178-libmultipath-add-rbd-discovery.patch
@@ -0,0 +1,219 @@
+From 2fc494b81157059e0be66022f6a2110f1ce179c3 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Tue, 9 Aug 2016 13:44:10 -0500
+Subject: [PATCH 02/11] libmultipath: add rbd discovery
+
+For BZ 1348372 from upstream commit:
+
+Commit 152f3f803ee922075e8b25027eb9dc5699f1aefa
+Author: Mike Christie <mchristi@redhat.com>
+Date: Mon Aug 8 07:01:47 2016 -0500
+
+ libmultipath: add rbd discovery
+
+ rbd is a block device interface for Ceph. It does not support
+ any SCSI commands, so this patch adds bus detection and virtual
+ vendor/product pathinfo.
+
+--------
+
+Porting notes:
+
+get_uid() chunk does not match upstream due to rhel not having
+the get uid callout code and sysfs uid detection code.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers.h | 1 +
+ libmultipath/discovery.c | 116 ++++++++++++++++++++++++++++++++++++++++-------
+ libmultipath/structs.h | 1 +
+ 3 files changed, 101 insertions(+), 17 deletions(-)
+
+diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h
+index f6fe326..735bb25 100644
+--- a/libmultipath/checkers.h
++++ b/libmultipath/checkers.h
+@@ -75,6 +75,7 @@ enum path_check_state {
+ #define EMC_CLARIION "emc_clariion"
+ #define READSECTOR0 "readsector0"
+ #define CCISS_TUR "cciss_tur"
++#define RBD "rbd"
+
+ #define DEFAULT_CHECKER DIRECTIO
+
+diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
+index 7a8282b..1b9f390 100644
+--- a/libmultipath/discovery.c
++++ b/libmultipath/discovery.c
+@@ -781,6 +781,21 @@ scsi_sysfs_pathinfo (struct path * pp)
+ }
+
+ static int
++rbd_sysfs_pathinfo (struct path * pp)
++{
++ sprintf(pp->vendor_id, "Ceph");
++ sprintf(pp->product_id, "RBD");
++
++ condlog(3, "%s: vendor = %s product = %s", pp->dev, pp->vendor_id,
++ pp->product_id);
++ /*
++ * set the hwe configlet pointer
++ */
++ pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, NULL);
++ return 0;
++}
++
++static int
+ ccw_sysfs_pathinfo (struct path * pp)
+ {
+ struct udev_device *parent;
+@@ -974,6 +989,8 @@ sysfs_pathinfo(struct path * pp)
+ pp->bus = SYSFS_BUS_CCW;
+ if (!strncmp(pp->dev,"sd", 2))
+ pp->bus = SYSFS_BUS_SCSI;
++ if (!strncmp(pp->dev,"rbd", 3))
++ pp->bus = SYSFS_BUS_RBD;
+
+ if (pp->bus == SYSFS_BUS_UNDEF)
+ return 0;
+@@ -986,6 +1003,9 @@ sysfs_pathinfo(struct path * pp)
+ } else if (pp->bus == SYSFS_BUS_CCISS) {
+ if (cciss_sysfs_pathinfo(pp))
+ return 1;
++ } else if (pp->bus == SYSFS_BUS_RBD) {
++ if (rbd_sysfs_pathinfo(pp))
++ return 1;
+ }
+ return 0;
+ }
+@@ -1087,10 +1107,60 @@ get_prio (struct path * pp)
+ }
+
+ static int
++get_rbd_uid(struct path * pp)
++{
++ struct udev_device *rbd_bus_dev;
++ int ret, rbd_bus_id;
++ const char *pool, *image, *snap;
++ char sysfs_path[PATH_SIZE];
++ uint64_t snap_id, max_snap_id = -3;
++
++ ret = sscanf(pp->dev, "rbd%d", &rbd_bus_id);
++ if (ret != 1)
++ return -EINVAL;
++
++ snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/devices/%d",
++ rbd_bus_id);
++ rbd_bus_dev = udev_device_new_from_syspath(conf->udev, sysfs_path);
++ if (!rbd_bus_dev)
++ return -ENODEV;
++
++ ret = -EINVAL;
++ pool = udev_device_get_sysattr_value(rbd_bus_dev, "pool_id");
++ if (!pool)
++ goto free_dev;
++
++ image = udev_device_get_sysattr_value(rbd_bus_dev, "image_id");
++ if (!image)
++ goto free_dev;
++
++ snap = udev_device_get_sysattr_value(rbd_bus_dev, "snap_id");
++ if (!snap)
++ goto free_dev;
++ snap_id = strtoull(snap, NULL, 19);
++ if (snap_id >= max_snap_id)
++ ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s", pool, image);
++ else
++ ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s-%s", pool,
++ image, snap);
++ if (ret < WWID_SIZE) {
++ ret = 0;
++ } else {
++ condlog(0, "%s: wwid overflow", pp->dev);
++ ret = -EOVERFLOW;
++ }
++
++free_dev:
++ udev_device_unref(rbd_bus_dev);
++ return ret;
++}
++
++static int
+ get_uid (struct path * pp)
+ {
+ char *c;
+ const char *value;
++ int ret;
+
+ if (!pp->uid_attribute)
+ select_getuid(pp);
+@@ -1101,25 +1171,37 @@ get_uid (struct path * pp)
+ }
+
+ memset(pp->wwid, 0, WWID_SIZE);
+- value = udev_device_get_property_value(pp->udev, pp->uid_attribute);
+- if ((!value || strlen(value) == 0) && conf->cmd == CMD_VALID_PATH)
+- value = getenv(pp->uid_attribute);
+- if (value && strlen(value)) {
+- size_t len = WWID_SIZE;
+-
+- if (strlen(value) + 1 > WWID_SIZE) {
+- condlog(0, "%s: wwid overflow", pp->dev);
+- } else {
+- len = strlen(value);
++ if (pp->bus == SYSFS_BUS_RBD) {
++ ret = get_rbd_uid(pp);
++ if (ret) {
++ condlog(1, "%s: failed to get sysfs uid: %s",
++ pp->dev, strerror(-ret));
++ pp->missing_udev_info = INFO_MISSING;
++ pp->tick = conf->retrigger_delay;
+ }
+- strncpy(pp->wwid, value, len);
+- pp->missing_udev_info = INFO_OK;
+- pp->tick = 0;
+ } else {
+- condlog(3, "%s: no %s attribute", pp->dev,
+- pp->uid_attribute);
+- pp->missing_udev_info = INFO_MISSING;
+- pp->tick = conf->retrigger_delay;
++ value = udev_device_get_property_value(pp->udev,
++ pp->uid_attribute);
++ if ((!value || strlen(value) == 0) &&
++ conf->cmd == CMD_VALID_PATH)
++ value = getenv(pp->uid_attribute);
++ if (value && strlen(value)) {
++ size_t len = WWID_SIZE;
++
++ if (strlen(value) + 1 > WWID_SIZE) {
++ condlog(0, "%s: wwid overflow", pp->dev);
++ } else {
++ len = strlen(value);
++ }
++ strncpy(pp->wwid, value, len);
++ pp->missing_udev_info = INFO_OK;
++ pp->tick = 0;
++ } else {
++ condlog(3, "%s: no %s attribute", pp->dev,
++ pp->uid_attribute);
++ pp->missing_udev_info = INFO_MISSING;
++ pp->tick = conf->retrigger_delay;
++ }
+ }
+
+ /* Strip any trailing blanks */
+diff --git a/libmultipath/structs.h b/libmultipath/structs.h
+index b5b4567..e566462 100644
+--- a/libmultipath/structs.h
++++ b/libmultipath/structs.h
+@@ -52,6 +52,7 @@ enum sysfs_buses {
+ SYSFS_BUS_IDE,
+ SYSFS_BUS_CCW,
+ SYSFS_BUS_CCISS,
++ SYSFS_BUS_RBD,
+ };
+
+ enum pathstates {
+--
+1.8.3.1
+
diff --git a/0179-multipath-tools-add-checker-callout-to-repair-path.patch b/0179-multipath-tools-add-checker-callout-to-repair-path.patch
new file mode 100644
index 0000000..8b48f7d
--- /dev/null
+++ b/0179-multipath-tools-add-checker-callout-to-repair-path.patch
@@ -0,0 +1,250 @@
+From 1073621a7a63ca4e9a00baedd8edc51e5381eb95 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Tue, 9 Aug 2016 13:46:11 -0500
+Subject: [PATCH 03/11] multipath-tools: add checker callout to repair path
+
+For BZ 1348372 from upstream commit:
+
+commit 015f87b16a7797a17afd514aec46e65c2a1a2f73
+Author: Mike Christie <mchristi@redhat.com>
+Date: Mon Aug 8 07:01:48 2016 -0500
+
+ multipath-tools: add checker callout to repair path
+
+ This patch adds a callback which can be used to repair a path
+ if check() has determined it is in the PATH_DOWN state.
+
+ The next patch that adds rbd checker support which will use this to
+ handle the case where a rbd device is blacklisted.
+
+--------
+
+Porting notes:
+checkerloop difference due to different path tracking.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers.c | 23 +++++++++++++++++++++++
+ libmultipath/checkers.h | 4 ++++
+ libmultipath/checkers/cciss_tur.c | 5 +++++
+ libmultipath/checkers/directio.c | 5 +++++
+ libmultipath/checkers/emc_clariion.c | 5 +++++
+ libmultipath/checkers/hp_sw.c | 5 +++++
+ libmultipath/checkers/rdac.c | 5 +++++
+ libmultipath/checkers/readsector0.c | 5 +++++
+ libmultipath/checkers/tur.c | 5 +++++
+ multipathd/main.c | 9 +++++++++
+ 10 files changed, 71 insertions(+)
+
+diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c
+index 7f9db2d..fa7d8b7 100644
+--- a/libmultipath/checkers.c
++++ b/libmultipath/checkers.c
+@@ -137,6 +137,14 @@ struct checker * add_checker (char * name)
+ if (!c->free)
+ goto out;
+
++ c->repair = (void (*)(struct checker *)) dlsym(c->handle,
++ "libcheck_repair");
++ errstr = dlerror();
++ if (errstr != NULL)
++ condlog(0, "A dynamic linking error occurred: (%s)", errstr);
++ if (!c->repair)
++ goto out;
++
+ c->fd = 0;
+ c->sync = 1;
+ list_add(&c->node, &checkers);
+@@ -202,6 +210,20 @@ void checker_put (struct checker * dst)
+ free_checker(src);
+ }
+
++void checker_repair (struct checker * c)
++{
++ if (!c)
++ return;
++
++ c->message[0] = '\0';
++ if (c->disable) {
++ MSG(c, "checker disabled");
++ return;
++ }
++
++ c->repair(c);
++}
++
+ int checker_check (struct checker * c)
+ {
+ int r;
+@@ -266,6 +288,7 @@ void checker_get (struct checker * dst, char * name)
+ dst->sync = src->sync;
+ strncpy(dst->name, src->name, CHECKER_NAME_LEN);
+ strncpy(dst->message, src->message, CHECKER_MSG_LEN);
++ dst->repair = src->repair;
+ dst->check = src->check;
+ dst->init = src->init;
+ dst->free = src->free;
+diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h
+index 735bb25..ad3b9e4 100644
+--- a/libmultipath/checkers.h
++++ b/libmultipath/checkers.h
+@@ -106,6 +106,9 @@ struct checker {
+ multipath-wide. Use MALLOC if
+ you want to stuff data in. */
+ int (*check)(struct checker *);
++ void (*repair)(struct checker *); /* called if check returns
++ PATH_DOWN to bring path into
++ usable state */
+ int (*init)(struct checker *); /* to allocate the context */
+ void (*free)(struct checker *); /* to free the context */
+ };
+@@ -125,6 +128,7 @@ void checker_set_async (struct checker *);
+ void checker_set_fd (struct checker *, int);
+ void checker_enable (struct checker *);
+ void checker_disable (struct checker *);
++void checker_repair (struct checker *);
+ int checker_check (struct checker *);
+ int checker_selected (struct checker *);
+ char * checker_name (struct checker *);
+diff --git a/libmultipath/checkers/cciss_tur.c b/libmultipath/checkers/cciss_tur.c
+index 4c26901..7e4eb81 100644
+--- a/libmultipath/checkers/cciss_tur.c
++++ b/libmultipath/checkers/cciss_tur.c
+@@ -63,6 +63,11 @@ void libcheck_free (struct checker * c)
+ return;
+ }
+
++void libcheck_repair (struct checker * c)
++{
++ return;
++}
++
+ extern int
+ libcheck_check (struct checker * c)
+ {
+diff --git a/libmultipath/checkers/directio.c b/libmultipath/checkers/directio.c
+index 46fe6a7..1a997ed 100644
+--- a/libmultipath/checkers/directio.c
++++ b/libmultipath/checkers/directio.c
+@@ -116,6 +116,11 @@ void libcheck_free (struct checker * c)
+ free(ct);
+ }
+
++void libcheck_repair (struct checker * c)
++{
++ return;
++}
++
+ static int
+ check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
+ {
+diff --git a/libmultipath/checkers/emc_clariion.c b/libmultipath/checkers/emc_clariion.c
+index b42d267..43b5025 100644
+--- a/libmultipath/checkers/emc_clariion.c
++++ b/libmultipath/checkers/emc_clariion.c
+@@ -90,6 +90,11 @@ void libcheck_free (struct checker * c)
+ free(c->context);
+ }
+
++void libcheck_repair (struct checker * c)
++{
++ return;
++}
++
+ int libcheck_check (struct checker * c)
+ {
+ unsigned char sense_buffer[128] = { 0, };
+diff --git a/libmultipath/checkers/hp_sw.c b/libmultipath/checkers/hp_sw.c
+index b50ac0c..857ac5e 100644
+--- a/libmultipath/checkers/hp_sw.c
++++ b/libmultipath/checkers/hp_sw.c
+@@ -44,6 +44,11 @@ void libcheck_free (struct checker * c)
+ return;
+ }
+
++void libcheck_repair (struct checker * c)
++{
++ return;
++}
++
+ static int
+ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+ void *resp, int mx_resp_len, int noisy, unsigned int timeout)
+diff --git a/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c
+index f0e0af3..5469e61 100644
+--- a/libmultipath/checkers/rdac.c
++++ b/libmultipath/checkers/rdac.c
+@@ -139,6 +139,11 @@ void libcheck_free (struct checker * c)
+ return;
+ }
+
++void libcheck_repair (struct checker * c)
++{
++ return;
++}
++
+ static int
+ do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len,
+ unsigned int timeout)
+diff --git a/libmultipath/checkers/readsector0.c b/libmultipath/checkers/readsector0.c
+index 0550fb6..b3ed1f3 100644
+--- a/libmultipath/checkers/readsector0.c
++++ b/libmultipath/checkers/readsector0.c
+@@ -23,6 +23,11 @@ void libcheck_free (struct checker * c)
+ return;
+ }
+
++void libcheck_repair (struct checker * c)
++{
++ return;
++}
++
+ int libcheck_check (struct checker * c)
+ {
+ unsigned char buf[4096];
+diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c
+index 1e5b039..91f1458 100644
+--- a/libmultipath/checkers/tur.c
++++ b/libmultipath/checkers/tur.c
+@@ -187,6 +187,11 @@ void libcheck_free (struct checker * c)
+ return;
+ }
+
++void libcheck_repair (struct checker * c)
++{
++ return;
++}
++
+ #define TUR_MSG(msg, fmt, args...) snprintf(msg, CHECKER_MSG_LEN, fmt, ##args);
+
+ int
+diff --git a/multipathd/main.c b/multipathd/main.c
+index 8808c88..d26fd22 100644
+--- a/multipathd/main.c
++++ b/multipathd/main.c
+@@ -1455,6 +1455,14 @@ check_path (struct vectors * vecs, struct path * pp)
+ }
+ }
+
++void repair_path(struct vectors * vecs, struct path * pp)
++{
++ if (pp->state != PATH_DOWN)
++ return;
++
++ checker_repair(&pp->checker);
++}
++
+ static void *
+ checkerloop (void *ap)
+ {
+@@ -1483,6 +1491,7 @@ checkerloop (void *ap)
+ if (vecs->pathvec) {
+ vector_foreach_slot (vecs->pathvec, pp, i) {
+ check_path(vecs, pp);
++ repair_path(vecs, pp);
+ }
+ }
+ if (vecs->mpvec) {
+--
+1.8.3.1
+
diff --git a/0180-multipath-tools-Add-rbd-checker.patch b/0180-multipath-tools-Add-rbd-checker.patch
new file mode 100644
index 0000000..cf8a752
--- /dev/null
+++ b/0180-multipath-tools-Add-rbd-checker.patch
@@ -0,0 +1,746 @@
+From e28c340ed961409700d46a1cb9a820a8b7a4d016 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Thu, 11 Aug 2016 02:12:12 -0500
+Subject: [PATCH 04/11] multipath-tools: Add rbd checker.
+
+For BZ 1348372 from upstream commit:
+
+commit d1cad5649b6fcf9027d43ca0405c900080133e32
+Author: Mike Christie <mchristi@redhat.com>
+Date: Mon Aug 8 07:01:49 2016 -0500
+
+ multipath-tools: Add rbd checker.
+
+ This checker currently only handles the case where a path is failed
+ due to it being blacklisted by the ceph cluster. The specific use
+ case for me is when LIO exports rbd images through multiple LIO
+ instances.
+
+ The problem it handles is when rbd instance1 has the exclusive lock,
+ but becomes unreachable another host in the cluster will take over
+ and blacklist the instance1. This prevents it from sending stale IO
+ and corrupting data.
+
+ Later, when the host is reachable, we will want to failback to it.
+ To this, the checker will detect we were blacklisted, unmap the old
+ image which will make sure old IO is failed, and then remap the
+image
+ and unblacklist the host. multipathd will then handle this like a
+ path being removed and re-added.
+
+--------
+
+Porting notes:
+Added rbd to multipath.conf.annotated.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers/Makefile | 7
+ libmultipath/checkers/rbd.c | 639 +++++++++++++++++++++++++++++++++++++++++
+ multipath.conf.annotated | 4
+ multipath/multipath.conf.5 | 3
+ 4 files changed, 651 insertions(+), 2 deletions(-)
+ create mode 100644 libmultipath/checkers/rbd.c
+
+Index: multipath-tools-130222/libmultipath/checkers/Makefile
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/checkers/Makefile
++++ multipath-tools-130222/libmultipath/checkers/Makefile
+@@ -14,10 +14,17 @@ LIBS= \
+ libcheckhp_sw.so \
+ libcheckrdac.so
+
++ifeq ($(shell test -r /usr/include/rados/librados.h && echo 1),1)
++LIBS += libcheckrbd.so
++endif
++
+ CFLAGS += -fPIC -I..
+
+ all: $(LIBS)
+
++libcheckrbd.so: rbd.o
++ $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -lrados -ludev
++
+ libcheckdirectio.so: libsg.o directio.o
+ $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -laio
+
+Index: multipath-tools-130222/libmultipath/checkers/rbd.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libmultipath/checkers/rbd.c
+@@ -0,0 +1,639 @@
++/*
++ * Copyright (c) 2016 Red Hat
++ * Copyright (c) 2004 Christophe Varoqui
++ *
++ * Code based off of tur.c and ceph's krbd.cc
++ */
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <pthread.h>
++#include <libudev.h>
++#include <ifaddrs.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/ioctl.h>
++#include <sys/time.h>
++#include <sys/wait.h>
++
++#include "rados/librados.h"
++
++#include "structs.h"
++#include "checkers.h"
++
++#include "../libmultipath/debug.h"
++#include "../libmultipath/uevent.h"
++
++struct rbd_checker_context;
++typedef int (thread_fn)(struct rbd_checker_context *ct, char *msg);
++
++#define RBD_MSG(msg, fmt, args...) snprintf(msg, CHECKER_MSG_LEN, fmt, ##args);
++
++struct rbd_checker_context {
++ int rbd_bus_id;
++ char *client_addr;
++ char *config_info;
++ char *snap;
++ char *pool;
++ char *image;
++ char *username;
++ int remapped;
++ int blacklisted;
++
++ rados_t cluster;
++
++ int state;
++ int running;
++ time_t time;
++ thread_fn *fn;
++ pthread_t thread;
++ pthread_mutex_t lock;
++ pthread_cond_t active;
++ pthread_spinlock_t hldr_lock;
++ int holders;
++ char message[CHECKER_MSG_LEN];
++};
++
++int libcheck_init(struct checker * c)
++{
++ struct rbd_checker_context *ct;
++ struct udev_device *block_dev;
++ struct udev_device *bus_dev;
++ struct udev *udev;
++ struct stat sb;
++ const char *block_name, *addr, *config_info;
++ const char *image, *pool, *snap, *username;
++ char sysfs_path[PATH_SIZE];
++ int ret;
++
++ ct = malloc(sizeof(struct rbd_checker_context));
++ if (!ct)
++ return 1;
++ memset(ct, 0, sizeof(struct rbd_checker_context));
++ ct->holders = 1;
++ pthread_cond_init(&ct->active, NULL);
++ pthread_mutex_init(&ct->lock, NULL);
++ pthread_spin_init(&ct->hldr_lock, PTHREAD_PROCESS_PRIVATE);
++ c->context = ct;
++
++ /*
++ * The rbd block layer sysfs device is not linked to the rbd bus
++ * device that we interact with, so figure that out now.
++ */
++ if (fstat(c->fd, &sb) != 0)
++ goto free_ct;
++
++ udev = udev_new();
++ if (!udev)
++ goto free_ct;
++
++ block_dev = udev_device_new_from_devnum(udev, 'b', sb.st_rdev);
++ if (!block_dev)
++ goto free_udev;
++
++ block_name = udev_device_get_sysname(block_dev);
++ ret = sscanf(block_name, "rbd%d", &ct->rbd_bus_id);
++
++ udev_device_unref(block_dev);
++ if (ret != 1)
++ goto free_udev;
++
++ snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/devices/%d",
++ ct->rbd_bus_id);
++ bus_dev = udev_device_new_from_syspath(udev, sysfs_path);
++ if (!bus_dev)
++ goto free_udev;
++
++ addr = udev_device_get_sysattr_value(bus_dev, "client_addr");
++ if (!addr) {
++ condlog(0, "Could not find client_addr in rbd sysfs. Try "
++ "updating kernel");
++ goto free_dev;
++ }
++
++ ct->client_addr = strdup(addr);
++ if (!ct->client_addr)
++ goto free_dev;
++
++ config_info = udev_device_get_sysattr_value(bus_dev, "config_info");
++ if (!config_info)
++ goto free_addr;
++
++ ct->config_info = strdup(config_info);
++ if (!ct->config_info)
++ goto free_addr;
++
++ username = strstr(config_info, "name=");
++ if (username) {
++ char *end;
++ int len;
++
++ username += 5;
++ end = strchr(username, ',');
++ if (!end)
++ goto free_info;
++ len = end - username;
++
++ ct->username = malloc(len + 1);
++ if (!ct->username)
++ goto free_info;
++ strncpy(ct->username, username, len);
++ ct->username[len] = '\0';
++ }
++
++ image = udev_device_get_sysattr_value(bus_dev, "name");
++ if (!image)
++ goto free_username;
++
++ ct->image = strdup(image);
++ if (!ct->image)
++ goto free_info;
++
++ pool = udev_device_get_sysattr_value(bus_dev, "pool");
++ if (!pool)
++ goto free_image;
++
++ ct->pool = strdup(pool);
++ if (!ct->pool)
++ goto free_image;
++
++ snap = udev_device_get_sysattr_value(bus_dev, "current_snap");
++ if (!snap)
++ goto free_pool;
++
++ if (strcmp("-", snap)) {
++ ct->snap = strdup(snap);
++ if (!ct->snap)
++ goto free_pool;
++ }
++
++ if (rados_create(&ct->cluster, NULL) < 0) {
++ condlog(0, "Could not create rados cluster");
++ goto free_snap;
++ }
++
++ if (rados_conf_read_file(ct->cluster, NULL) < 0) {
++ condlog(0, "Could not read rados conf");
++ goto shutdown_rados;
++ }
++
++ ret = rados_connect(ct->cluster);
++ if (ret < 0) {
++ condlog(0, "Could not connect to rados cluster");
++ goto shutdown_rados;
++ }
++
++ udev_device_unref(bus_dev);
++ udev_unref(udev);
++
++ condlog(3, "rbd%d checker init %s %s/%s@%s %s", ct->rbd_bus_id,
++ ct->client_addr, ct->pool, ct->image, ct->snap ? ct->snap : "-",
++ ct->username ? ct->username : "none");
++ return 0;
++
++shutdown_rados:
++ rados_shutdown(ct->cluster);
++free_snap:
++ if (ct->snap)
++ free(ct->snap);
++free_pool:
++ free(ct->pool);
++free_image:
++ free(ct->image);
++free_username:
++ if (ct->username)
++ free(ct->username);
++free_info:
++ free(ct->config_info);
++free_addr:
++ free(ct->client_addr);
++free_dev:
++ udev_device_unref(bus_dev);
++free_udev:
++ udev_unref(udev);
++free_ct:
++ free(ct);
++ return 1;
++}
++
++void cleanup_context(struct rbd_checker_context *ct)
++{
++ pthread_mutex_destroy(&ct->lock);
++ pthread_cond_destroy(&ct->active);
++ pthread_spin_destroy(&ct->hldr_lock);
++
++ rados_shutdown(ct->cluster);
++
++ if (ct->username)
++ free(ct->username);
++ if (ct->snap)
++ free(ct->snap);
++ free(ct->pool);
++ free(ct->image);
++ free(ct->config_info);
++ free(ct->client_addr);
++ free(ct);
++}
++
++void libcheck_free(struct checker * c)
++{
++ if (c->context) {
++ struct rbd_checker_context *ct = c->context;
++ int holders;
++ pthread_t thread;
++
++ pthread_spin_lock(&ct->hldr_lock);
++ ct->holders--;
++ holders = ct->holders;
++ thread = ct->thread;
++ pthread_spin_unlock(&ct->hldr_lock);
++ if (holders)
++ pthread_cancel(thread);
++ else
++ cleanup_context(ct);
++ c->context = NULL;
++ }
++}
++
++static int rbd_is_blacklisted(struct rbd_checker_context *ct, char *msg)
++{
++ char *addr_tok, *start, *save;
++ char *cmd[2];
++ char *blklist, *stat;
++ size_t blklist_len, stat_len;
++ int ret;
++ char *end;
++
++ cmd[0] = "{\"prefix\": \"osd blacklist ls\"}";
++ cmd[1] = NULL;
++
++ ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0,
++ &blklist, &blklist_len, &stat, &stat_len);
++ if (ret < 0) {
++ RBD_MSG(msg, "rbd checker failed: mon command failed %d",
++ ret);
++ return ret;
++ }
++
++ if (!blklist || !blklist_len)
++ goto free_bufs;
++
++ /*
++ * parse list of addrs with the format
++ * ipv4:port/nonce date time\n
++ * or
++ * [ipv6]:port/nonce date time\n
++ */
++ ret = 0;
++ for (start = blklist; ; start = NULL) {
++ addr_tok = strtok_r(start, "\n", &save);
++ if (!addr_tok || !strlen(addr_tok))
++ break;
++
++ end = strchr(addr_tok, ' ');
++ if (!end) {
++ RBD_MSG(msg, "rbd%d checker failed: invalid blacklist %s",
++ ct->rbd_bus_id, addr_tok);
++ break;
++ }
++ *end = '\0';
++
++ if (!strcmp(addr_tok, ct->client_addr)) {
++ ct->blacklisted = 1;
++ RBD_MSG(msg, "rbd%d checker: %s is blacklisted",
++ ct->rbd_bus_id, ct->client_addr);
++ ret = 1;
++ break;
++ }
++ }
++
++free_bufs:
++ rados_buffer_free(blklist);
++ rados_buffer_free(stat);
++ return ret;
++}
++
++int rbd_check(struct rbd_checker_context *ct, char *msg)
++{
++ if (ct->blacklisted || rbd_is_blacklisted(ct, msg) == 1)
++ return PATH_DOWN;
++
++ RBD_MSG(msg, "rbd checker reports path is up");
++ /*
++ * Path may have issues, but the ceph cluster is at least
++ * accepting IO, so we can attempt to do IO.
++ *
++ * TODO: in future versions, we can run other tests to
++ * verify OSDs and networks.
++ */
++ return PATH_UP;
++}
++
++int safe_write(int fd, const void *buf, size_t count)
++{
++ while (count > 0) {
++ ssize_t r = write(fd, buf, count);
++ if (r < 0) {
++ if (errno == EINTR)
++ continue;
++ return -errno;
++ }
++ count -= r;
++ buf = (char *)buf + r;
++ }
++ return 0;
++}
++
++static int sysfs_write_rbd_bus(const char *which, const char *buf,
++ size_t buf_len)
++{
++ char sysfs_path[PATH_SIZE];
++ int fd;
++ int r;
++
++ /* we require newer kernels so single_major should alwayws be there */
++ snprintf(sysfs_path, sizeof(sysfs_path),
++ "/sys/bus/rbd/%s_single_major", which);
++ fd = open(sysfs_path, O_WRONLY);
++ if (fd < 0)
++ return -errno;
++
++ r = safe_write(fd, buf, buf_len);
++ close(fd);
++ return r;
++}
++
++static int rbd_remap(struct rbd_checker_context *ct)
++{
++ char *argv[11];
++ pid_t pid;
++ int ret = 0, i = 0;
++ int status;
++
++ pid = fork();
++ switch (pid) {
++ case 0:
++ argv[i++] = "rbd";
++ argv[i++] = "map";
++ argv[i++] = "-o noshare";
++ if (ct->username) {
++ argv[i++] = "--id";
++ argv[i++] = ct->username;
++ }
++ argv[i++] = "--pool";
++ argv[i++] = ct->pool;
++ if (ct->snap) {
++ argv[i++] = "--snap";
++ argv[i++] = ct->snap;
++ }
++ argv[i++] = ct->image;
++ argv[i] = NULL;
++
++ ret = execvp(argv[0], argv);
++ condlog(0, "Error executing rbd: %s", strerror(errno));
++ exit(-1);
++ case -1:
++ condlog(0, "fork failed: %s", strerror(errno));
++ return -1;
++ default:
++ ret = -1;
++ wait(&status);
++ if (WIFEXITED(status)) {
++ status = WEXITSTATUS(status);
++ if (status == 0)
++ ret = 0;
++ else
++ condlog(0, "rbd failed with %d", status);
++ }
++ }
++
++ return ret;
++}
++
++static int sysfs_write_rbd_remove(const char *buf, int buf_len)
++{
++ return sysfs_write_rbd_bus("remove", buf, buf_len);
++}
++
++static int rbd_rm_blacklist(struct rbd_checker_context *ct)
++{
++ char *cmd[2];
++ char *stat, *cmd_str;
++ size_t stat_len;
++ int ret;
++
++ ret = asprintf(&cmd_str, "{\"prefix\": \"osd blacklist\", \"blacklistop\": \"rm\", \"addr\": \"%s\"}",
++ ct->client_addr);
++ if (ret == -1)
++ return -ENOMEM;
++
++ cmd[0] = cmd_str;
++ cmd[1] = NULL;
++
++ ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0,
++ NULL, 0, &stat, &stat_len);
++ if (ret < 0) {
++ condlog(1, "rbd%d repair failed to remove blacklist for %s %d",
++ ct->rbd_bus_id, ct->client_addr, ret);
++ goto free_cmd;
++ }
++
++ condlog(1, "rbd%d repair rm blacklist for %s",
++ ct->rbd_bus_id, ct->client_addr);
++ free(stat);
++free_cmd:
++ free(cmd_str);
++ return ret;
++}
++
++static int rbd_repair(struct rbd_checker_context *ct, char *msg)
++{
++ char del[17];
++ int ret;
++
++ if (!ct->blacklisted)
++ return PATH_UP;
++
++ if (!ct->remapped) {
++ ret = rbd_remap(ct);
++ if (ret) {
++ RBD_MSG(msg, "rbd%d repair failed to remap. Err %d",
++ ct->rbd_bus_id, ret);
++ return PATH_DOWN;
++ }
++ }
++ ct->remapped = 1;
++
++ snprintf(del, sizeof(del), "%d force", ct->rbd_bus_id);
++ ret = sysfs_write_rbd_remove(del, strlen(del) + 1);
++ if (ret) {
++ RBD_MSG(msg, "rbd%d repair failed to clean up. Err %d",
++ ct->rbd_bus_id, ret);
++ return PATH_DOWN;
++ }
++
++ ret = rbd_rm_blacklist(ct);
++ if (ret) {
++ RBD_MSG(msg, "rbd%d repair could not remove blacklist entry. Err %d",
++ ct->rbd_bus_id, ret);
++ return PATH_DOWN;
++ }
++
++ ct->remapped = 0;
++ ct->blacklisted = 0;
++
++ RBD_MSG(msg, "rbd%d has been repaired", ct->rbd_bus_id);
++ return PATH_UP;
++}
++
++#define rbd_thread_cleanup_push(ct) pthread_cleanup_push(cleanup_func, ct)
++#define rbd_thread_cleanup_pop(ct) pthread_cleanup_pop(1)
++
++void cleanup_func(void *data)
++{
++ int holders;
++ struct rbd_checker_context *ct = data;
++ pthread_spin_lock(&ct->hldr_lock);
++ ct->holders--;
++ holders = ct->holders;
++ ct->thread = 0;
++ pthread_spin_unlock(&ct->hldr_lock);
++ if (!holders)
++ cleanup_context(ct);
++}
++
++void *rbd_thread(void *ctx)
++{
++ struct rbd_checker_context *ct = ctx;
++ int state;
++
++ condlog(3, "rbd%d thread starting up", ct->rbd_bus_id);
++
++ ct->message[0] = '\0';
++ /* This thread can be canceled, so setup clean up */
++ rbd_thread_cleanup_push(ct)
++
++ /* checker start up */
++ pthread_mutex_lock(&ct->lock);
++ ct->state = PATH_PENDING;
++ pthread_mutex_unlock(&ct->lock);
++
++ state = ct->fn(ct, ct->message);
++
++ /* checker done */
++ pthread_mutex_lock(&ct->lock);
++ ct->state = state;
++ pthread_mutex_unlock(&ct->lock);
++ pthread_cond_signal(&ct->active);
++
++ condlog(3, "rbd%d thead finished, state %s", ct->rbd_bus_id,
++ checker_state_name(state));
++ rbd_thread_cleanup_pop(ct);
++ return ((void *)0);
++}
++
++static void rbd_timeout(struct timespec *tsp)
++{
++ struct timeval now;
++
++ gettimeofday(&now, NULL);
++ tsp->tv_sec = now.tv_sec;
++ tsp->tv_nsec = now.tv_usec * 1000;
++ tsp->tv_nsec += 1000000; /* 1 millisecond */
++}
++
++static int rbd_exec_fn(struct checker *c, thread_fn *fn)
++{
++ struct rbd_checker_context *ct = c->context;
++ struct timespec tsp;
++ pthread_attr_t attr;
++ int rbd_status, r;
++
++ if (c->sync)
++ return rbd_check(ct, c->message);
++ /*
++ * Async mode
++ */
++ r = pthread_mutex_lock(&ct->lock);
++ if (r != 0) {
++ condlog(2, "rbd%d mutex lock failed with %d", ct->rbd_bus_id,
++ r);
++ MSG(c, "rbd%d thread failed to initialize", ct->rbd_bus_id);
++ return PATH_WILD;
++ }
++
++ if (ct->running) {
++ /* Check if checker is still running */
++ if (ct->thread) {
++ condlog(3, "rbd%d thread not finished", ct->rbd_bus_id);
++ rbd_status = PATH_PENDING;
++ } else {
++ /* checker done */
++ ct->running = 0;
++ rbd_status = ct->state;
++ strncpy(c->message, ct->message, CHECKER_MSG_LEN);
++ c->message[CHECKER_MSG_LEN - 1] = '\0';
++ }
++ pthread_mutex_unlock(&ct->lock);
++ } else {
++ /* Start new checker */
++ ct->state = PATH_UNCHECKED;
++ ct->fn = fn;
++ pthread_spin_lock(&ct->hldr_lock);
++ ct->holders++;
++ pthread_spin_unlock(&ct->hldr_lock);
++ setup_thread_attr(&attr, 32 * 1024, 1);
++ r = pthread_create(&ct->thread, &attr, rbd_thread, ct);
++ if (r) {
++ pthread_mutex_unlock(&ct->lock);
++ ct->thread = 0;
++ ct->holders--;
++ condlog(3, "rbd%d failed to start rbd thread, using sync mode",
++ ct->rbd_bus_id);
++ return fn(ct, c->message);
++ }
++ pthread_attr_destroy(&attr);
++ rbd_timeout(&tsp);
++ r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp);
++ rbd_status = ct->state;
++ strncpy(c->message, ct->message,CHECKER_MSG_LEN);
++ c->message[CHECKER_MSG_LEN -1] = '\0';
++ pthread_mutex_unlock(&ct->lock);
++
++ if (ct->thread &&
++ (rbd_status == PATH_PENDING || rbd_status == PATH_UNCHECKED)) {
++ condlog(3, "rbd%d thread still running",
++ ct->rbd_bus_id);
++ ct->running = 1;
++ rbd_status = PATH_PENDING;
++ }
++ }
++
++ return rbd_status;
++}
++
++void libcheck_repair(struct checker * c)
++{
++ struct rbd_checker_context *ct = c->context;
++
++ if (!ct || !ct->blacklisted)
++ return;
++ rbd_exec_fn(c, rbd_repair);
++}
++
++int libcheck_check(struct checker * c)
++{
++ struct rbd_checker_context *ct = c->context;
++
++ if (!ct)
++ return PATH_UNCHECKED;
++
++ if (ct->blacklisted)
++ return PATH_DOWN;
++
++ return rbd_exec_fn(c, rbd_check);
++}
+Index: multipath-tools-130222/multipath.conf.annotated
+===================================================================
+--- multipath-tools-130222.orig/multipath.conf.annotated
++++ multipath-tools-130222/multipath.conf.annotated
+@@ -97,7 +97,7 @@
+ # # scope : multipath & multipathd
+ # # desc : the default method used to determine the paths' state
+ # # values : readsector0|tur|emc_clariion|hp_sw|directio|rdac|
+-# cciss_tur|hp_tur
++# cciss_tur|hp_tur|rbd
+ # # default : directio
+ # #
+ # path_checker directio
+@@ -493,7 +493,7 @@
+ # # scope : multipathd & multipathd
+ # # desc : path checking algorithm to use to check path state
+ # # values : readsector0|tur|emc_clariion|hp_sw|directio|rdac|
+-# # cciss_tur|hp_tur
++# # cciss_tur|hp_tur|rbd
+ # #
+ # path_checker directio
+ #
+Index: multipath-tools-130222/multipath/multipath.conf.5
+===================================================================
+--- multipath-tools-130222.orig/multipath/multipath.conf.5
++++ multipath-tools-130222/multipath/multipath.conf.5
+@@ -284,6 +284,9 @@ Check the path state for LSI/Engenio/Net
+ .B directio
+ Read the first sector with direct I/O.
+ .TP
++.B rbd
++Check if the path is in the Ceph blacklist.
++.TP
+ Default value is \fIdirectio\fR.
+ .RE
+ .TP
diff --git a/0181-multipath-tools-Add-rbd-to-the-hwtable.patch b/0181-multipath-tools-Add-rbd-to-the-hwtable.patch
new file mode 100644
index 0000000..fd9d6ff
--- /dev/null
+++ b/0181-multipath-tools-Add-rbd-to-the-hwtable.patch
@@ -0,0 +1,51 @@
+From b25186a60347e2a0f2e53a10c05d9ad52a88c890 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Tue, 9 Aug 2016 13:53:21 -0500
+Subject: [PATCH 05/11] multipath-tools: Add rbd to the hwtable
+
+For BZ 1348372 from upstream commit:
+
+commit 61fe9e521965ff70db6a65370b394d769077d54c
+Author: Mike Christie <mchristi@redhat.com>
+Date: Mon Aug 8 07:01:50 2016 -0500
+
+ multipath-tools: Add rbd to the hwtable
+
+ Add rbd to hwtable. These defaults are for the HA type of setup
+ supported by the checker. We do no support features like multibus
+ at the dm-multipath level yet
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/hwtable.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
+index 61d1033..d278c04 100644
+--- a/libmultipath/hwtable.c
++++ b/libmultipath/hwtable.c
+@@ -1206,6 +1206,21 @@ static struct hwentry default_hw[] = {
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .checker_name = TUR,
+ },
++ {
++ /*
++ * Red Hat
++ *
++ * Maintainer: Mike Christie
++ * Mail: mchristi@redhat.com
++ */
++ .vendor = "Ceph",
++ .product = "RBD",
++ .pgpolicy = FAILOVER,
++ .no_path_retry = NO_PATH_RETRY_FAIL,
++ .checker_name = RBD,
++ .deferred_remove = DEFERRED_REMOVE_ON,
++ },
++
+ /*
+ * EOL
+ */
+--
+1.8.3.1
+
diff --git a/0182-multipath-tools-check-for-initialized-checker-before.patch b/0182-multipath-tools-check-for-initialized-checker-before.patch
new file mode 100644
index 0000000..26b8f28
--- /dev/null
+++ b/0182-multipath-tools-check-for-initialized-checker-before.patch
@@ -0,0 +1,49 @@
+From 7592f62383e6143a54d89885e505834c4977c4a6 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Tue, 16 Aug 2016 11:44:27 -0500
+Subject: [PATCH 06/11] multipath-tools: check for initialized checker before
+
+For bz from upstream commit:
+
+commit b5773d46a4550c3c222bb415197e0bc5f09c1169
+Author: Mike Christie <mchristi@redhat.com>
+Date: Mon Aug 15 12:13:45 2016 -0500
+
+ multipath-tools: check for initialized checker before
+
+ This fixes a regression added with:
+ 015f87b16a7797a17afd514aec46e65c2a1a2f73
+
+ We can hit a race where when pathinfo is setting up a path, the path
+ could have gone down already. In the DI_CHECKER chunk we then do not
+run
+ get_state and attach a checker. Later when check_path is run
+ path_offline could still return PATH_DOWN or PATH_REMOVED and
+ get_state is again not run so we do not get to attach a checker. I
+ was then running repair_path since the state was PATH_DOWN, and we
+then
+ hit a segfault.
+
+ This has us test if a checker is selected before running repair.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c
+index fa7d8b7..6cd8d34 100644
+--- a/libmultipath/checkers.c
++++ b/libmultipath/checkers.c
+@@ -212,7 +212,7 @@ void checker_put (struct checker * dst)
+
+ void checker_repair (struct checker * c)
+ {
+- if (!c)
++ if (!c || !checker_selected(c))
+ return;
+
+ c->message[0] = '\0';
+--
+1.8.3.1
+
diff --git a/0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch b/0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch
new file mode 100644
index 0000000..93323c1
--- /dev/null
+++ b/0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch
@@ -0,0 +1,91 @@
+From 2926316c8492a1d18c7bbdac0fac75c38ce16152 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Tue, 16 Aug 2016 11:47:16 -0500
+Subject: [PATCH 07/11] multipathd: Don't call repair on blacklisted path
+
+For BZ 1348372 from upstream commit:
+
+Author: Mike Christie <mchristi@redhat.com>
+Date: Mon Aug 15 12:13:46 2016 -0500
+
+ multipathd: Don't call repair on blacklisted paths
+
+ This fixes a regression added in
+ 015f87b16a7797a17afd514aec46e65c2a1a2f73
+
+ If a path is blacklisted the checkerloop will free the path so
+ don't call repair on it.
+
+ This moves the repair call down into check_path, because I think
+ we also do not need to call it for other cases where we cannot get
+ the uuid info or being orphaned.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ multipathd/main.c | 22 ++++++++++++----------
+ 1 file changed, 12 insertions(+), 10 deletions(-)
+
+diff --git a/multipathd/main.c b/multipathd/main.c
+index d26fd22..4638c8b 100644
+--- a/multipathd/main.c
++++ b/multipathd/main.c
+@@ -1238,6 +1238,16 @@ int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh)
+ return 0;
+ }
+
++void repair_path(struct path * pp)
++{
++ if (pp->state != PATH_DOWN)
++ return;
++
++ checker_repair(&pp->checker);
++ if (strlen(checker_message(&pp->checker)))
++ LOG_MSG(1, checker_message(&pp->checker));
++}
++
+ void
+ check_path (struct vectors * vecs, struct path * pp)
+ {
+@@ -1352,6 +1362,7 @@ check_path (struct vectors * vecs, struct path * pp)
+ pp->mpp->failback_tick = 0;
+
+ pp->mpp->stat_path_failures++;
++ repair_path(pp);
+ return;
+ }
+
+@@ -1431,7 +1442,7 @@ check_path (struct vectors * vecs, struct path * pp)
+ }
+
+ pp->state = newstate;
+-
++ repair_path(pp);
+
+ if (pp->mpp->wait_for_udev)
+ return;
+@@ -1455,14 +1466,6 @@ check_path (struct vectors * vecs, struct path * pp)
+ }
+ }
+
+-void repair_path(struct vectors * vecs, struct path * pp)
+-{
+- if (pp->state != PATH_DOWN)
+- return;
+-
+- checker_repair(&pp->checker);
+-}
+-
+ static void *
+ checkerloop (void *ap)
+ {
+@@ -1491,7 +1494,6 @@ checkerloop (void *ap)
+ if (vecs->pathvec) {
+ vector_foreach_slot (vecs->pathvec, pp, i) {
+ check_path(vecs, pp);
+- repair_path(vecs, pp);
+ }
+ }
+ if (vecs->mpvec) {
+--
+1.8.3.1
+
diff --git a/0184-rbd-fix-sync-repair-support.patch b/0184-rbd-fix-sync-repair-support.patch
new file mode 100644
index 0000000..6ae6303
--- /dev/null
+++ b/0184-rbd-fix-sync-repair-support.patch
@@ -0,0 +1,29 @@
+From d1bda720153b4978121fbae40f82d2f4b9aff997 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Thu, 25 Aug 2016 01:34:11 -0500
+Subject: [PATCH 08/11] rbd: fix sync repair support
+
+If sync was set we were calling check instead
+of function passed in.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers/rbd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c
+index 6f1b53a..76f4005 100644
+--- a/libmultipath/checkers/rbd.c
++++ b/libmultipath/checkers/rbd.c
+@@ -554,7 +554,7 @@ static int rbd_exec_fn(struct checker *c, thread_fn *fn)
+ int rbd_status, r;
+
+ if (c->sync)
+- return rbd_check(ct, c->message);
++ return fn(ct, c->message);
+ /*
+ * Async mode
+ */
+--
+1.8.3.1
+
diff --git a/0185-rbd-check-for-nonshared-clients.patch b/0185-rbd-check-for-nonshared-clients.patch
new file mode 100644
index 0000000..c209d73
--- /dev/null
+++ b/0185-rbd-check-for-nonshared-clients.patch
@@ -0,0 +1,32 @@
+From c9a788f437f2729f943cd03c43e84b65d74eb015 Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Wed, 31 Aug 2016 15:22:09 -0500
+Subject: [PATCH 09/11] rbd: check for nonshared clients
+
+The rbd checker only supports nonshared clients so add a check
+during init time.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers/rbd.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c
+index 76f4005..a6f3405 100644
+--- a/libmultipath/checkers/rbd.c
++++ b/libmultipath/checkers/rbd.c
+@@ -123,6 +123,11 @@ int libcheck_init(struct checker * c)
+ if (!config_info)
+ goto free_addr;
+
++ if (!strstr(config_info, "noshare")) {
++ condlog(3, "Only nonshared clients supported.");
++ goto free_addr;
++ }
++
+ ct->config_info = strdup(config_info);
+ if (!ct->config_info)
+ goto free_addr;
+--
+1.8.3.1
+
diff --git a/0186-rbd-check-for-exclusive-lock-enabled.patch b/0186-rbd-check-for-exclusive-lock-enabled.patch
new file mode 100644
index 0000000..290850b
--- /dev/null
+++ b/0186-rbd-check-for-exclusive-lock-enabled.patch
@@ -0,0 +1,56 @@
+From 513d210cdbccfdaadb0cf7f09ba97d563aac52bb Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Wed, 31 Aug 2016 15:40:16 -0500
+Subject: [PATCH 10/11] rbd: check for exclusive lock enabled
+
+Only attach the checker if the rbd image has the exclusive lock
+enabled.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers/rbd.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c
+index a6f3405..e34bf53 100644
+--- a/libmultipath/checkers/rbd.c
++++ b/libmultipath/checkers/rbd.c
+@@ -33,6 +33,8 @@ typedef int (thread_fn)(struct rbd_checker_context *ct, char *msg);
+
+ #define RBD_MSG(msg, fmt, args...) snprintf(msg, CHECKER_MSG_LEN, fmt, ##args);
+
++#define RBD_FEATURE_EXCLUSIVE_LOCK (1 << 2)
++
+ struct rbd_checker_context {
+ int rbd_bus_id;
+ char *client_addr;
+@@ -65,8 +67,9 @@ int libcheck_init(struct checker * c)
+ struct udev_device *bus_dev;
+ struct udev *udev;
+ struct stat sb;
+- const char *block_name, *addr, *config_info;
++ const char *block_name, *addr, *config_info, *features_str;
+ const char *image, *pool, *snap, *username;
++ uint64_t features = 0;
+ char sysfs_path[PATH_SIZE];
+ int ret;
+
+@@ -119,6 +122,15 @@ int libcheck_init(struct checker * c)
+ if (!ct->client_addr)
+ goto free_dev;
+
++ features_str = udev_device_get_sysattr_value(bus_dev, "features");
++ if (!features_str)
++ goto free_addr;
++ features = strtoll(features_str, NULL, 16);
++ if (!(features & RBD_FEATURE_EXCLUSIVE_LOCK)) {
++ condlog(3, "Exclusive lock not set.");
++ goto free_addr;
++ }
++
+ config_info = udev_device_get_sysattr_value(bus_dev, "config_info");
+ if (!config_info)
+ goto free_addr;
+--
+1.8.3.1
+
diff --git a/0187-rbd-fixup-log-messages.patch b/0187-rbd-fixup-log-messages.patch
new file mode 100644
index 0000000..c4ed6db
--- /dev/null
+++ b/0187-rbd-fixup-log-messages.patch
@@ -0,0 +1,238 @@
+From 3ed9a923904887e41c774c71232ae2a1ff6fc3fb Mon Sep 17 00:00:00 2001
+From: Mike Christie <mchristi@redhat.com>
+Date: Wed, 31 Aug 2016 15:59:53 -0500
+Subject: [PATCH 11/11] rbd: fixup log messages
+
+Add rbd device prefix to condlog messages that was missing it, and drop
+it in RBD_MSG because it is already added by caller.
+
+Signed-off-by: Mike Christie <mchristi@redhat.com>
+---
+ libmultipath/checkers/rbd.c | 67 +++++++++++++++++++++++----------------------
+ 1 file changed, 35 insertions(+), 32 deletions(-)
+
+diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c
+index e34bf53..8e6cd3c 100644
+--- a/libmultipath/checkers/rbd.c
++++ b/libmultipath/checkers/rbd.c
+@@ -113,8 +113,8 @@ int libcheck_init(struct checker * c)
+
+ addr = udev_device_get_sysattr_value(bus_dev, "client_addr");
+ if (!addr) {
+- condlog(0, "Could not find client_addr in rbd sysfs. Try "
+- "updating kernel");
++ condlog(0, "rbd%d: Could not find client_addr in rbd sysfs. "
++ "Try updating kernel", ct->rbd_bus_id);
+ goto free_dev;
+ }
+
+@@ -127,7 +127,7 @@ int libcheck_init(struct checker * c)
+ goto free_addr;
+ features = strtoll(features_str, NULL, 16);
+ if (!(features & RBD_FEATURE_EXCLUSIVE_LOCK)) {
+- condlog(3, "Exclusive lock not set.");
++ condlog(3, "rbd%d: Exclusive lock not set.", ct->rbd_bus_id);
+ goto free_addr;
+ }
+
+@@ -136,7 +136,8 @@ int libcheck_init(struct checker * c)
+ goto free_addr;
+
+ if (!strstr(config_info, "noshare")) {
+- condlog(3, "Only nonshared clients supported.");
++ condlog(3, "rbd%d: Only nonshared clients supported.",
++ ct->rbd_bus_id);
+ goto free_addr;
+ }
+
+@@ -189,18 +190,20 @@ int libcheck_init(struct checker * c)
+ }
+
+ if (rados_create(&ct->cluster, NULL) < 0) {
+- condlog(0, "Could not create rados cluster");
++ condlog(0, "rbd%d: Could not create rados cluster",
++ ct->rbd_bus_id);
+ goto free_snap;
+ }
+
+ if (rados_conf_read_file(ct->cluster, NULL) < 0) {
+- condlog(0, "Could not read rados conf");
++ condlog(0, "rbd%d: Could not read rados conf", ct->rbd_bus_id);
+ goto shutdown_rados;
+ }
+
+ ret = rados_connect(ct->cluster);
+ if (ret < 0) {
+- condlog(0, "Could not connect to rados cluster");
++ condlog(0, "rbd%d: Could not connect to rados cluster",
++ ct->rbd_bus_id);
+ goto shutdown_rados;
+ }
+
+@@ -291,8 +294,7 @@ static int rbd_is_blacklisted(struct rbd_checker_context *ct, char *msg)
+ ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0,
+ &blklist, &blklist_len, &stat, &stat_len);
+ if (ret < 0) {
+- RBD_MSG(msg, "rbd checker failed: mon command failed %d",
+- ret);
++ RBD_MSG(msg, "checker failed: mon command failed %d", ret);
+ return ret;
+ }
+
+@@ -313,16 +315,15 @@ static int rbd_is_blacklisted(struct rbd_checker_context *ct, char *msg)
+
+ end = strchr(addr_tok, ' ');
+ if (!end) {
+- RBD_MSG(msg, "rbd%d checker failed: invalid blacklist %s",
+- ct->rbd_bus_id, addr_tok);
++ RBD_MSG(msg, "checker failed: invalid blacklist %s",
++ addr_tok);
+ break;
+ }
+ *end = '\0';
+
+ if (!strcmp(addr_tok, ct->client_addr)) {
+ ct->blacklisted = 1;
+- RBD_MSG(msg, "rbd%d checker: %s is blacklisted",
+- ct->rbd_bus_id, ct->client_addr);
++ RBD_MSG(msg, "%s is blacklisted", ct->client_addr);
+ ret = 1;
+ break;
+ }
+@@ -339,7 +340,7 @@ int rbd_check(struct rbd_checker_context *ct, char *msg)
+ if (ct->blacklisted || rbd_is_blacklisted(ct, msg) == 1)
+ return PATH_DOWN;
+
+- RBD_MSG(msg, "rbd checker reports path is up");
++ RBD_MSG(msg, "checker reports path is up");
+ /*
+ * Path may have issues, but the ceph cluster is at least
+ * accepting IO, so we can attempt to do IO.
+@@ -411,10 +412,12 @@ static int rbd_remap(struct rbd_checker_context *ct)
+ argv[i] = NULL;
+
+ ret = execvp(argv[0], argv);
+- condlog(0, "Error executing rbd: %s", strerror(errno));
++ condlog(0, "rbd%d: Error executing rbd: %s", ct->rbd_bus_id,
++ strerror(errno));
+ exit(-1);
+ case -1:
+- condlog(0, "fork failed: %s", strerror(errno));
++ condlog(0, "rbd%d: fork failed: %s", ct->rbd_bus_id,
++ strerror(errno));
+ return -1;
+ default:
+ ret = -1;
+@@ -424,7 +427,8 @@ static int rbd_remap(struct rbd_checker_context *ct)
+ if (status == 0)
+ ret = 0;
+ else
+- condlog(0, "rbd failed with %d", status);
++ condlog(0, "rbd%d: failed with %d",
++ ct->rbd_bus_id, status);
+ }
+ }
+
+@@ -454,12 +458,12 @@ static int rbd_rm_blacklist(struct rbd_checker_context *ct)
+ ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0,
+ NULL, 0, &stat, &stat_len);
+ if (ret < 0) {
+- condlog(1, "rbd%d repair failed to remove blacklist for %s %d",
++ condlog(1, "rbd%d: repair failed to remove blacklist for %s %d",
+ ct->rbd_bus_id, ct->client_addr, ret);
+ goto free_cmd;
+ }
+
+- condlog(1, "rbd%d repair rm blacklist for %s",
++ condlog(1, "rbd%d: repair rm blacklist for %s",
+ ct->rbd_bus_id, ct->client_addr);
+ free(stat);
+ free_cmd:
+@@ -478,8 +482,7 @@ static int rbd_repair(struct rbd_checker_context *ct, char *msg)
+ if (!ct->remapped) {
+ ret = rbd_remap(ct);
+ if (ret) {
+- RBD_MSG(msg, "rbd%d repair failed to remap. Err %d",
+- ct->rbd_bus_id, ret);
++ RBD_MSG(msg, "repair failed to remap. Err %d", ret);
+ return PATH_DOWN;
+ }
+ }
+@@ -488,22 +491,21 @@ static int rbd_repair(struct rbd_checker_context *ct, char *msg)
+ snprintf(del, sizeof(del), "%d force", ct->rbd_bus_id);
+ ret = sysfs_write_rbd_remove(del, strlen(del) + 1);
+ if (ret) {
+- RBD_MSG(msg, "rbd%d repair failed to clean up. Err %d",
+- ct->rbd_bus_id, ret);
++ RBD_MSG(msg, "repair failed to clean up. Err %d", ret);
+ return PATH_DOWN;
+ }
+
+ ret = rbd_rm_blacklist(ct);
+ if (ret) {
+- RBD_MSG(msg, "rbd%d repair could not remove blacklist entry. Err %d",
+- ct->rbd_bus_id, ret);
++ RBD_MSG(msg, "repair could not remove blacklist entry. Err %d",
++ ret);
+ return PATH_DOWN;
+ }
+
+ ct->remapped = 0;
+ ct->blacklisted = 0;
+
+- RBD_MSG(msg, "rbd%d has been repaired", ct->rbd_bus_id);
++ RBD_MSG(msg, "has been repaired");
+ return PATH_UP;
+ }
+
+@@ -528,7 +530,7 @@ void *rbd_thread(void *ctx)
+ struct rbd_checker_context *ct = ctx;
+ int state;
+
+- condlog(3, "rbd%d thread starting up", ct->rbd_bus_id);
++ condlog(3, "rbd%d: thread starting up", ct->rbd_bus_id);
+
+ ct->message[0] = '\0';
+ /* This thread can be canceled, so setup clean up */
+@@ -547,7 +549,7 @@ void *rbd_thread(void *ctx)
+ pthread_mutex_unlock(&ct->lock);
+ pthread_cond_signal(&ct->active);
+
+- condlog(3, "rbd%d thead finished, state %s", ct->rbd_bus_id,
++ condlog(3, "rbd%d: thead finished, state %s", ct->rbd_bus_id,
+ checker_state_name(state));
+ rbd_thread_cleanup_pop(ct);
+ return ((void *)0);
+@@ -577,16 +579,17 @@ static int rbd_exec_fn(struct checker *c, thread_fn *fn)
+ */
+ r = pthread_mutex_lock(&ct->lock);
+ if (r != 0) {
+- condlog(2, "rbd%d mutex lock failed with %d", ct->rbd_bus_id,
++ condlog(2, "rbd%d: mutex lock failed with %d", ct->rbd_bus_id,
+ r);
+- MSG(c, "rbd%d thread failed to initialize", ct->rbd_bus_id);
++ MSG(c, "rbd%d: thread failed to initialize", ct->rbd_bus_id);
+ return PATH_WILD;
+ }
+
+ if (ct->running) {
+ /* Check if checker is still running */
+ if (ct->thread) {
+- condlog(3, "rbd%d thread not finished", ct->rbd_bus_id);
++ condlog(3, "rbd%d: thread not finished",
++ ct->rbd_bus_id);
+ rbd_status = PATH_PENDING;
+ } else {
+ /* checker done */
+@@ -623,7 +626,7 @@ static int rbd_exec_fn(struct checker *c, thread_fn *fn)
+
+ if (ct->thread &&
+ (rbd_status == PATH_PENDING || rbd_status == PATH_UNCHECKED)) {
+- condlog(3, "rbd%d thread still running",
++ condlog(3, "rbd%d: thread still running",
+ ct->rbd_bus_id);
+ ct->running = 1;
+ rbd_status = PATH_PENDING;
+--
+1.8.3.1
+
diff --git a/0188-RHBZ-1368501-dont-exit.patch b/0188-RHBZ-1368501-dont-exit.patch
new file mode 100644
index 0000000..4783991
--- /dev/null
+++ b/0188-RHBZ-1368501-dont-exit.patch
@@ -0,0 +1,208 @@
+---
+ libmultipath/configure.c | 8 +++--
+ multipathd/main.c | 68 +++++++++++++++++++++++++++++++++++++----------
+ 2 files changed, 59 insertions(+), 17 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/configure.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.c
++++ multipath-tools-130222/libmultipath/configure.c
+@@ -829,8 +829,10 @@ coalesce_paths (struct vectors * vecs, v
+ * at this point, we know we really got a new mp
+ */
+ mpp = add_map_with_path(vecs, pp1, 0);
+- if (!mpp)
+- return 1;
++ if (!mpp) {
++ orphan_path(pp1);
++ continue;
++ }
+
+ if (pp1->priority == PRIO_UNDEF)
+ mpp->action = ACT_REJECT;
+@@ -879,7 +881,7 @@ coalesce_paths (struct vectors * vecs, v
+ condlog(3, "%s: domap (%u) failure "
+ "for create/reload map",
+ mpp->alias, r);
+- if (r == DOMAP_FAIL) {
++ if (r == DOMAP_FAIL || conf->daemon) {
+ condlog(2, "%s: %s map",
+ mpp->alias, (mpp->action == ACT_CREATE)?
+ "ignoring" : "removing");
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -821,7 +821,7 @@ map_discovery (struct vectors * vecs)
+
+ vector_foreach_slot (vecs->mpvec, mpp, i)
+ if (setup_multipath(vecs, mpp))
+- return 1;
++ i--;
+
+ return 0;
+ }
+@@ -1523,21 +1523,29 @@ configure (struct vectors * vecs, int st
+ vector mpvec;
+ int i, ret;
+
+- if (!vecs->pathvec && !(vecs->pathvec = vector_alloc()))
++ if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) {
++ condlog(0, "couldn't allocate path vec in configure");
+ return 1;
++ }
+
+- if (!vecs->mpvec && !(vecs->mpvec = vector_alloc()))
++ if (!vecs->mpvec && !(vecs->mpvec = vector_alloc())) {
++ condlog(0, "couldn't allocate multipath vec in configure");
+ return 1;
++ }
+
+- if (!(mpvec = vector_alloc()))
++ if (!(mpvec = vector_alloc())) {
++ condlog(0, "couldn't allocate new maps vec in configure");
+ return 1;
++ }
+
+ /*
+ * probe for current path (from sysfs) and map (from dm) sets
+ */
+ ret = path_discovery(vecs->pathvec, conf, DI_ALL);
+- if (ret < 0)
++ if (ret < 0) {
++ condlog(0, "configure failed at path discovery");
+ return 1;
++ }
+
+ vector_foreach_slot (vecs->pathvec, pp, i){
+ if (filter_path(conf, pp) > 0){
+@@ -1548,21 +1556,27 @@ configure (struct vectors * vecs, int st
+ else
+ pp->checkint = conf->checkint;
+ }
+- if (map_discovery(vecs))
++ if (map_discovery(vecs)) {
++ condlog(0, "configure failed at map discovery");
+ return 1;
++ }
+
+ /*
+ * create new set of maps & push changed ones into dm
+ */
+- if (coalesce_paths(vecs, mpvec, NULL, 1))
++ if (coalesce_paths(vecs, mpvec, NULL, 1)) {
++ condlog(0, "configure failed while coalescing paths");
+ return 1;
++ }
+
+ /*
+ * may need to remove some maps which are no longer relevant
+ * e.g., due to blacklist changes in conf file
+ */
+- if (coalesce_maps(vecs, mpvec))
++ if (coalesce_maps(vecs, mpvec)) {
++ condlog(0, "configure failed while coalescing maps");
+ return 1;
++ }
+
+ dm_lib_release();
+
+@@ -1588,11 +1602,15 @@ configure (struct vectors * vecs, int st
+ * start dm event waiter threads for these new maps
+ */
+ vector_foreach_slot(vecs->mpvec, mpp, i) {
+- if (setup_multipath(vecs, mpp))
+- return 1;
++ if (setup_multipath(vecs, mpp)) {
++ i--;
++ continue;
++ }
+ if (start_waiters)
+- if (start_waiter_thread(mpp, vecs))
+- return 1;
++ if (start_waiter_thread(mpp, vecs)) {
++ remove_map(mpp, vecs, 1);
++ i--;
++ }
+ }
+ return 0;
+ }
+@@ -1857,15 +1875,23 @@ child (void * param)
+ condlog(2, "--------start up--------");
+ condlog(2, "read " DEFAULT_CONFIGFILE);
+
+- if (load_config(DEFAULT_CONFIGFILE, udev))
++ if (load_config(DEFAULT_CONFIGFILE, udev)) {
++ condlog(0, "failed to load config");
++ if (logsink)
++ log_thread_stop();
+ exit(1);
++ }
+
+ if (init_checkers()) {
+ condlog(0, "failed to initialize checkers");
++ if (logsink)
++ log_thread_stop();
+ exit(1);
+ }
+ if (init_prio()) {
+ condlog(0, "failed to initialize prioritizers");
++ if (logsink)
++ log_thread_stop();
+ exit(1);
+ }
+
+@@ -1898,8 +1924,12 @@ child (void * param)
+ }
+
+ vecs = gvecs = init_vecs();
+- if (!vecs)
++ if (!vecs) {
++ condlog(0, "failed to create vecs");
++ if (logsink)
++ log_thread_stop();
+ exit(1);
++ }
+
+ setscheduler();
+ set_oom_adj();
+@@ -1911,11 +1941,15 @@ child (void * param)
+ */
+ if ((rc = pthread_create(&uevent_thr, &uevent_attr, ueventloop, udev))) {
+ condlog(0, "failed to create uevent thread: %d", rc);
++ if (logsink)
++ log_thread_stop();
+ exit(1);
+ }
+ pthread_attr_destroy(&uevent_attr);
+ if ((rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs))) {
+ condlog(0, "failed to create cli listener: %d", rc);
++ if (logsink)
++ log_thread_stop();
+ exit(1);
+ }
+ /*
+@@ -1927,6 +1961,8 @@ child (void * param)
+ if (configure(vecs, 1)) {
+ unlock(vecs->lock);
+ condlog(0, "failure during configuration");
++ if (logsink)
++ log_thread_stop();
+ exit(1);
+ }
+ unlock(vecs->lock);
+@@ -1936,10 +1972,14 @@ child (void * param)
+ */
+ if ((rc = pthread_create(&check_thr, &misc_attr, checkerloop, vecs))) {
+ condlog(0,"failed to create checker loop thread: %d", rc);
++ if (logsink)
++ log_thread_stop();
+ exit(1);
+ }
+ if ((rc = pthread_create(&uevq_thr, &misc_attr, uevqloop, vecs))) {
+ condlog(0, "failed to create uevent dispatcher: %d", rc);
++ if (logsink)
++ log_thread_stop();
+ exit(1);
+ }
+ pthread_attr_destroy(&misc_attr);
diff --git a/0189-RHBZ-1368211-remove-retries.patch b/0189-RHBZ-1368211-remove-retries.patch
new file mode 100644
index 0000000..253d572
--- /dev/null
+++ b/0189-RHBZ-1368211-remove-retries.patch
@@ -0,0 +1,168 @@
+---
+ libmultipath/config.c | 1 +
+ libmultipath/config.h | 1 +
+ libmultipath/devmapper.c | 35 +++++++++++++++++++----------------
+ libmultipath/dict.c | 25 +++++++++++++++++++++++++
+ multipath.conf.defaults | 1 +
+ multipath/multipath.conf.5 | 5 +++++
+ 6 files changed, 52 insertions(+), 16 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/devmapper.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/devmapper.c
++++ multipath-tools-130222/libmultipath/devmapper.c
+@@ -803,10 +803,11 @@ dm_flush_map_nopaths(const char * mapnam
+ extern int
+ dm_suspend_and_flush_map (const char * mapname)
+ {
+- int s = 0, queue_if_no_path = 0;
++ int need_reset = 0, queue_if_no_path = 0;
+ unsigned long long mapsize;
+ char params[PARAMS_SIZE] = {0};
+ int udev_flags = 0;
++ int retries = conf->remove_retries;
+
+ if (!dm_is_mpath(mapname))
+ return 0; /* nothing to do */
+@@ -821,22 +822,24 @@ dm_suspend_and_flush_map (const char * m
+ queue_if_no_path = 1;
+ }
+
+- if (queue_if_no_path)
+- s = dm_queue_if_no_path((char *)mapname, 0);
+- /* Leave queue_if_no_path alone if unset failed */
+- if (s)
+- queue_if_no_path = 0;
+- else
+- s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0, 0);
+-
+- if (!dm_flush_map(mapname)) {
+- condlog(4, "multipath map %s removed", mapname);
+- return 0;
+- }
++ if (queue_if_no_path && dm_queue_if_no_path((char *)mapname, 0) == 0)
++ need_reset = 1;
++
++ do {
++ if (!queue_if_no_path || need_reset)
++ dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0, 0);
++
++ if (!dm_flush_map(mapname)) {
++ condlog(4, "multipath map %s removed", mapname);
++ return 0;
++ }
++ dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags);
++ if (retries)
++ sleep(1);
++ } while (retries-- > 0);
+ condlog(2, "failed to remove multipath map %s", mapname);
+- dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags);
+- if (queue_if_no_path)
+- s = dm_queue_if_no_path((char *)mapname, 1);
++ if (need_reset)
++ dm_queue_if_no_path((char *)mapname, 1);
+ return 1;
+ }
+
+Index: multipath-tools-130222/libmultipath/config.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.c
++++ multipath-tools-130222/libmultipath/config.c
+@@ -680,6 +680,7 @@ load_config (char * file, struct udev *u
+ conf->new_bindings_in_boot = 0;
+ conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;
+ conf->skip_kpartx = DEFAULT_SKIP_KPARTX;
++ conf->remove_retries = 0;
+
+ /*
+ * preload default hwtable
+Index: multipath-tools-130222/libmultipath/config.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.h
++++ multipath-tools-130222/libmultipath/config.h
+@@ -146,6 +146,7 @@ struct config {
+ int delayed_reconfig;
+ int uev_wait_timeout;
+ int skip_kpartx;
++ int remove_retries;
+ unsigned int version[3];
+
+ char * dev;
+Index: multipath-tools-130222/libmultipath/dict.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/dict.c
++++ multipath-tools-130222/libmultipath/dict.c
+@@ -935,6 +935,24 @@ def_new_bindings_in_boot_handler(vector
+ return 0;
+ }
+
++static int
++def_remove_retries_handler(vector strvec)
++{
++ char *buff;
++
++ buff = set_value(strvec);
++
++ if (!buff)
++ return 1;
++
++ conf->remove_retries = atoi(buff);
++ if (conf->remove_retries < 0)
++ conf->remove_retries = 0;
++ FREE(buff);
++
++ return 0;
++}
++
+ /*
+ * blacklist block handlers
+ */
+@@ -3405,6 +3423,12 @@ snprint_def_new_bindings_in_boot(char *
+ }
+
+ static int
++snprint_def_remove_retries (char * buff, int len, void * data)
++{
++ return snprintf(buff, len, "%i", conf->remove_retries);
++}
++
++static int
+ snprint_ble_simple (char * buff, int len, void * data)
+ {
+ struct blentry * ble = (struct blentry *)data;
+@@ -3483,6 +3507,7 @@ init_keywords(void)
+ install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay);
+ install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout);
+ install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot);
++ install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries);
+ __deprecated install_keyword("default_selector", &def_selector_handler, NULL);
+ __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
+ __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
+Index: multipath-tools-130222/multipath.conf.defaults
+===================================================================
+--- multipath-tools-130222.orig/multipath.conf.defaults
++++ multipath-tools-130222/multipath.conf.defaults
+@@ -41,6 +41,7 @@
+ # retrigger_delay 10
+ # missing_uev_wait_timeout 30
+ # new_bindings_in_boot no
++# remove_retries 0
+ #}
+ #blacklist {
+ # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
+Index: multipath-tools-130222/multipath/multipath.conf.5
+===================================================================
+--- multipath-tools-130222.orig/multipath/multipath.conf.5
++++ multipath-tools-130222/multipath/multipath.conf.5
+@@ -556,6 +556,11 @@ user_friendly_names. When multipathd is
+ regular filesystem, the device will be renamed to a user_friendly_name. The
+ default is
+ .I no
++.TP
++.B remove_retries
++This sets how may times multipath will retry removing a device that is in-use.
++Between each attempt, multipath will sleep 1 second. The default is
++.I 0
+ .
+ .SH "blacklist section"
+ The
diff --git a/0190-RHBZ-1380602-rbd-lock-on-read.patch b/0190-RHBZ-1380602-rbd-lock-on-read.patch
new file mode 100644
index 0000000..584d37f
--- /dev/null
+++ b/0190-RHBZ-1380602-rbd-lock-on-read.patch
@@ -0,0 +1,38 @@
+---
+ libmultipath/checkers/rbd.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+Index: multipath-tools-130222/libmultipath/checkers/rbd.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/checkers/rbd.c
++++ multipath-tools-130222/libmultipath/checkers/rbd.c
+@@ -45,6 +45,7 @@ struct rbd_checker_context {
+ char *username;
+ int remapped;
+ int blacklisted;
++ int lock_on_read:1;
+
+ rados_t cluster;
+
+@@ -141,6 +142,9 @@ int libcheck_init(struct checker * c)
+ goto free_addr;
+ }
+
++ if (strstr(config_info, "lock_on_read"))
++ ct->lock_on_read = 1;
++
+ ct->config_info = strdup(config_info);
+ if (!ct->config_info)
+ goto free_addr;
+@@ -397,7 +401,10 @@ static int rbd_remap(struct rbd_checker_
+ case 0:
+ argv[i++] = "rbd";
+ argv[i++] = "map";
+- argv[i++] = "-o noshare";
++ if (ct->lock_on_read)
++ argv[i++] = "-o noshare,lock_on_read";
++ else
++ argv[i++] = "-o noshare";
+ if (ct->username) {
+ argv[i++] = "--id";
+ argv[i++] = ct->username;
diff --git a/0191-RHBZ-1169168-disable-changed-paths.patch b/0191-RHBZ-1169168-disable-changed-paths.patch
new file mode 100644
index 0000000..e7e9286
--- /dev/null
+++ b/0191-RHBZ-1169168-disable-changed-paths.patch
@@ -0,0 +1,210 @@
+---
+ libmultipath/config.c | 1 +
+ libmultipath/config.h | 1 +
+ libmultipath/dict.c | 33 +++++++++++++++++++++++++++++++++
+ libmultipath/discovery.c | 11 ++++++-----
+ libmultipath/discovery.h | 1 +
+ libmultipath/structs.h | 1 +
+ multipathd/main.c | 26 ++++++++++++++++++++++++++
+ 7 files changed, 69 insertions(+), 5 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/discovery.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/discovery.c
++++ multipath-tools-130222/libmultipath/discovery.c
+@@ -1155,8 +1155,8 @@ free_dev:
+ return ret;
+ }
+
+-static int
+-get_uid (struct path * pp)
++int
++get_uid (struct path * pp, struct udev_device *udev)
+ {
+ char *c;
+ const char *value;
+@@ -1165,7 +1165,7 @@ get_uid (struct path * pp)
+ if (!pp->uid_attribute)
+ select_getuid(pp);
+
+- if (!pp->udev) {
++ if (!udev) {
+ condlog(1, "%s: no udev information", pp->dev);
+ return 1;
+ }
+@@ -1180,7 +1180,7 @@ get_uid (struct path * pp)
+ pp->tick = conf->retrigger_delay;
+ }
+ } else {
+- value = udev_device_get_property_value(pp->udev,
++ value = udev_device_get_property_value(udev,
+ pp->uid_attribute);
+ if ((!value || strlen(value) == 0) &&
+ conf->cmd == CMD_VALID_PATH)
+@@ -1194,6 +1194,7 @@ get_uid (struct path * pp)
+ len = strlen(value);
+ }
+ strncpy(pp->wwid, value, len);
++ condlog(4, "%s: got wwid of '%s'", pp->dev, pp->wwid);
+ pp->missing_udev_info = INFO_OK;
+ pp->tick = 0;
+ } else {
+@@ -1282,7 +1283,7 @@ pathinfo (struct path *pp, vector hwtabl
+ }
+
+ if ((mask & DI_WWID) && !strlen(pp->wwid))
+- get_uid(pp);
++ get_uid(pp, pp->udev);
+ if (mask & DI_BLACKLIST && mask & DI_WWID) {
+ if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
+ pp->wwid) > 0) {
+Index: multipath-tools-130222/libmultipath/discovery.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/discovery.h
++++ multipath-tools-130222/libmultipath/discovery.h
+@@ -44,6 +44,7 @@ int sysfs_set_scsi_tmo (struct multipath
+ int sysfs_get_timeout(struct path *pp, unsigned int *timeout);
+ int sysfs_get_host_pci_name(struct path *pp, char *pci_name);
+ int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address);
++int get_uid (struct path * pp, struct udev_device *udev);
+
+ /*
+ * discovery bitmask
+Index: multipath-tools-130222/libmultipath/structs.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/structs.h
++++ multipath-tools-130222/libmultipath/structs.h
+@@ -209,6 +209,7 @@ struct path {
+ int fd;
+ int missing_udev_info;
+ int retriggers;
++ int wwid_changed;
+
+ /* configlet pointers */
+ struct hwentry * hwe;
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -784,6 +784,26 @@ uev_update_path (struct uevent *uev, str
+ if (pp->missing_udev_info == INFO_REQUESTED)
+ return uev_add_path(uev, vecs);
+
++ if (conf->disable_changed_wwids &&
++ (strlen(pp->wwid) || pp->wwid_changed)) {
++ char wwid[WWID_SIZE];
++
++ strcpy(wwid, pp->wwid);
++ get_uid(pp, uev->udev);
++ if (strcmp(wwid, pp->wwid) != 0) {
++ condlog(0, "%s: path wwid changed from '%s' to '%s'. disallowing", uev->kernel, wwid, pp->wwid);
++ strcpy(pp->wwid, wwid);
++ if (!pp->wwid_changed) {
++ pp->wwid_changed = 1;
++ pp->tick = 1;
++ dm_fail_path(pp->mpp->alias, pp->dev_t);
++ }
++ }
++ else {
++ pp->wwid_changed = 0;
++ }
++ }
++
+ /* reinit the prio values on change event, in case something is
+ * different */
+ prio_init(&pp->prio);
+@@ -1284,6 +1304,12 @@ check_path (struct vectors * vecs, struc
+ else
+ checker_clear_message(&pp->checker);
+
++ if (pp->wwid_changed) {
++ condlog(2, "%s: path wwid has changed. Refusing to use",
++ pp->dev);
++ newstate = PATH_DOWN;
++ }
++
+ if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) {
+ condlog(2, "%s: unusable path", pp->dev);
+ pathinfo(pp, conf->hwtable, 0);
+Index: multipath-tools-130222/libmultipath/config.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.c
++++ multipath-tools-130222/libmultipath/config.c
+@@ -681,6 +681,7 @@ load_config (char * file, struct udev *u
+ conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;
+ conf->skip_kpartx = DEFAULT_SKIP_KPARTX;
+ conf->remove_retries = 0;
++ conf->disable_changed_wwids = 0;
+
+ /*
+ * preload default hwtable
+Index: multipath-tools-130222/libmultipath/config.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.h
++++ multipath-tools-130222/libmultipath/config.h
+@@ -147,6 +147,7 @@ struct config {
+ int uev_wait_timeout;
+ int skip_kpartx;
+ int remove_retries;
++ int disable_changed_wwids;
+ unsigned int version[3];
+
+ char * dev;
+Index: multipath-tools-130222/libmultipath/dict.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/dict.c
++++ multipath-tools-130222/libmultipath/dict.c
+@@ -953,6 +953,29 @@ def_remove_retries_handler(vector strvec
+ return 0;
+ }
+
++static int
++def_disable_changed_wwids_handler(vector strvec)
++{
++ char * buff;
++
++ buff = set_value(strvec);
++
++ if (!buff)
++ return 1;
++
++ if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
++ (strlen(buff) == 1 && !strcmp(buff, "0")))
++ conf->disable_changed_wwids = 0;
++ else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
++ (strlen(buff) == 1 && !strcmp(buff, "1")))
++ conf->disable_changed_wwids = 1;
++ else
++ conf->disable_changed_wwids = 0;
++
++ FREE(buff);
++ return 0;
++}
++
+ /*
+ * blacklist block handlers
+ */
+@@ -3429,6 +3452,15 @@ snprint_def_remove_retries (char * buff,
+ }
+
+ static int
++snprint_def_disable_changed_wwids(char * buff, int len, void * data)
++{
++ if (conf->disable_changed_wwids == 1)
++ return snprintf(buff, len, "yes");
++ else
++ return snprintf(buff, len, "no");
++}
++
++static int
+ snprint_ble_simple (char * buff, int len, void * data)
+ {
+ struct blentry * ble = (struct blentry *)data;
+@@ -3508,6 +3540,7 @@ init_keywords(void)
+ install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout);
+ install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot);
+ install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries);
++ install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids);
+ __deprecated install_keyword("default_selector", &def_selector_handler, NULL);
+ __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
+ __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
diff --git a/0192-RHBZ-1362409-infinibox-config.patch b/0192-RHBZ-1362409-infinibox-config.patch
new file mode 100644
index 0000000..dd56f8b
--- /dev/null
+++ b/0192-RHBZ-1362409-infinibox-config.patch
@@ -0,0 +1,34 @@
+---
+ libmultipath/hwtable.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+Index: multipath-tools-130222/libmultipath/hwtable.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/hwtable.c
++++ multipath-tools-130222/libmultipath/hwtable.c
+@@ -1168,6 +1168,25 @@ static struct hwentry default_hw[] = {
+ .prio_name = PRIO_ALUA,
+ .prio_args = NULL,
+ },
++ /*
++ * Infinidat
++ */
++ {
++ .vendor = "NFINIDAT",
++ .product = "InfiniBox.*",
++ .features = DEFAULT_FEATURES,
++ .hwhandler = DEFAULT_HWHANDLER,
++ .pgpolicy = GROUP_BY_PRIO,
++ .pgfailback = 30,
++ .rr_weight = RR_WEIGHT_PRIO,
++ .no_path_retry = NO_PATH_RETRY_FAIL,
++ .checker_name = TUR,
++ .prio_name = PRIO_ALUA,
++ .prio_args = NULL,
++ .selector = "round-robin 0",
++ .flush_on_last_del = FLUSH_ENABLED,
++ .dev_loss = 30,
++ },
+ {
+ .vendor = "XtremIO",
+ .product = "XtremApp",
diff --git a/0194-RHBZ-1351964-kpartx-recurse.patch b/0194-RHBZ-1351964-kpartx-recurse.patch
new file mode 100644
index 0000000..be5b22e
--- /dev/null
+++ b/0194-RHBZ-1351964-kpartx-recurse.patch
@@ -0,0 +1,17 @@
+---
+ kpartx/dos.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: multipath-tools-130222/kpartx/dos.c
+===================================================================
+--- multipath-tools-130222.orig/kpartx/dos.c
++++ multipath-tools-130222/kpartx/dos.c
+@@ -46,7 +46,7 @@ read_extended_partition(int fd, struct p
+ for (i=0; i<2; i++) {
+ memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p));
+ if (is_extended(p.sys_type)) {
+- if (p.nr_sects && !moretodo) {
++ if (p.start_sect && p.nr_sects && !moretodo) {
+ next = start + sector_size_mul * le32_to_cpu(p.start_sect);
+ moretodo = 1;
+ }
diff --git a/0195-RHBZ-1359510-no-daemon-msg.patch b/0195-RHBZ-1359510-no-daemon-msg.patch
new file mode 100644
index 0000000..667f10d
--- /dev/null
+++ b/0195-RHBZ-1359510-no-daemon-msg.patch
@@ -0,0 +1,111 @@
+---
+ libmultipath/configure.c | 21 ++++++++++++++++++++-
+ libmultipath/configure.h | 1 +
+ multipath/main.c | 21 +++++++++++++++++++++
+ 3 files changed, 42 insertions(+), 1 deletion(-)
+
+Index: multipath-tools-130222/libmultipath/configure.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.c
++++ multipath-tools-130222/libmultipath/configure.c
+@@ -743,7 +743,8 @@ deadmap (struct multipath * mpp)
+ return 1; /* dead */
+ }
+
+-int check_daemon(void)
++extern int
++check_daemon(void)
+ {
+ int fd;
+ char *reply;
+@@ -776,6 +777,7 @@ coalesce_paths (struct vectors * vecs, v
+ {
+ int r = 1;
+ int k, i;
++ int map_processed = 0;
+ char empty_buff[WWID_SIZE];
+ char params[PARAMS_SIZE];
+ struct multipath * mpp;
+@@ -936,6 +938,13 @@ coalesce_paths (struct vectors * vecs, v
+ else
+ remove_map(mpp, vecs, 0);
+ }
++
++ /* By now at least one multipath device map is processed,
++ * so set map_processed = 1
++ */
++ if (!map_processed)
++ map_processed = 1;
++
+ }
+ /*
+ * Flush maps with only dead paths (ie not in sysfs)
+@@ -963,6 +972,16 @@ coalesce_paths (struct vectors * vecs, v
+ condlog(2, "%s: remove (dead)", alias);
+ }
+ }
++
++ /* If there is at least one multipath device map processed then
++ * check if 'multipathd' service is running or not?
++ */
++ if (map_processed) {
++ if (!conf->daemon && !check_daemon())
++ condlog(0, "'multipathd' service is currently not "
++ "running, IO failover/failback will not work");
++ }
++
+ return 0;
+ }
+
+Index: multipath-tools-130222/libmultipath/configure.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.h
++++ multipath-tools-130222/libmultipath/configure.h
+@@ -27,6 +27,7 @@ enum actions {
+ int setup_map (struct multipath * mpp, char * params, int params_size );
+ int domap (struct multipath * mpp, char * params);
+ int reinstate_paths (struct multipath *mpp);
++int check_daemon(void);
+ int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload);
+ int get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid);
+ int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh);
+Index: multipath-tools-130222/multipath/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipath/main.c
++++ multipath-tools-130222/multipath/main.c
+@@ -178,6 +178,7 @@ static int
+ get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
+ {
+ int i;
++ int maps_present = 0;
+ struct multipath * mpp;
+ char params[PARAMS_SIZE], status[PARAMS_SIZE];
+
+@@ -226,7 +227,27 @@ get_dm_mpvec (vector curmp, vector pathv
+
+ if (conf->cmd == CMD_CREATE)
+ reinstate_paths(mpp);
++
++ /* At this place we have found at least one multipath
++ * device map, so set maps_present = 1
++ */
++ if (!maps_present)
++ maps_present = 1;
++
+ }
++
++ /* If there is at least one multipath device map present then
++ * check if 'multipathd' service is running or not?
++ */
++ if (maps_present) {
++ if (!conf->daemon && !check_daemon()) {
++ condlog(0, "multipath device maps are present, but "
++ "'multipathd' service is not running");
++ condlog(0, "IO failover/failback will not work without "
++ "'multipathd' service running");
++ }
++ }
++
+ return 0;
+ }
+
diff --git a/0196-RHBZ-1239173-dont-set-flag.patch b/0196-RHBZ-1239173-dont-set-flag.patch
new file mode 100644
index 0000000..269edc8
--- /dev/null
+++ b/0196-RHBZ-1239173-dont-set-flag.patch
@@ -0,0 +1,38 @@
+---
+ libmultipath/configure.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/configure.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.c
++++ multipath-tools-130222/libmultipath/configure.c
+@@ -257,7 +257,7 @@ extern int
+ setup_map (struct multipath * mpp, char * params, int params_size)
+ {
+ struct pathgroup * pgp;
+- int i;
++ int i, old_nr_active;
+
+ /*
+ * don't bother if devmap size is unknown
+@@ -311,8 +311,12 @@ setup_map (struct multipath * mpp, char
+ if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp))
+ return 1;
+
++ old_nr_active = mpp->nr_active;
+ mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
+
++ if (mpp->nr_active && !old_nr_active)
++ mpp->force_udev_reload = 1;
++
+ /*
+ * ponders each path group and determine highest prio pg
+ * to switch over (default to first)
+@@ -445,7 +449,6 @@ select_action (struct multipath * mpp, v
+ mpp->alias);
+ return;
+ }
+- mpp->force_udev_reload = !pathcount(mpp, PATH_WILD);
+ if (cmpp->size != mpp->size) {
+ mpp->force_udev_reload = 1;
+ mpp->action = ACT_RESIZE;
diff --git a/0197-RHBZ-1394059-max-sectors-kb.patch b/0197-RHBZ-1394059-max-sectors-kb.patch
new file mode 100644
index 0000000..7ed778d
--- /dev/null
+++ b/0197-RHBZ-1394059-max-sectors-kb.patch
@@ -0,0 +1,474 @@
+---
+ libmultipath/config.c | 3 +
+ libmultipath/config.h | 3 +
+ libmultipath/configure.c | 1
+ libmultipath/defaults.h | 1
+ libmultipath/devmapper.c | 4 +-
+ libmultipath/dict.c | 87 +++++++++++++++++++++++++++++++++++++++++++++
+ libmultipath/discovery.c | 60 +++++++++++++++++++++++++++++++
+ libmultipath/discovery.h | 1
+ libmultipath/propsel.c | 25 ++++++++++++
+ libmultipath/propsel.h | 1
+ libmultipath/structs.h | 7 +++
+ multipath/multipath.conf.5 | 8 ++++
+ 12 files changed, 200 insertions(+), 1 deletion(-)
+
+Index: multipath-tools-130222/libmultipath/config.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.c
++++ multipath-tools-130222/libmultipath/config.c
+@@ -344,6 +344,7 @@ merge_hwe (struct hwentry * dst, struct
+ merge_num(delay_watch_checks);
+ merge_num(delay_wait_checks);
+ merge_num(skip_kpartx);
++ merge_num(max_sectors_kb);
+
+ /*
+ * Make sure features is consistent with
+@@ -405,6 +406,7 @@ overwrite_hwe (struct hwentry * dst, str
+ overwrite_num(delay_watch_checks);
+ overwrite_num(delay_wait_checks);
+ overwrite_num(skip_kpartx);
++ overwrite_num(max_sectors_kb);
+
+ /*
+ * Make sure features is consistent with
+@@ -682,6 +684,7 @@ load_config (char * file, struct udev *u
+ conf->skip_kpartx = DEFAULT_SKIP_KPARTX;
+ conf->remove_retries = 0;
+ conf->disable_changed_wwids = 0;
++ conf->max_sectors_kb = DEFAULT_MAX_SECTORS_KB;
+
+ /*
+ * preload default hwtable
+Index: multipath-tools-130222/libmultipath/config.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.h
++++ multipath-tools-130222/libmultipath/config.h
+@@ -65,6 +65,7 @@ struct hwentry {
+ int delay_watch_checks;
+ int delay_wait_checks;
+ int skip_kpartx;
++ int max_sectors_kb;
+ char * bl_product;
+ };
+
+@@ -92,6 +93,7 @@ struct mpentry {
+ int delay_watch_checks;
+ int delay_wait_checks;
+ int skip_kpartx;
++ int max_sectors_kb;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+@@ -148,6 +150,7 @@ struct config {
+ int skip_kpartx;
+ int remove_retries;
+ int disable_changed_wwids;
++ int max_sectors_kb;
+ unsigned int version[3];
+
+ char * dev;
+Index: multipath-tools-130222/libmultipath/configure.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.c
++++ multipath-tools-130222/libmultipath/configure.c
+@@ -295,6 +295,7 @@ setup_map (struct multipath * mpp, char
+ select_delay_watch_checks(mpp);
+ select_delay_wait_checks(mpp);
+ select_skip_kpartx(mpp);
++ select_max_sectors_kb(mpp);
+
+ sysfs_set_scsi_tmo(mpp);
+ /*
+Index: multipath-tools-130222/libmultipath/defaults.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/defaults.h
++++ multipath-tools-130222/libmultipath/defaults.h
+@@ -25,6 +25,7 @@
+ #define DEFAULT_RETRIGGER_TRIES 3
+ #define DEFAULT_UEV_WAIT_TIMEOUT 30
+ #define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF
++#define DEFAULT_MAX_SECTORS_KB MAX_SECTORS_KB_UNDEF
+
+ #define DEFAULT_CHECKINT 5
+ #define MAX_CHECKINT(a) (a << 2)
+Index: multipath-tools-130222/libmultipath/dict.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/dict.c
++++ multipath-tools-130222/libmultipath/dict.c
+@@ -976,6 +976,22 @@ def_disable_changed_wwids_handler(vector
+ return 0;
+ }
+
++static int
++def_max_sectors_kb_handler(vector strvec)
++{
++ char * buff;
++
++ buff = set_value(strvec);
++ if (!buff)
++ return 1;
++
++ if ((conf->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN)
++ conf->max_sectors_kb = MAX_SECTORS_KB_UNDEF;
++
++ FREE(buff);
++ return 0;
++}
++
+ /*
+ * blacklist block handlers
+ */
+@@ -1765,6 +1781,26 @@ hw_delay_wait_checks_handler(vector strv
+ return 0;
+ }
+
++static int
++hw_max_sectors_kb_handler(vector strvec)
++{
++ struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
++ char * buff;
++
++ if (!hwe)
++ return 1;
++
++ buff = set_value(strvec);
++ if (!buff)
++ return 1;
++
++ if ((hwe->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN)
++ hwe->max_sectors_kb = MAX_SECTORS_KB_UNDEF;
++
++ FREE(buff);
++ return 0;
++}
++
+ /*
+ * multipaths block handlers
+ */
+@@ -2316,6 +2352,26 @@ mp_delay_wait_checks_handler(vector strv
+ return 0;
+ }
+
++static int
++mp_max_sectors_kb_handler(vector strvec)
++{
++ struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
++ char * buff;
++
++ if (!mpe)
++ return 1;
++
++ buff = set_value(strvec);
++ if (!buff)
++ return 1;
++
++ if ((mpe->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN)
++ mpe->max_sectors_kb = MAX_SECTORS_KB_UNDEF;
++
++ FREE(buff);
++ return 0;
++}
++
+ /*
+ * config file keywords printing
+ */
+@@ -2615,6 +2671,16 @@ snprint_mp_delay_wait_checks(char * buff
+ }
+
+ static int
++snprint_mp_max_sectors_kb(char * buff, int len, void * data)
++{
++ struct mpentry * mpe = (struct mpentry *)data;
++
++ if (mpe->max_sectors_kb == MAX_SECTORS_KB_UNDEF)
++ return 0;
++ return snprintf(buff, len, "%d", mpe->max_sectors_kb);
++}
++
++static int
+ snprint_hw_fast_io_fail(char * buff, int len, void * data)
+ {
+ struct hwentry * hwe = (struct hwentry *)data;
+@@ -2993,6 +3059,16 @@ snprint_detect_prio(char * buff, int len
+ }
+
+ static int
++snprint_hw_max_sectors_kb(char * buff, int len, void * data)
++{
++ struct hwentry * hwe = (struct hwentry *)data;
++
++ if (hwe->max_sectors_kb == MAX_SECTORS_KB_UNDEF)
++ return 0;
++ return snprintf(buff, len, "%d", hwe->max_sectors_kb);
++}
++
++static int
+ snprint_def_polling_interval (char * buff, int len, void * data)
+ {
+ return snprintf(buff, len, "%i", conf->checkint);
+@@ -3461,6 +3537,14 @@ snprint_def_disable_changed_wwids(char *
+ }
+
+ static int
++snprint_def_max_sectors_kb(char * buff, int len, void * data)
++{
++ if (conf->max_sectors_kb == MAX_SECTORS_KB_UNDEF)
++ return 0;
++ return snprintf(buff, len, "%d", conf->max_sectors_kb);
++}
++
++static int
+ snprint_ble_simple (char * buff, int len, void * data)
+ {
+ struct blentry * ble = (struct blentry *)data;
+@@ -3541,6 +3625,7 @@ init_keywords(void)
+ install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot);
+ install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries);
+ install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids);
++ install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb);
+ __deprecated install_keyword("default_selector", &def_selector_handler, NULL);
+ __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
+ __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
+@@ -3609,6 +3694,7 @@ init_keywords(void)
+ install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks);
+ install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks);
+ install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx);
++ install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb);
+ install_sublevel_end();
+
+ install_keyword_root("multipaths", &multipaths_handler);
+@@ -3637,5 +3723,6 @@ init_keywords(void)
+ install_keyword("delay_watch_checks", &mp_delay_watch_checks_handler, &snprint_mp_delay_watch_checks);
+ install_keyword("delay_wait_checks", &mp_delay_wait_checks_handler, &snprint_mp_delay_wait_checks);
+ install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx);
++ install_keyword("max_sectors_kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb);
+ install_sublevel_end();
+ }
+Index: multipath-tools-130222/libmultipath/discovery.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/discovery.c
++++ multipath-tools-130222/libmultipath/discovery.c
+@@ -12,6 +12,7 @@
+ #include <errno.h>
+ #include <libgen.h>
+ #include <libudev.h>
++#include <libdevmapper.h>
+
+ #include "checkers.h"
+ #include "vector.h"
+@@ -27,6 +28,7 @@
+ #include "discovery.h"
+ #include "prio.h"
+ #include "defaults.h"
++#include "devmapper.h"
+
+ int
+ store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice,
+@@ -166,6 +168,64 @@ declare_sysfs_get_str(rev);
+ declare_sysfs_get_str(dev);
+
+ int
++sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
++{
++ struct pathgroup * pgp;
++ struct path *pp;
++ char buff[11];
++ struct udev_device *udevice = NULL;
++ int i, j, len, ret;
++ int max_sectors_kb;
++
++ if (mpp->max_sectors_kb == MAX_SECTORS_KB_UNDEF)
++ return 0;
++ max_sectors_kb = mpp->max_sectors_kb;
++ if (is_reload) {
++ if (!mpp->dmi && dm_get_info(mpp->alias, &mpp->dmi) != 0) {
++ condlog(0, "failed to get dm info on %s to set max_sectors_kb", mpp->alias);
++ return 1;
++ }
++ udevice = udev_device_new_from_devnum(conf->udev, 'b',
++ makedev(mpp->dmi->major,
++ mpp->dmi->minor));
++ if (!udevice) {
++ condlog(0, "failed to get udev device to set max_sectors_kb for %s", mpp->alias);
++ return 1;
++ }
++ if (sysfs_attr_get_value(udevice, "queue/max_sectors_kb",
++ buff, sizeof(buff)) <= 0) {
++ condlog(0, "failed to get current max_sectors_kb from %s", mpp->alias);
++ goto fail_reload;
++ }
++ if (sscanf(buff, "%u\n", &max_sectors_kb) != 1) {
++ condlog(0, "can't parse current max_sectors_kb from %s",
++ mpp->alias);
++ goto fail_reload;
++ }
++ udev_device_unref(udevice);
++ }
++ snprintf(buff, 11, "%d", max_sectors_kb);
++ len = strlen(buff);
++
++ vector_foreach_slot (mpp->pg, pgp, i) {
++ vector_foreach_slot (pgp->paths, pp, j) {
++ ret = sysfs_attr_set_value(pp->udev,
++ "queue/max_sectors_kb",
++ buff, len);
++ if (ret < 0) {
++ condlog(0, "failed setting max_sectors_kb on %s : %s", pp->dev, strerror(-ret));
++ return 1;
++ }
++ }
++ }
++ return 0;
++
++fail_reload:
++ udev_device_unref(udevice);
++ return 1;
++}
++
++int
+ sysfs_get_timeout(struct path *pp, unsigned int *timeout)
+ {
+ const char *attr = NULL;
+Index: multipath-tools-130222/libmultipath/discovery.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/discovery.h
++++ multipath-tools-130222/libmultipath/discovery.h
+@@ -41,6 +41,7 @@ int store_pathinfo (vector pathvec, vect
+ struct udev_device *udevice, int flag,
+ struct path **pp_ptr);
+ int sysfs_set_scsi_tmo (struct multipath *mpp);
++int sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload);
+ int sysfs_get_timeout(struct path *pp, unsigned int *timeout);
+ int sysfs_get_host_pci_name(struct path *pp, char *pci_name);
+ int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address);
+Index: multipath-tools-130222/libmultipath/propsel.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/propsel.c
++++ multipath-tools-130222/libmultipath/propsel.c
+@@ -880,3 +880,28 @@ select_skip_kpartx (struct multipath * m
+ condlog(3, "skip_kpartx = DISABLED (internal default)");
+ return 0;
+ }
++
++extern int
++select_max_sectors_kb (struct multipath * mp)
++{
++ if (mp->mpe && mp->mpe->max_sectors_kb != MAX_SECTORS_KB_UNDEF) {
++ mp->max_sectors_kb = mp->mpe->max_sectors_kb;
++ condlog(3, "max_sectors_kb = %i (multipath setting)",
++ mp->max_sectors_kb);
++ return 0;
++ }
++ if (mp->hwe && mp->hwe->max_sectors_kb != MAX_SECTORS_KB_UNDEF) {
++ mp->max_sectors_kb = mp->hwe->max_sectors_kb;
++ condlog(3, "max_sectors_kb = %i (controler setting)",
++ mp->max_sectors_kb);
++ return 0;
++ }
++ if (conf->max_sectors_kb != MAX_SECTORS_KB_UNDEF) {
++ mp->max_sectors_kb = conf->max_sectors_kb;
++ condlog(3, "max_sectors_kb = %i (config file default)",
++ mp->max_sectors_kb);
++ return 0;
++ }
++ mp->max_sectors_kb = MAX_SECTORS_KB_UNDEF;
++ return 0;
++}
+Index: multipath-tools-130222/libmultipath/propsel.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/propsel.h
++++ multipath-tools-130222/libmultipath/propsel.h
+@@ -24,3 +24,4 @@ int select_deferred_remove(struct multip
+ int select_delay_watch_checks (struct multipath * mp);
+ int select_delay_wait_checks (struct multipath * mp);
+ int select_skip_kpartx (struct multipath * mp);
++int select_max_sectors_kb (struct multipath * mp);
+Index: multipath-tools-130222/libmultipath/structs.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/structs.h
++++ multipath-tools-130222/libmultipath/structs.h
+@@ -128,6 +128,12 @@ enum skip_kpartx_states {
+ SKIP_KPARTX_ON,
+ };
+
++
++enum max_sectors_kb_states {
++ MAX_SECTORS_KB_UNDEF = 0,
++ MAX_SECTORS_KB_MIN = 4, /* can't be smaller than page size */
++};
++
+ enum scsi_protocol {
+ SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */
+ SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */
+@@ -245,6 +251,7 @@ struct multipath {
+ int delay_wait_checks;
+ int force_udev_reload;
+ int skip_kpartx;
++ int max_sectors_kb;
+ unsigned int dev_loss;
+ uid_t uid;
+ gid_t gid;
+Index: multipath-tools-130222/multipath/multipath.conf.5
+===================================================================
+--- multipath-tools-130222.orig/multipath/multipath.conf.5
++++ multipath-tools-130222/multipath/multipath.conf.5
+@@ -561,6 +561,10 @@ default is
+ This sets how may times multipath will retry removing a device that is in-use.
+ Between each attempt, multipath will sleep 1 second. The default is
+ .I 0
++.TP
++.B max_sectors_kb
++Sets the max_sectors_kb device parameter on all path devices and the multipath
++device to the specified value. Default is device dependent.
+ .
+ .SH "blacklist section"
+ The
+@@ -672,6 +676,8 @@ section:
+ .B delay_wait_checks
+ .TP
+ .B skip_kpartx
++.TP
++.B max_sectors_kb
+ .RE
+ .PD
+ .LP
+@@ -772,6 +778,8 @@ section:
+ .B delay_wait_checks
+ .TP
+ .B skip_kpartx
++.TP
++.B max_sectors_kb
+ .RE
+ .PD
+ .LP
+Index: multipath-tools-130222/libmultipath/devmapper.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/devmapper.c
++++ multipath-tools-130222/libmultipath/devmapper.c
+@@ -21,7 +21,7 @@
+ #include "devmapper.h"
+ #include "config.h"
+ #include "sysfs.h"
+-
++#include "discovery.h"
+ #include "log_pthread.h"
+ #include <sys/types.h>
+ #include <time.h>
+@@ -330,6 +330,7 @@ extern int
+ dm_addmap_create (struct multipath *mpp, char * params) {
+ int ro;
+
++ sysfs_set_max_sectors_kb(mpp, 0);
+ for (ro = 0; ro <= 1; ro++) {
+ int err;
+
+@@ -356,6 +357,7 @@ dm_addmap_create (struct multipath *mpp,
+
+ extern int
+ dm_addmap_reload (struct multipath *mpp, char *params) {
++ sysfs_set_max_sectors_kb(mpp, 1);
+ if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW, SKIP_KPARTX_OFF))
+ return 1;
+ if (errno != EROFS)
diff --git a/0198-RHBZ-1372032-detect-path-checker.patch b/0198-RHBZ-1372032-detect-path-checker.patch
new file mode 100644
index 0000000..48fcfce
--- /dev/null
+++ b/0198-RHBZ-1372032-detect-path-checker.patch
@@ -0,0 +1,377 @@
+---
+ libmultipath/config.c | 4 ++
+ libmultipath/config.h | 2 +
+ libmultipath/defaults.h | 1
+ libmultipath/dict.c | 74 +++++++++++++++++++++++++++++++++++++++++++++
+ libmultipath/discovery.c | 1
+ libmultipath/hwtable.c | 1
+ libmultipath/propsel.c | 65 +++++++++++++++++++++++++++++++--------
+ libmultipath/propsel.h | 1
+ libmultipath/structs.h | 7 ++++
+ multipath/multipath.conf.5 | 9 +++++
+ 10 files changed, 152 insertions(+), 13 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/config.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.c
++++ multipath-tools-130222/libmultipath/config.c
+@@ -340,6 +340,7 @@ merge_hwe (struct hwentry * dst, struct
+ merge_num(user_friendly_names);
+ merge_num(retain_hwhandler);
+ merge_num(detect_prio);
++ merge_num(detect_checker);
+ merge_num(deferred_remove);
+ merge_num(delay_watch_checks);
+ merge_num(delay_wait_checks);
+@@ -402,6 +403,7 @@ overwrite_hwe (struct hwentry * dst, str
+ overwrite_num(user_friendly_names);
+ overwrite_num(retain_hwhandler);
+ overwrite_num(detect_prio);
++ overwrite_num(detect_checker);
+ overwrite_num(deferred_remove);
+ overwrite_num(delay_watch_checks);
+ overwrite_num(delay_wait_checks);
+@@ -476,6 +478,7 @@ store_hwe (vector hwtable, struct hwentr
+ hwe->user_friendly_names = dhwe->user_friendly_names;
+ hwe->retain_hwhandler = dhwe->retain_hwhandler;
+ hwe->detect_prio = dhwe->detect_prio;
++ hwe->detect_checker = dhwe->detect_checker;
+
+ if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product)))
+ goto out;
+@@ -672,6 +675,7 @@ load_config (char * file, struct udev *u
+ conf->fast_io_fail = DEFAULT_FAST_IO_FAIL;
+ conf->retain_hwhandler = DEFAULT_RETAIN_HWHANDLER;
+ conf->detect_prio = DEFAULT_DETECT_PRIO;
++ conf->detect_checker = DEFAULT_DETECT_CHECKER;
+ conf->deferred_remove = DEFAULT_DEFERRED_REMOVE;
+ conf->hw_strmatch = 0;
+ conf->force_sync = 0;
+Index: multipath-tools-130222/libmultipath/config.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.h
++++ multipath-tools-130222/libmultipath/config.h
+@@ -61,6 +61,7 @@ struct hwentry {
+ int user_friendly_names;
+ int retain_hwhandler;
+ int detect_prio;
++ int detect_checker;
+ int deferred_remove;
+ int delay_watch_checks;
+ int delay_wait_checks;
+@@ -136,6 +137,7 @@ struct config {
+ int reassign_maps;
+ int retain_hwhandler;
+ int detect_prio;
++ int detect_checker;
+ int force_sync;
+ int deferred_remove;
+ int ignore_new_boot_devs;
+Index: multipath-tools-130222/libmultipath/defaults.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/defaults.h
++++ multipath-tools-130222/libmultipath/defaults.h
+@@ -19,6 +19,7 @@
+ #define DEFAULT_FAST_IO_FAIL 5
+ #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF
+ #define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF
++#define DEFAULT_DETECT_CHECKER DETECT_CHECKER_OFF
+ #define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF
+ #define DEFAULT_DELAY_CHECKS DELAY_CHECKS_OFF
+ #define DEFAULT_RETRIGGER_DELAY 10
+Index: multipath-tools-130222/libmultipath/dict.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/dict.c
++++ multipath-tools-130222/libmultipath/dict.c
+@@ -714,6 +714,29 @@ def_detect_prio_handler(vector strvec)
+ }
+
+ static int
++def_detect_checker_handler(vector strvec)
++{
++ char * buff;
++
++ buff = set_value(strvec);
++
++ if (!buff)
++ return 1;
++
++ if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
++ (strlen(buff) == 1 && !strcmp(buff, "0")))
++ conf->detect_checker = DETECT_CHECKER_OFF;
++ else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
++ (strlen(buff) == 1 && !strcmp(buff, "1")))
++ conf->detect_checker = DETECT_CHECKER_ON;
++ else
++ conf->detect_checker = DETECT_CHECKER_UNDEF;
++
++ FREE(buff);
++ return 0;
++}
++
++static int
+ def_hw_strmatch_handler(vector strvec)
+ {
+ char *buff;
+@@ -1682,6 +1705,33 @@ hw_detect_prio_handler(vector strvec)
+ }
+
+ static int
++hw_detect_checker_handler(vector strvec)
++{
++ struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
++ char * buff;
++
++ if (!hwe)
++ return 1;
++
++ buff = set_value(strvec);
++
++ if (!buff)
++ return 1;
++
++ if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
++ (strlen(buff) == 1 && !strcmp(buff, "0")))
++ hwe->detect_checker = DETECT_CHECKER_OFF;
++ else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
++ (strlen(buff) == 1 && !strcmp(buff, "1")))
++ hwe->detect_checker = DETECT_CHECKER_ON;
++ else
++ hwe->detect_checker = DETECT_CHECKER_UNDEF;
++
++ FREE(buff);
++ return 0;
++}
++
++static int
+ hw_deferred_remove_handler(vector strvec)
+ {
+ struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
+@@ -3059,6 +3109,19 @@ snprint_detect_prio(char * buff, int len
+ }
+
+ static int
++snprint_detect_checker(char * buff, int len, void * data)
++{
++ struct hwentry * hwe = (struct hwentry *)data;
++
++ if (hwe->detect_checker == DETECT_CHECKER_ON)
++ return snprintf(buff, len, "yes");
++ else if (hwe->detect_checker == DETECT_CHECKER_OFF)
++ return snprintf(buff, len, "no");
++ else
++ return 0;
++}
++
++static int
+ snprint_hw_max_sectors_kb(char * buff, int len, void * data)
+ {
+ struct hwentry * hwe = (struct hwentry *)data;
+@@ -3424,6 +3487,15 @@ snprint_def_detect_prio(char * buff, int
+ }
+
+ static int
++snprint_def_detect_checker(char * buff, int len, void * data)
++{
++ if (conf->detect_checker == DETECT_PRIO_ON)
++ return snprintf(buff, len, "yes");
++ else
++ return snprintf(buff, len, "no");
++}
++
++static int
+ snprint_def_hw_strmatch(char * buff, int len, void * data)
+ {
+ if (conf->hw_strmatch)
+@@ -3611,6 +3683,7 @@ init_keywords(void)
+ install_keyword("find_multipaths", &def_find_multipaths_handler, &snprint_def_find_multipaths);
+ install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler_handler);
+ install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio);
++ install_keyword("detect_path_checker", &def_detect_checker_handler, &snprint_def_detect_checker);
+ install_keyword("hw_str_match", &def_hw_strmatch_handler, &snprint_def_hw_strmatch);
+ install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync);
+ install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove);
+@@ -3690,6 +3763,7 @@ init_keywords(void)
+ install_keyword("user_friendly_names", &hw_names_handler, &snprint_hw_user_friendly_names);
+ install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler_handler);
+ install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_detect_prio);
++ install_keyword("detect_path_checker", &hw_detect_checker_handler, &snprint_detect_checker);
+ install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove);
+ install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks);
+ install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks);
+Index: multipath-tools-130222/libmultipath/discovery.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/discovery.c
++++ multipath-tools-130222/libmultipath/discovery.c
+@@ -1107,6 +1107,7 @@ get_state (struct path * pp, int daemon)
+ return PATH_UNCHECKED;
+ }
+ }
++ select_detect_checker(pp);
+ select_checker(pp);
+ if (!checker_selected(c)) {
+ condlog(3, "%s: No checker selected", pp->dev);
+Index: multipath-tools-130222/libmultipath/hwtable.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/hwtable.c
++++ multipath-tools-130222/libmultipath/hwtable.c
+@@ -289,6 +289,7 @@ static struct hwentry default_hw[] = {
+ .prio_args = NULL,
+ .retain_hwhandler = RETAIN_HWHANDLER_ON,
+ .detect_prio = DETECT_PRIO_ON,
++ .detect_checker = DETECT_CHECKER_ON,
+ },
+ {
+ .vendor = "EMC",
+Index: multipath-tools-130222/libmultipath/propsel.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/propsel.c
++++ multipath-tools-130222/libmultipath/propsel.c
+@@ -335,11 +335,43 @@ select_hwhandler (struct multipath * mp)
+ return 0;
+ }
+
++int
++detect_alua(struct path * pp)
++{
++ int ret;
++ int tpgs = 0;
++
++ if ((tpgs = get_target_port_group_support(pp->fd)) <= 0)
++ return 0;
++ pp->tpgs = tpgs;
++ ret = get_target_port_group(pp->fd, NULL);
++ if (ret < 0)
++ return 0;
++ if (get_asymmetric_access_state(pp->fd, ret, NULL) < 0)
++ return 0;
++ return 1;
++}
++
++void
++detect_checker(struct path * pp)
++{
++ if (detect_alua(pp))
++ checker_get(&pp->checker, TUR);
++}
++
+ extern int
+ select_checker(struct path *pp)
+ {
+ struct checker * c = &pp->checker;
+
++ if (pp->detect_checker == DETECT_CHECKER_ON) {
++ detect_checker(pp);
++ if (checker_selected(c)) {
++ condlog(3, "%s: path checker = %s (detected setting)",
++ pp->dev, checker_name(c));
++ goto out;
++ }
++ }
+ if (pp->hwe && pp->hwe->checker_name) {
+ checker_get(c, pp->hwe->checker_name);
+ condlog(3, "%s: path checker = %s (controller setting)",
+@@ -396,19 +428,8 @@ select_getuid (struct path * pp)
+ void
+ detect_prio(struct path * pp)
+ {
+- int ret;
+- struct prio *p = &pp->prio;
+- int tpgs = 0;
+-
+- if ((tpgs = get_target_port_group_support(pp->fd)) <= 0)
+- return;
+- pp->tpgs = tpgs;
+- ret = get_target_port_group(pp->fd, NULL);
+- if (ret < 0)
+- return;
+- if (get_asymmetric_access_state(pp->fd, ret, NULL) < 0)
+- return;
+- prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS);
++ if (detect_alua(pp))
++ prio_get(&pp->prio, PRIO_ALUA, DEFAULT_PRIO_ARGS);
+ }
+
+ extern int
+@@ -803,6 +824,24 @@ select_detect_prio (struct path * pp)
+ return 0;
+ }
+
++extern int
++select_detect_checker (struct path * pp)
++{
++ if (pp->hwe && pp->hwe->detect_checker) {
++ pp->detect_checker = pp->hwe->detect_checker;
++ condlog(3, "%s: detect_checker = %d (controller default)", pp->dev, pp->detect_checker);
++ return 0;
++ }
++ if (conf->detect_checker) {
++ pp->detect_checker = conf->detect_checker;
++ condlog(3, "%s: detect_checker = %d (config file default)", pp->dev, pp->detect_checker);
++ return 0;
++ }
++ pp->detect_checker = DEFAULT_DETECT_CHECKER;
++ condlog(3, "%s: detect_checker = %d (compiled in default)", pp->dev, pp->detect_checker);
++ return 0;
++}
++
+ extern int
+ select_delay_watch_checks (struct multipath * mp)
+ {
+Index: multipath-tools-130222/libmultipath/propsel.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/propsel.h
++++ multipath-tools-130222/libmultipath/propsel.h
+@@ -20,6 +20,7 @@ int select_dev_loss(struct multipath *mp
+ int select_reservation_key(struct multipath *mp);
+ int select_retain_hwhandler (struct multipath * mp);
+ int select_detect_prio(struct path * pp);
++int select_detect_checker(struct path * pp);
+ int select_deferred_remove(struct multipath *mp);
+ int select_delay_watch_checks (struct multipath * mp);
+ int select_delay_wait_checks (struct multipath * mp);
+Index: multipath-tools-130222/libmultipath/structs.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/structs.h
++++ multipath-tools-130222/libmultipath/structs.h
+@@ -115,6 +115,12 @@ enum detect_prio_states {
+ DETECT_PRIO_ON,
+ };
+
++enum detect_checker_states {
++ DETECT_CHECKER_UNDEF,
++ DETECT_CHECKER_OFF,
++ DETECT_CHECKER_ON,
++};
++
+ enum deferred_remove_states {
+ DEFERRED_REMOVE_UNDEF,
+ DEFERRED_REMOVE_OFF,
+@@ -204,6 +210,7 @@ struct path {
+ int priority;
+ int pgindex;
+ int detect_prio;
++ int detect_checker;
+ int watch_checks;
+ int wait_checks;
+ int tpgs;
+Index: multipath-tools-130222/multipath/multipath.conf.5
+===================================================================
+--- multipath-tools-130222.orig/multipath/multipath.conf.5
++++ multipath-tools-130222/multipath/multipath.conf.5
+@@ -448,6 +448,15 @@ will automatically use the
+ prioritizer. If not, the prioritizer will be selected as usual. Default is
+ .I no
+ .TP
++.B detect_checker
++If set to
++.I yes
++, multipath will try to detect if the device supports ALUA. If so, the device
++will automatically use the
++.I tur
++checker. If not, the prioritizer will be selected as ususal. Default is
++.I no
++.TP
+ .B hw_str_match
+ If set to
+ .I yes
diff --git a/0199-RHBZ-1279355-3pardata-config.patch b/0199-RHBZ-1279355-3pardata-config.patch
new file mode 100644
index 0000000..037de3d
--- /dev/null
+++ b/0199-RHBZ-1279355-3pardata-config.patch
@@ -0,0 +1,17 @@
+---
+ libmultipath/hwtable.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: multipath-tools-130222/libmultipath/hwtable.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/hwtable.c
++++ multipath-tools-130222/libmultipath/hwtable.c
+@@ -69,7 +69,7 @@ static struct hwentry default_hw[] = {
+ .pgpolicy = MULTIBUS,
+ .pgfailback = FAILBACK_UNDEF,
+ .rr_weight = RR_WEIGHT_NONE,
+- .no_path_retry = NO_PATH_RETRY_UNDEF,
++ .no_path_retry = 12,
+ .checker_name = DEFAULT_CHECKER,
+ .prio_name = DEFAULT_PRIO,
+ .prio_args = NULL,
diff --git a/0200-RHBZ-1402092-orphan-status.patch b/0200-RHBZ-1402092-orphan-status.patch
new file mode 100644
index 0000000..67830ca
--- /dev/null
+++ b/0200-RHBZ-1402092-orphan-status.patch
@@ -0,0 +1,29 @@
+---
+ libmultipath/print.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+Index: multipath-tools-130222/libmultipath/print.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/print.c
++++ multipath-tools-130222/libmultipath/print.c
+@@ -386,7 +386,9 @@ snprint_dev_t (char * buff, size_t len,
+ static int
+ snprint_offline (char * buff, size_t len, struct path * pp)
+ {
+- if (pp->offline)
++ if (!pp || !pp->mpp)
++ return snprintf(buff, len, "unknown");
++ else if (pp->offline)
+ return snprintf(buff, len, "offline");
+ else
+ return snprintf(buff, len, "running");
+@@ -395,6 +397,9 @@ snprint_offline (char * buff, size_t len
+ static int
+ snprint_chk_state (char * buff, size_t len, struct path * pp)
+ {
++ if (!pp || !pp->mpp)
++ return snprintf(buff, len, "undef");
++
+ switch (pp->state) {
+ case PATH_UP:
+ return snprintf(buff, len, "ready");
diff --git a/0201-RHBZ-1403552-silence-warning.patch b/0201-RHBZ-1403552-silence-warning.patch
new file mode 100644
index 0000000..c816ea0
--- /dev/null
+++ b/0201-RHBZ-1403552-silence-warning.patch
@@ -0,0 +1,59 @@
+---
+ libmultipath/discovery.c | 15 +++++++++++----
+ multipathd/main.c | 10 ++++++++++
+ 2 files changed, 21 insertions(+), 4 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/discovery.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/discovery.c
++++ multipath-tools-130222/libmultipath/discovery.c
+@@ -84,10 +84,6 @@ path_discover (vector pathvec, struct co
+ if (!devname)
+ return PATHINFO_FAILED;
+
+- if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
+- (char *)devname) > 0)
+- return PATHINFO_SKIPPED;
+-
+ pp = find_path_by_dev(pathvec, (char *)devname);
+ if (!pp) {
+ return store_pathinfo(pathvec, conf->hwtable,
+@@ -1286,6 +1282,17 @@ pathinfo (struct path *pp, vector hwtabl
+ if (!pp)
+ return PATHINFO_FAILED;
+
++ /*
++ * For behavior backward-compatibility with multipathd,
++ * the blacklisting by filter_devnode() is not
++ * limited by DI_BLACKLIST and occurs before this debug
++ * message with the mask value.
++ */
++ if (filter_devnode(conf->blist_devnode,
++ conf->elist_devnode,
++ pp->dev) > 0)
++ return PATHINFO_SKIPPED;
++
+ condlog(3, "%s: mask = 0x%x", pp->dev, mask);
+
+ /*
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -776,6 +776,16 @@ uev_update_path (struct uevent *uev, str
+
+ pp = find_path_by_dev(vecs->pathvec, uev->kernel);
+ if (!pp) {
++ /* If the path is blacklisted, print a debug/non-default verbosity message. */
++ if (uev->udev) {
++ int flag = DI_SYSFS | DI_WWID;
++
++ if (store_pathinfo(NULL, conf->hwtable, uev->udev, flag, NULL) == PATHINFO_SKIPPED) {
++ condlog(3, "%s: spurious uevent, path is blacklisted", uev->kernel);
++ return 0;
++ }
++ }
++
+ condlog(0, "%s: spurious uevent, path not found",
+ uev->kernel);
+ return 1;
diff --git a/0202-RHBZ-1362120-skip-prio.patch b/0202-RHBZ-1362120-skip-prio.patch
new file mode 100644
index 0000000..8a187ad
--- /dev/null
+++ b/0202-RHBZ-1362120-skip-prio.patch
@@ -0,0 +1,18 @@
+---
+ multipathd/main.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -1248,7 +1248,8 @@ int update_prio(struct path *pp, int ref
+ return changed;
+ }
+ oldpriority = pp->priority;
+- pathinfo(pp, conf->hwtable, DI_PRIO);
++ if (pp->state != PATH_DOWN)
++ pathinfo(pp, conf->hwtable, DI_PRIO);
+
+ if (pp->priority == oldpriority)
+ return 0;
diff --git a/0203-RHBZ-1363718-add-msgs.patch b/0203-RHBZ-1363718-add-msgs.patch
new file mode 100644
index 0000000..9eaadc0
--- /dev/null
+++ b/0203-RHBZ-1363718-add-msgs.patch
@@ -0,0 +1,24 @@
+---
+ multipathd/main.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -337,6 +337,7 @@ ev_add_map (char * dev, char * alias, st
+
+ if (mpp) {
+ if (mpp->wait_for_udev > 1) {
++ condlog(2, "%s: performing delayed actions", mpp->alias);
+ if (update_map(mpp, vecs))
+ /* setup multipathd removed the map */
+ return 1;
+@@ -535,6 +536,7 @@ ev_add_path (struct path * pp, struct ve
+ pp->tpgs == TPGS_IMPLICIT))
+ mpp->force_udev_reload = 1;
+ else {
++ condlog(2, "%s : delaying path addition until %s is fully initialized", pp->dev, mpp->alias);
+ mpp->wait_for_udev = 2;
+ orphan_path(pp);
+ return 0;
diff --git a/0204-RHBZ-1406226-nimble-config.patch b/0204-RHBZ-1406226-nimble-config.patch
new file mode 100644
index 0000000..c3f2e90
--- /dev/null
+++ b/0204-RHBZ-1406226-nimble-config.patch
@@ -0,0 +1,28 @@
+---
+ libmultipath/hwtable.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+Index: multipath-tools-130222/libmultipath/hwtable.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/hwtable.c
++++ multipath-tools-130222/libmultipath/hwtable.c
+@@ -1189,6 +1189,19 @@ static struct hwentry default_hw[] = {
+ .dev_loss = 30,
+ },
+ {
++ .vendor = "Nimble",
++ .product = "Server",
++ .features = "1 queue_if_no_path",
++ .hwhandler = "1 alua",
++ .pgpolicy = GROUP_BY_PRIO,
++ .prio_name = PRIO_ALUA,
++ .prio_args = NULL,
++ .pgfailback = -FAILBACK_IMMEDIATE,
++ .selector = "round-robin 0",
++ .dev_loss = MAX_DEV_LOSS_TMO,
++ .fast_io_fail = 1,
++ },
++ {
+ .vendor = "XtremIO",
+ .product = "XtremApp",
+ .features = DEFAULT_FEATURES,
diff --git a/0205-RHBZ-1416569-reset-stats.patch b/0205-RHBZ-1416569-reset-stats.patch
new file mode 100644
index 0000000..bb414ba
--- /dev/null
+++ b/0205-RHBZ-1416569-reset-stats.patch
@@ -0,0 +1,108 @@
+---
+ multipathd/cli.c | 2 ++
+ multipathd/cli_handlers.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
+ multipathd/cli_handlers.h | 2 ++
+ multipathd/main.c | 2 ++
+ 4 files changed, 50 insertions(+)
+
+Index: multipath-tools-130222/multipathd/cli.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/cli.c
++++ multipath-tools-130222/multipathd/cli.c
+@@ -482,6 +482,8 @@ cli_init (void) {
+ add_handler(LIST+BLACKLIST, NULL);
+ add_handler(LIST+DEVICES, NULL);
+ add_handler(LIST+WILDCARDS, NULL);
++ add_handler(RESET+MAPS+STATS, NULL);
++ add_handler(RESET+MAP+STATS, NULL);
+ add_handler(ADD+PATH, NULL);
+ add_handler(DEL+PATH, NULL);
+ add_handler(ADD+MAP, NULL);
+Index: multipath-tools-130222/multipathd/cli_handlers.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/cli_handlers.c
++++ multipath-tools-130222/multipathd/cli_handlers.c
+@@ -233,6 +233,17 @@ show_config (char ** r, int * len)
+ return 0;
+ }
+
++void
++reset_stats(struct multipath * mpp)
++{
++ mpp->stat_switchgroup = 0;
++ mpp->stat_path_failures = 0;
++ mpp->stat_map_loads = 0;
++ mpp->stat_total_queueing_time = 0;
++ mpp->stat_queueing_timeouts = 0;
++ mpp->stat_map_failures = 0;
++}
++
+ int
+ cli_list_config (void * v, char ** reply, int * len, void * data)
+ {
+@@ -501,6 +512,39 @@ cli_list_daemon (void * v, char ** reply
+ }
+
+ int
++cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
++{
++ struct vectors * vecs = (struct vectors *)data;
++ int i;
++ struct multipath * mpp;
++
++ condlog(3, "reset multipaths stats (operator)");
++
++ vector_foreach_slot(vecs->mpvec, mpp, i) {
++ reset_stats(mpp);
++ }
++ return 0;
++}
++
++int
++cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
++{
++ struct vectors * vecs = (struct vectors *)data;
++ struct multipath * mpp;
++ char * param = get_keyparam(v, MAP);
++
++ param = convert_dev(param, 0);
++ mpp = find_mp_by_str(vecs->mpvec, param);
++
++ if (!mpp)
++ return 1;
++
++ condlog(3, "reset multipath %s stats (operator)", param);
++ reset_stats(mpp);
++ return 0;
++}
++
++int
+ cli_add_path (void * v, char ** reply, int * len, void * data)
+ {
+ struct vectors * vecs = (struct vectors *)data;
+Index: multipath-tools-130222/multipathd/cli_handlers.h
+===================================================================
+--- multipath-tools-130222.orig/multipathd/cli_handlers.h
++++ multipath-tools-130222/multipathd/cli_handlers.h
+@@ -16,6 +16,8 @@ int cli_list_config (void * v, char ** r
+ int cli_list_blacklist (void * v, char ** reply, int * len, void * data);
+ int cli_list_devices (void * v, char ** reply, int * len, void * data);
+ int cli_list_wildcards (void * v, char ** reply, int * len, void * data);
++int cli_reset_maps_stats (void * v, char ** reply, int * len, void * data);
++int cli_reset_map_stats (void * v, char ** reply, int * len, void * data);
+ int cli_add_path (void * v, char ** reply, int * len, void * data);
+ int cli_del_path (void * v, char ** reply, int * len, void * data);
+ int cli_add_map (void * v, char ** reply, int * len, void * data);
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -1011,6 +1011,8 @@ uxlsnrloop (void * ap)
+ set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
+ set_handler_callback(LIST+DEVICES, cli_list_devices);
+ set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
++ set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats);
++ set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats);
+ set_handler_callback(ADD+PATH, cli_add_path);
+ set_handler_callback(DEL+PATH, cli_del_path);
+ set_handler_callback(ADD+MAP, cli_add_map);
diff --git a/0206-RHBZ-1239173-pt2-no-paths.patch b/0206-RHBZ-1239173-pt2-no-paths.patch
new file mode 100644
index 0000000..c38be56
--- /dev/null
+++ b/0206-RHBZ-1239173-pt2-no-paths.patch
@@ -0,0 +1,121 @@
+---
+ libmultipath/configure.c | 2 -
+ libmultipath/devmapper.h | 6 ++++
+ multipath/11-dm-mpath.rules | 61 +++++++++++++++++++++++++++++++++-----------
+ 3 files changed, 54 insertions(+), 15 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/configure.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.c
++++ multipath-tools-130222/libmultipath/configure.c
+@@ -615,7 +615,7 @@ extern int
+ domap (struct multipath * mpp, char * params)
+ {
+ int r = 0;
+- uint16_t udev_flags = ((mpp->force_udev_reload)? 0 : MPATH_UDEV_RELOAD_FLAG) | ((mpp->skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0);
++ uint16_t udev_flags = ((mpp->force_udev_reload)? 0 : MPATH_UDEV_RELOAD_FLAG) | ((mpp->skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0) | ((mpp->nr_active)? 0 : MPATH_UDEV_NO_PATHS_FLAG);
+
+ /*
+ * last chance to quit before touching the devmaps
+Index: multipath-tools-130222/libmultipath/devmapper.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/devmapper.h
++++ multipath-tools-130222/libmultipath/devmapper.h
+@@ -18,6 +18,12 @@
+ #define MPATH_UDEV_NO_KPARTX_FLAG 0
+ #endif
+
++#ifdef DM_SUBSYSTEM_UDEV_FLAG2
++#define MPATH_UDEV_NO_PATHS_FLAG DM_SUBSYSTEM_UDEV_FLAG2
++#else
++#define MPATH_UDEV_NO_PATHS_FLAG 0
++#endif
++
+ void dm_init(void);
+ int dm_prereq (void);
+ int dm_drv_version (unsigned int * version, char * str);
+Index: multipath-tools-130222/multipath/11-dm-mpath.rules
+===================================================================
+--- multipath-tools-130222.orig/multipath/11-dm-mpath.rules
++++ multipath-tools-130222/multipath/11-dm-mpath.rules
+@@ -2,33 +2,66 @@ ACTION!="add|change", GOTO="mpath_end"
+ ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="mpath_end"
+ ENV{DM_UUID}!="mpath-?*", GOTO="mpath_end"
+
++IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD"
++IMPORT{db}="MPATH_DEVICE_READY"
++
++# If this uevent didn't come from dm, don't try to update the
++# device state
++ENV{DM_COOKIE}!="?*", ENV{DM_ACTION}!="PATH_*", IMPORT{db}="DM_UDEV_DISABLE_OTHER_RULES_FLAG", IMPORT{db}="DM_NOSCAN", GOTO="scan_import"
++
++ENV{.MPATH_DEVICE_READY_OLD}="$env{MPATH_DEVICE_READY}"
++
++# multipath sets DM_SUBSYSTEM_UDEV_FLAG2 when it reloads a
++# table with no active devices. If this happens, mark the
++# device not ready
++ENV{DM_SUBSYSTEM_UDEV_FLAG2}=="1", ENV{MPATH_DEVICE_READY}="0",\
++ GOTO="mpath_action"
++
++# If the last path has failed mark the device not ready
++ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0",\
++ ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
++
++# Don't mark a device ready on a PATH_FAILED event. even if
++# DM_NR_VALID_PATHS is greater than 0. Just keep the existing
++# value
++ENV{DM_ACTION}=="PATH_FAILED", GOTO="mpath_action"
++
++# This event is either a PATH_REINSTATED or a table reload where
++# there are active paths. Mark the device ready
++ENV{MPATH_DEVICE_READY}=""
++
++LABEL="mpath_action"
++# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem.
++# Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its
++# paths are lost/recovered. For any stack above the mpath device, this is not
++# something that should be reacted upon since it would be useless extra work.
++# It's exactly mpath's job to provide *seamless* device access to any of the
++# paths that are available underneath.
++ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0"
++
+ # Do not initiate scanning if no path is available,
+ # otherwise there would be a hang or IO error on access.
+ # We'd like to avoid this, especially within udev processing.
+-ENV{DM_NR_VALID_PATHS}!="?*", IMPORT{db}="DM_NR_VALID_PATHS"
+-ENV{DM_NR_VALID_PATHS}=="0", ENV{DM_NOSCAN}="1"
++ENV{MPATH_DEVICE_READY}=="0", ENV{DM_NOSCAN}="1"
+
+ # Also skip all foreign rules if no path is available.
+ # Remember the original value of DM_DISABLE_OTHER_RULES_FLAG
+ # and restore it back once we have at least one path available.
+-IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD"
+-ENV{DM_ACTION}=="PATH_FAILED",\
+- ENV{DM_NR_VALID_PATHS}=="0",\
++ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}!="0",\
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}",\
+ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+-ENV{DM_ACTION}=="PATH_REINSTATED",\
+- ENV{DM_NR_VALID_PATHS}=="1",\
++ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\
+ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\
+ ENV{DM_ACTIVATION}="1"
+
+-# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem.
+-# Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its
+-# paths are lost/recovered. For any stack above the mpath device, this is not
+-# something that should be reacted upon since it would be useless extra work.
+-# It's exactly mpath's job to provide *seamless* device access to any of the
+-# paths that are available underneath.
+-ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0"
++LABEL="scan_import"
++ENV{DM_NOSCAN}!="1", GOTO="mpath_end"
++ENV{ID_FS_TYPE}!="?*", IMPORT{db}="ID_FS_TYPE"
++ENV{ID_FS_USAGE}!="?*", IMPORT{db}="ID_FS_USAGE"
++ENV{ID_FS_UUID}!="?*", IMPORT{db}="ID_FS_UUID"
++ENV{ID_FS_ENC}!="?*", IMPORT{db}="ID_FS_UUID_ENC"
++ENV{ID_FS_VERSION}!="?*", IMPORT{db}="ID_FS_VERSION"
+
+ LABEL="mpath_end"
diff --git a/0207-UP-add-libmpathcmd.patch b/0207-UP-add-libmpathcmd.patch
new file mode 100644
index 0000000..ee19d65
--- /dev/null
+++ b/0207-UP-add-libmpathcmd.patch
@@ -0,0 +1,840 @@
+From c146b5840bbd7ad89c8a8de6192590ad0595a977 Mon Sep 17 00:00:00 2001
+From: Benjamin Marzinski <bmarzins@redhat.com>
+Date: Thu, 7 Apr 2016 18:19:58 -0500
+Subject: [PATCH] Add libmpathcmd library and use it internally
+
+Other programs would like to communicate with multipathd to issue
+command or check status. Instead of having them exec multipathd,
+I've pulled the code that sends commands and receives replies from
+multipathd into its own library. I've made the multipath tools use
+this library internally as well.
+
+Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
+---
+ Makefile | 1
+ Makefile.inc | 2
+ libmpathcmd/Makefile | 32 +++++++
+ libmpathcmd/mpath_cmd.c | 178 +++++++++++++++++++++++++++++++++++++++
+ libmpathcmd/mpath_cmd.h | 125 +++++++++++++++++++++++++++
+ libmpathpersist/Makefile | 5 -
+ libmpathpersist/mpath_updatepr.c | 30 +++---
+ libmultipath/Makefile | 4
+ libmultipath/config.c | 1
+ libmultipath/configure.c | 10 +-
+ libmultipath/uxsock.c | 88 +++----------------
+ libmultipath/uxsock.h | 6 -
+ mpathpersist/Makefile | 2
+ multipath/Makefile | 5 -
+ multipathd/Makefile | 4
+ multipathd/uxclnt.c | 13 +-
+ multipathd/uxlsnr.c | 9 -
+ 17 files changed, 401 insertions(+), 114 deletions(-)
+ create mode 100644 libmpathcmd/Makefile
+ create mode 100644 libmpathcmd/mpath_cmd.c
+ create mode 100644 libmpathcmd/mpath_cmd.h
+
+Index: multipath-tools-130222/Makefile
+===================================================================
+--- multipath-tools-130222.orig/Makefile
++++ multipath-tools-130222/Makefile
+@@ -20,6 +20,7 @@ export KRNLSRC
+ export KRNLOBJ
+
+ BUILDDIRS = \
++ libmpathcmd \
+ libmultipath \
+ libmultipath/prioritizers \
+ libmultipath/checkers \
+Index: multipath-tools-130222/Makefile.inc
+===================================================================
+--- multipath-tools-130222.orig/Makefile.inc
++++ multipath-tools-130222/Makefile.inc
+@@ -34,6 +34,8 @@ syslibdir = $(prefix)/usr/$(LIB)
+ libdir = $(prefix)/usr/$(LIB)/multipath
+ unitdir = $(prefix)/lib/systemd/system
+ mpathpersistdir = $(TOPDIR)/libmpathpersist
++includedir = $(prefix)/usr/include
++mpathcmddir = $(TOPDIR)/libmpathcmd
+
+ GZIP = /bin/gzip -9 -c
+ INSTALL_PROGRAM = install
+Index: multipath-tools-130222/libmpathcmd/Makefile
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libmpathcmd/Makefile
+@@ -0,0 +1,32 @@
++# Makefile
++#
++include ../Makefile.inc
++
++SONAME=0
++DEVLIB = libmpathcmd.so
++LIBS = $(DEVLIB).$(SONAME)
++
++CFLAGS += -fPIC
++
++OBJS = mpath_cmd.o
++
++all: $(LIBS)
++
++$(LIBS): $(OBJS)
++ $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS)
++ ln -sf $@ $(DEVLIB)
++
++install: $(LIBS)
++ $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
++ $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
++ ln -sf $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
++ $(INSTALL_PROGRAM) -d $(DESTDIR)$(includedir)
++ $(INSTALL_PROGRAM) -m 644 mpath_cmd.h $(DESTDIR)$(includedir)
++
++uninstall:
++ rm -f $(DESTDIR)$(syslibdir)/$(LIBS)
++ rm -f $(DESTDIR)$(syslibdir)/$(DEVLIB)
++ rm -f $(DESTDIR)$(includedir)/mpath_cmd.h
++
++clean:
++ rm -f core *.a *.o *.gz *.so *.so.*
+Index: multipath-tools-130222/libmpathcmd/mpath_cmd.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libmpathcmd/mpath_cmd.c
+@@ -0,0 +1,178 @@
++#include <stdlib.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <poll.h>
++#include <string.h>
++#include <errno.h>
++
++#include "mpath_cmd.h"
++
++/*
++ * keep reading until its all read
++ */
++static ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout)
++{
++ size_t total = 0;
++ ssize_t n;
++ int ret;
++ struct pollfd pfd;
++
++ while (len) {
++ pfd.fd = fd;
++ pfd.events = POLLIN;
++ ret = poll(&pfd, 1, timeout);
++ if (!ret) {
++ errno = ETIMEDOUT;
++ return -1;
++ } else if (ret < 0) {
++ if (errno == EINTR)
++ continue;
++ return -1;
++ } else if (!(pfd.revents & POLLIN))
++ continue;
++ n = read(fd, buf, len);
++ if (n < 0) {
++ if ((errno == EINTR) || (errno == EAGAIN))
++ continue;
++ return -1;
++ }
++ if (!n)
++ return total;
++ buf = n + (char *)buf;
++ len -= n;
++ total += n;
++ }
++ return total;
++}
++
++/*
++ * keep writing until it's all sent
++ */
++static size_t write_all(int fd, const void *buf, size_t len)
++{
++ size_t total = 0;
++
++ while (len) {
++ ssize_t n = write(fd, buf, len);
++ if (n < 0) {
++ if ((errno == EINTR) || (errno == EAGAIN))
++ continue;
++ return total;
++ }
++ if (!n)
++ return total;
++ buf = n + (char *)buf;
++ len -= n;
++ total += n;
++ }
++ return total;
++}
++
++/*
++ * connect to a unix domain socket
++ */
++int mpath_connect(void)
++{
++ int fd, len;
++ struct sockaddr_un addr;
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sun_family = AF_LOCAL;
++ addr.sun_path[0] = '\0';
++ len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t);
++ strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len);
++
++ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
++ if (fd == -1)
++ return -1;
++
++ if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
++ close(fd);
++ return -1;
++ }
++
++ return fd;
++}
++
++int mpath_disconnect(int fd)
++{
++ return close(fd);
++}
++
++ssize_t mpath_recv_reply_len(int fd, unsigned int timeout)
++{
++ size_t len;
++ ssize_t ret;
++
++ ret = read_all(fd, &len, sizeof(len), timeout);
++ if (ret < 0)
++ return ret;
++ if (ret != sizeof(len)) {
++ errno = EIO;
++ return -1;
++ }
++ return len;
++}
++
++int mpath_recv_reply_data(int fd, char *reply, size_t len,
++ unsigned int timeout)
++{
++ ssize_t ret;
++
++ ret = read_all(fd, reply, len, timeout);
++ if (ret < 0)
++ return ret;
++ if (ret != len) {
++ errno = EIO;
++ return -1;
++ }
++ reply[len - 1] = '\0';
++ return 0;
++}
++
++int mpath_recv_reply(int fd, char **reply, unsigned int timeout)
++{
++ int err;
++ ssize_t len;
++
++ *reply = NULL;
++ len = mpath_recv_reply_len(fd, timeout);
++ if (len <= 0)
++ return -1;
++ *reply = malloc(len);
++ if (!*reply)
++ return -1;
++ err = mpath_recv_reply_data(fd, *reply, len, timeout);
++ if (err) {
++ free(*reply);
++ *reply = NULL;
++ return -1;
++ }
++ return 0;
++}
++
++int mpath_send_cmd(int fd, const char *cmd)
++{
++ size_t len;
++
++ if (cmd != NULL)
++ len = strlen(cmd) + 1;
++ else
++ len = 0;
++ if (write_all(fd, &len, sizeof(len)) != sizeof(len))
++ return -1;
++ if (len && write_all(fd, cmd, len) != len)
++ return -1;
++ return 0;
++}
++
++int mpath_process_cmd(int fd, const char *cmd, char **reply,
++ unsigned int timeout)
++{
++ if (mpath_send_cmd(fd, cmd) != 0)
++ return -1;
++ return mpath_recv_reply(fd, reply, timeout);
++}
+Index: multipath-tools-130222/libmpathcmd/mpath_cmd.h
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libmpathcmd/mpath_cmd.h
+@@ -0,0 +1,125 @@
++/*
++ * Copyright (C) 2015 Red Hat, Inc.
++ *
++ * This file is part of the device-mapper multipath userspace tools.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++ * USA.
++ */
++
++#ifndef LIB_MPATH_CMD_H
++#define LIB_MPATH_CMD_H
++
++#ifdef __cpluscplus
++extern "C" {
++#endif
++
++#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
++#define DEFAULT_REPLY_TIMEOUT 10000
++
++
++/*
++ * DESCRIPTION:
++ * Connect to the running multipathd daemon. On systems with the
++ * multipathd.socket systemd unit file installed, this command will
++ * start multipathd if it is not already running. This function
++ * must be run before any of the others in this library
++ *
++ * RETURNS:
++ * A file descriptor on success. -1 on failure (with errno set).
++ */
++int mpath_connect(void);
++
++
++/*
++ * DESCRIPTION:
++ * Disconnect from the multipathd daemon. This function must be
++ * run after after processing all the multipath commands.
++ *
++ * RETURNS:
++ * 0 on success. -1 on failure (with errno set).
++ */
++int mpath_disconnect(int fd);
++
++
++/*
++ * DESCRIPTION
++ * Send multipathd a command and return the reply. This function
++ * does the same as calling mpath_send_cmd() and then
++ * mpath_recv_reply()
++ *
++ * RETURNS:
++ * 0 on successs, and reply will either be NULL (if there was no
++ * reply data), or point to the reply string, which must be freed by
++ * the caller. -1 on failure (with errno set).
++ */
++int mpath_process_cmd(int fd, const char *cmd, char **reply,
++ unsigned int timeout);
++
++
++/*
++ * DESCRIPTION:
++ * Send a command to multipathd
++ *
++ * RETURNS:
++ * 0 on success. -1 on failure (with errno set)
++ */
++int mpath_send_cmd(int fd, const char *cmd);
++
++
++/*
++ * DESCRIPTION:
++ * Return a reply from multipathd for a previously sent command.
++ * This is equivalent to calling mpath_recv_reply_len(), allocating
++ * a buffer of the appropriate size, and then calling
++ * mpath_recv_reply_data() with that buffer.
++ *
++ * RETURNS:
++ * 0 on success, and reply will either be NULL (if there was no
++ * reply data), or point to the reply string, which must be freed by
++ * the caller, -1 on failure (with errno set).
++ */
++int mpath_recv_reply(int fd, char **reply, unsigned int timeout);
++
++
++/*
++ * DESCRIPTION:
++ * Return the size of the upcoming reply data from the sent multipath
++ * command. This must be called before calling mpath_recv_reply_data().
++ *
++ * RETURNS:
++ * The required size of the reply data buffer on success. -1 on
++ * failure (with errno set).
++ */
++ssize_t mpath_recv_reply_len(int fd, unsigned int timeout);
++
++
++/*
++ * DESCRIPTION:
++ * Return the reply data from the sent multipath command.
++ * mpath_recv_reply_len must be called first. reply must point to a
++ * buffer of len size.
++ *
++ * RETURNS:
++ * 0 on success, and reply will contain the reply data string. -1
++ * on failure (with errno set).
++ */
++int mpath_recv_reply_data(int fd, char *reply, size_t len,
++ unsigned int timeout);
++
++#ifdef __cplusplus
++}
++#endif
++#endif /* LIB_MPATH_CMD_H */
+Index: multipath-tools-130222/libmpathpersist/Makefile
+===================================================================
+--- multipath-tools-130222.orig/libmpathpersist/Makefile
++++ multipath-tools-130222/libmpathpersist/Makefile
+@@ -10,8 +10,9 @@ DEVLIB = libmpathpersist.so
+ LIBS = $(DEVLIB).$(SONAME)
+
+
+-CFLAGS += -fPIC -I$(multipathdir) -I$(mpathpersistdir)
+-LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath
++CFLAGS += -fPIC -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir)
++LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath \
++ -L$(mpathcmddir) -lmpathcmd
+
+ OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o
+
+Index: multipath-tools-130222/libmpathpersist/mpath_updatepr.c
+===================================================================
+--- multipath-tools-130222.orig/libmpathpersist/mpath_updatepr.c
++++ multipath-tools-130222/libmpathpersist/mpath_updatepr.c
+@@ -12,9 +12,9 @@
+ #include <sys/poll.h>
+ #include <errno.h>
+ #include <debug.h>
++#include <mpath_cmd.h>
++#include <uxsock.h>
+ #include "memory.h"
+-#include "../libmultipath/uxsock.h"
+-#include "../libmultipath/defaults.h"
+
+ unsigned long mem_allocated; /* Total memory used in Bytes */
+
+@@ -23,10 +23,9 @@ int update_prflag(char * arg1, char * ar
+ int fd;
+ char str[64];
+ char *reply;
+- size_t len;
+ int ret = 0;
+
+- fd = ux_socket_connect(DEFAULT_SOCKET);
++ fd = mpath_connect();
+ if (fd == -1) {
+ condlog (0, "ux socket connect error");
+ return 1 ;
+@@ -34,18 +33,23 @@ int update_prflag(char * arg1, char * ar
+
+ snprintf(str,sizeof(str),"map %s %s", arg1, arg2);
+ condlog (2, "%s: pr flag message=%s", arg1, str);
+- send_packet(fd, str, strlen(str) + 1);
+- recv_packet(fd, &reply, &len);
+-
+- condlog (2, "%s: message=%s reply=%s", arg1, str, reply);
+- if (!reply || strncmp(reply,"ok", 2) == 0)
+- ret = -1;
+- else if (strncmp(reply, "fail", 4) == 0)
++ send_packet(fd, str);
++ ret = recv_packet(fd, &reply);
++ if (ret < 0) {
++ condlog(2, "%s: message=%s recv error=%d", arg1, str, errno);
+ ret = -2;
+- else{
+- ret = atoi(reply);
++ } else {
++ condlog (2, "%s: message=%s reply=%s", arg1, str, reply);
++ if (!reply || strncmp(reply,"ok", 2) == 0)
++ ret = -1;
++ else if (strncmp(reply, "fail", 4) == 0)
++ ret = -2;
++ else{
++ ret = atoi(reply);
++ }
+ }
+
+ free(reply);
++ mpath_disconnect(fd);
+ return ret;
+ }
+Index: multipath-tools-130222/libmultipath/Makefile
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/Makefile
++++ multipath-tools-130222/libmultipath/Makefile
+@@ -7,8 +7,8 @@ include ../Makefile.inc
+ SONAME=0
+ DEVLIB = libmultipath.so
+ LIBS = $(DEVLIB).$(SONAME)
+-LIBDEPS = -lpthread -ldl -ldevmapper -ludev
+-CFLAGS += -fPIC
++LIBDEPS = -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd
++CFLAGS += -fPIC -I$(mpathcmddir)
+
+ OBJS = memory.o parser.o vector.o devmapper.o \
+ hwtable.o blacklist.o util.o dmparser.o config.o \
+Index: multipath-tools-130222/libmultipath/config.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/config.c
++++ multipath-tools-130222/libmultipath/config.c
+@@ -25,6 +25,7 @@
+ #include "prio.h"
+ #include "devmapper.h"
+ #include "version.h"
++#include "mpath_cmd.h"
+
+ static int
+ hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2)
+Index: multipath-tools-130222/libmultipath/configure.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/configure.c
++++ multipath-tools-130222/libmultipath/configure.c
+@@ -14,6 +14,7 @@
+ #include <errno.h>
+ #include <libdevmapper.h>
+ #include <libudev.h>
++#include <mpath_cmd.h>
+
+ #include "checkers.h"
+ #include "vector.h"
+@@ -752,16 +753,15 @@ check_daemon(void)
+ {
+ int fd;
+ char *reply;
+- size_t len;
+ int ret = 0;
+
+- fd = ux_socket_connect(DEFAULT_SOCKET);
++ fd = mpath_connect();
+ if (fd == -1)
+ return 0;
+
+- if (send_packet(fd, "show daemon", 12) != 0)
++ if (send_packet(fd, "show daemon") != 0)
+ goto out;
+- if (recv_packet(fd, &reply, &len) != 0)
++ if (recv_packet(fd, &reply) != 0)
+ goto out;
+
+ if (strstr(reply, "shutdown"))
+@@ -772,7 +772,7 @@ check_daemon(void)
+ out_free:
+ FREE(reply);
+ out:
+- close(fd);
++ mpath_disconnect(fd);
+ return ret;
+ }
+
+Index: multipath-tools-130222/libmultipath/uxsock.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/uxsock.c
++++ multipath-tools-130222/libmultipath/uxsock.c
+@@ -16,37 +16,10 @@
+ #include <sys/poll.h>
+ #include <signal.h>
+ #include <errno.h>
++#include <mpath_cmd.h>
+
+ #include "memory.h"
+ #include "uxsock.h"
+-
+-/*
+- * connect to a unix domain socket
+- */
+-int ux_socket_connect(const char *name)
+-{
+- int fd, len;
+- struct sockaddr_un addr;
+-
+- memset(&addr, 0, sizeof(addr));
+- addr.sun_family = AF_LOCAL;
+- addr.sun_path[0] = '\0';
+- len = strlen(name) + 1 + sizeof(sa_family_t);
+- strncpy(&addr.sun_path[1], name, len);
+-
+- fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+- if (fd == -1) {
+- return -1;
+- }
+-
+- if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
+- close(fd);
+- return -1;
+- }
+-
+- return fd;
+-}
+-
+ /*
+ * create a unix domain socket and start listening on it
+ * return a file descriptor open on the socket
+@@ -102,32 +75,9 @@ size_t write_all(int fd, const void *buf
+ }
+
+ /*
+- * keep reading until its all read
+- */
+-size_t read_all(int fd, void *buf, size_t len)
+-{
+- size_t total = 0;
+-
+- while (len) {
+- ssize_t n = read(fd, buf, len);
+- if (n < 0) {
+- if ((errno == EINTR) || (errno == EAGAIN))
+- continue;
+- return total;
+- }
+- if (!n)
+- return total;
+- buf = n + (char *)buf;
+- len -= n;
+- total += n;
+- }
+- return total;
+-}
+-
+-/*
+ * send a packet in length prefix format
+ */
+-int send_packet(int fd, const char *buf, size_t len)
++int send_packet(int fd, const char *buf)
+ {
+ int ret = 0;
+ sigset_t set, old;
+@@ -137,10 +87,7 @@ int send_packet(int fd, const char *buf,
+ sigaddset(&set, SIGPIPE);
+ pthread_sigmask(SIG_BLOCK, &set, &old);
+
+- if (write_all(fd, &len, sizeof(len)) != sizeof(len))
+- ret = -1;
+- if (!ret && write_all(fd, buf, len) != len)
+- ret = -1;
++ ret = mpath_send_cmd(fd, buf);
+
+ /* And unblock it again */
+ pthread_sigmask(SIG_SETMASK, &old, NULL);
+@@ -151,25 +98,24 @@ int send_packet(int fd, const char *buf,
+ /*
+ * receive a packet in length prefix format
+ */
+-int recv_packet(int fd, char **buf, size_t *len)
++int recv_packet(int fd, char **buf)
+ {
+- if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) {
+- (*buf) = NULL;
+- *len = 0;
+- return -1;
+- }
+- if (len == 0) {
+- (*buf) = NULL;
+- return 0;
+- }
+- (*buf) = MALLOC(*len);
++ int err;
++ ssize_t len;
++ unsigned int timeout = DEFAULT_REPLY_TIMEOUT;
++
++ *buf = NULL;
++ len = mpath_recv_reply_len(fd, timeout);
++ if (len <= 0)
++ return len;
++ (*buf) = MALLOC(len);
+ if (!*buf)
+- return -1;
+- if (read_all(fd, *buf, *len) != *len) {
++ return -ENOMEM;
++ err = mpath_recv_reply_data(fd, *buf, len, timeout);
++ if (err) {
+ FREE(*buf);
+ (*buf) = NULL;
+- *len = 0;
+- return -1;
++ return err;
+ }
+ return 0;
+ }
+Index: multipath-tools-130222/libmultipath/uxsock.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/uxsock.h
++++ multipath-tools-130222/libmultipath/uxsock.h
+@@ -1,7 +1,5 @@
+ /* some prototypes */
+-int ux_socket_connect(const char *name);
+ int ux_socket_listen(const char *name);
+-int send_packet(int fd, const char *buf, size_t len);
+-int recv_packet(int fd, char **buf, size_t *len);
++int send_packet(int fd, const char *buf);
++int recv_packet(int fd, char **buf);
+ size_t write_all(int fd, const void *buf, size_t len);
+-size_t read_all(int fd, void *buf, size_t len);
+Index: multipath-tools-130222/mpathpersist/Makefile
+===================================================================
+--- multipath-tools-130222.orig/mpathpersist/Makefile
++++ multipath-tools-130222/mpathpersist/Makefile
+@@ -5,7 +5,7 @@ include ../Makefile.inc
+ OBJS = main.o
+
+ CFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
+-LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath -ludev
++LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -L$(mpathcmddir) -lmpathcmd -lmultipath -ludev
+
+ EXEC = mpathpersist
+
+Index: multipath-tools-130222/multipath/Makefile
+===================================================================
+--- multipath-tools-130222.orig/multipath/Makefile
++++ multipath-tools-130222/multipath/Makefile
+@@ -6,8 +6,9 @@ include ../Makefile.inc
+
+ OBJS = main.o
+
+-CFLAGS += -fPIC -I$(multipathdir)
+-LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev
++CFLAGS += -I$(multipathdir) -I$(mpathcmddir)
++LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev \
++ -L$(mpathcmddir) -lmpathcmd
+
+ EXEC = multipath
+
+Index: multipath-tools-130222/multipathd/Makefile
+===================================================================
+--- multipath-tools-130222.orig/multipathd/Makefile
++++ multipath-tools-130222/multipathd/Makefile
+@@ -5,10 +5,10 @@ include ../Makefile.inc
+ #
+ # basic flags setting
+ #
+-CFLAGS += -fPIE -DPIE -I$(multipathdir) -I$(mpathpersistdir)
++CFLAGS += -fPIE -DPIE -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir)
+ LDFLAGS += -lpthread -ldevmapper -lreadline -ludev -ldl \
+ -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
+- -Wl,-z,now -pie
++ -L$(mpathcmddir) -lmpathcmd -Wl,-z,now -pie
+
+ #
+ # debuging stuff
+Index: multipath-tools-130222/multipathd/uxclnt.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/uxclnt.c
++++ multipath-tools-130222/multipathd/uxclnt.c
+@@ -17,6 +17,7 @@
+ #include <readline/readline.h>
+ #include <readline/history.h>
+
++#include <mpath_cmd.h>
+ #include <uxsock.h>
+ #include <memory.h>
+ #include <defaults.h>
+@@ -49,7 +50,6 @@ static void process(int fd)
+ rl_readline_name = "multipathd";
+ rl_completion_entry_function = key_generator;
+ while ((line = readline("multipathd> "))) {
+- size_t len;
+ size_t llen = strlen(line);
+
+ if (!llen) {
+@@ -61,8 +61,8 @@ static void process(int fd)
+ if (!strncmp(line, "quit", 4) && llen == 4)
+ break;
+
+- if (send_packet(fd, line, llen + 1) != 0) break;
+- if (recv_packet(fd, &reply, &len) != 0) break;
++ if (send_packet(fd, line) != 0) break;
++ if (recv_packet(fd, &reply) != 0) break;
+
+ print_reply(reply);
+
+@@ -77,13 +77,12 @@ static void process(int fd)
+ static void process_req(int fd, char * inbuf)
+ {
+ char *reply;
+- size_t len;
+
+- if (send_packet(fd, inbuf, strlen(inbuf) + 1) != 0) {
++ if (send_packet(fd, inbuf) != 0) {
+ printf("cannot send packet\n");
+ return;
+ }
+- if (recv_packet(fd, &reply, &len) != 0)
++ if (recv_packet(fd, &reply) != 0)
+ printf("error receiving packet\n");
+ else {
+ printf("%s", reply);
+@@ -98,7 +97,7 @@ int uxclnt(char * inbuf)
+ {
+ int fd;
+
+- fd = ux_socket_connect(DEFAULT_SOCKET);
++ fd = mpath_connect();
+ if (fd == -1) {
+ perror("ux_socket_connect");
+ exit(1);
+Index: multipath-tools-130222/multipathd/uxlsnr.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/uxlsnr.c
++++ multipath-tools-130222/multipathd/uxlsnr.c
+@@ -29,6 +29,7 @@
+ #include <structs_vec.h>
+ #include <uxsock.h>
+ #include <defaults.h>
++#include <mpath_cmd.h>
+
+ #include "main.h"
+ #include "cli.h"
+@@ -108,7 +109,6 @@ void * uxsock_listen(int (*uxsock_trigge
+ void * trigger_data)
+ {
+ int ux_sock;
+- size_t len;
+ int rlen;
+ char *inbuf;
+ char *reply;
+@@ -171,16 +171,15 @@ void * uxsock_listen(int (*uxsock_trigge
+ struct client *next = c->next;
+
+ if (polls[i].revents & POLLIN) {
+- if (recv_packet(c->fd, &inbuf, &len) != 0) {
++ if (recv_packet(c->fd, &inbuf) != 0) {
+ dead_client(c);
+ } else {
+- inbuf[len - 1] = 0;
+ condlog(4, "Got request [%s]", inbuf);
+ uxsock_trigger(inbuf, &reply, &rlen,
+ trigger_data);
+ if (reply) {
+- if (send_packet(c->fd, reply,
+- rlen) != 0) {
++ if (send_packet(c->fd,
++ reply) != 0) {
+ dead_client(c);
+ }
+ condlog(4, "Reply [%d bytes]",
diff --git a/0208-UPBZ-1430097-multipathd-IPC-changes.patch b/0208-UPBZ-1430097-multipathd-IPC-changes.patch
new file mode 100644
index 0000000..cddf641
--- /dev/null
+++ b/0208-UPBZ-1430097-multipathd-IPC-changes.patch
@@ -0,0 +1,280 @@
+[PATCH] Multipath: Remove duplicated memset() for multipathd show command.
+[PATCH] multipath-tools: New way to limit the IPC command length.
+[PATCH] multipath-tools: Perform socket client uid check on IPC commands.
+
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ libmultipath/print.c | 10 ----------
+ libmultipath/uxsock.c | 38 +++++++++++++++++++++++++++++---------
+ libmultipath/uxsock.h | 9 +++++++++
+ multipathd/main.c | 15 +++++++++++++--
+ multipathd/uxlsnr.c | 31 ++++++++++++++++++++++++++-----
+ multipathd/uxlsnr.h | 8 +++++---
+ 6 files changed, 82 insertions(+), 29 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/print.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/print.c
++++ multipath-tools-130222/libmultipath/print.c
+@@ -771,8 +771,6 @@ snprint_multipath_header (char * line, i
+ int fwd;
+ struct multipath_data * data;
+
+- memset(line, 0, len);
+-
+ do {
+ if (!TAIL)
+ break;
+@@ -806,8 +804,6 @@ snprint_multipath (char * line, int len,
+ struct multipath_data * data;
+ char buff[MAX_FIELD_LEN] = {};
+
+- memset(line, 0, len);
+-
+ do {
+ if (!TAIL)
+ break;
+@@ -842,8 +838,6 @@ snprint_path_header (char * line, int le
+ int fwd;
+ struct path_data * data;
+
+- memset(line, 0, len);
+-
+ do {
+ if (!TAIL)
+ break;
+@@ -877,8 +871,6 @@ snprint_path (char * line, int len, char
+ struct path_data * data;
+ char buff[MAX_FIELD_LEN];
+
+- memset(line, 0, len);
+-
+ do {
+ if (!TAIL)
+ break;
+@@ -914,8 +906,6 @@ snprint_pathgroup (char * line, int len,
+ struct pathgroup_data * data;
+ char buff[MAX_FIELD_LEN];
+
+- memset(line, 0, len);
+-
+ do {
+ if (!TAIL)
+ break;
+Index: multipath-tools-130222/libmultipath/uxsock.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/uxsock.c
++++ multipath-tools-130222/libmultipath/uxsock.c
+@@ -20,6 +20,15 @@
+
+ #include "memory.h"
+ #include "uxsock.h"
++
++/*
++ * Code is similar with mpath_recv_reply() with data size limitation
++ * and debug-able malloc.
++ * When limit == 0, it means no limit on data size, used for socket client
++ * to receiving data from multipathd.
++ */
++static int _recv_packet(int fd, char **buf, ssize_t limit);
++
+ /*
+ * create a unix domain socket and start listening on it
+ * return a file descriptor open on the socket
+@@ -95,27 +104,38 @@ int send_packet(int fd, const char *buf)
+ return ret;
+ }
+
+-/*
+- * receive a packet in length prefix format
+- */
+-int recv_packet(int fd, char **buf)
++static int _recv_packet(int fd, char **buf, ssize_t limit)
+ {
+- int err;
+- ssize_t len;
++ int err = 0;
++ ssize_t len = 0;
+ unsigned int timeout = DEFAULT_REPLY_TIMEOUT;
+
+ *buf = NULL;
+ len = mpath_recv_reply_len(fd, timeout);
+ if (len <= 0)
+ return len;
++ if ((limit > 0) && (len > limit))
++ return -EINVAL;
+ (*buf) = MALLOC(len);
+ if (!*buf)
+ return -ENOMEM;
+ err = mpath_recv_reply_data(fd, *buf, len, timeout);
+- if (err) {
++ if (err != 0) {
+ FREE(*buf);
+ (*buf) = NULL;
+- return err;
+ }
+- return 0;
++ return err;
++}
++
++/*
++ * receive a packet in length prefix format
++ */
++int recv_packet(int fd, char **buf)
++{
++ return _recv_packet(fd, buf, 0 /* no limit */);
++}
++
++int recv_packet_from_client(int fd, char **buf)
++{
++ return _recv_packet(fd, buf, _MAX_CMD_LEN);
+ }
+Index: multipath-tools-130222/libmultipath/uxsock.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/uxsock.h
++++ multipath-tools-130222/libmultipath/uxsock.h
+@@ -3,3 +3,12 @@ int ux_socket_listen(const char *name);
+ int send_packet(int fd, const char *buf);
+ int recv_packet(int fd, char **buf);
+ size_t write_all(int fd, const void *buf, size_t len);
++
++#define _MAX_CMD_LEN 512
++
++/*
++ * Used for receiving socket command from untrusted socket client where data
++ * size is restricted to 512(_MAX_CMD_LEN) at most.
++ * Return -EINVAL if data length requested by client exceeded the _MAX_CMD_LEN.
++ */
++int recv_packet_from_client(int fd, char **buf);
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -18,6 +18,7 @@
+ #include <linux/oom.h>
+ #include <libudev.h>
+ #include <semaphore.h>
++#include <stdbool.h>
+ #include <mpath_persist.h>
+ #include "prioritizers/alua_rtpg.h"
+
+@@ -859,7 +860,8 @@ map_discovery (struct vectors * vecs)
+ }
+
+ int
+-uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data)
++uxsock_trigger (char * str, char ** reply, int * len, bool is_root,
++ void * trigger_data)
+ {
+ struct vectors * vecs;
+ int r;
+@@ -872,6 +874,15 @@ uxsock_trigger (char * str, char ** repl
+ lock(vecs->lock);
+ pthread_testcancel();
+
++ if ((str != NULL) && (is_root == false) &&
++ (strncmp(str, "list", strlen("list")) != 0) &&
++ (strncmp(str, "show", strlen("show")) != 0)) {
++ *reply = STRDUP("permission deny: need to be root");
++ *len = strlen(*reply) + 1;
++ r = 1;
++ goto out;
++ }
++
+ r = parse_cmd(str, reply, len, vecs);
+
+ if (r > 0) {
+@@ -885,7 +896,7 @@ uxsock_trigger (char * str, char ** repl
+ r = 0;
+ }
+ /* else if (r < 0) leave *reply alone */
+-
++out:
+ lock_cleanup_pop(vecs->lock);
+ return r;
+ }
+Index: multipath-tools-130222/multipathd/uxlsnr.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/uxlsnr.c
++++ multipath-tools-130222/multipathd/uxlsnr.c
+@@ -21,6 +21,7 @@
+ #include <sys/un.h>
+ #include <sys/poll.h>
+ #include <signal.h>
++#include <stdbool.h>
+ #include <checkers.h>
+ #include <memory.h>
+ #include <debug.h>
+@@ -48,6 +49,23 @@ struct pollfd *polls;
+ volatile sig_atomic_t reconfig_sig = 0;
+ volatile sig_atomic_t log_reset_sig = 0;
+
++static bool _socket_client_is_root(int fd);
++
++static bool _socket_client_is_root(int fd)
++{
++ socklen_t len = 0;
++ struct ucred uc;
++
++ len = sizeof(struct ucred);
++ if ((fd >= 0) &&
++ (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) &&
++ (uc.uid == 0))
++ return true;
++
++ /* Treat error as not root client */
++ return false;
++}
++
+ /*
+ * handle a new client joining
+ */
+@@ -105,8 +123,7 @@ void uxsock_cleanup(void *arg)
+ /*
+ * entry point
+ */
+-void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *),
+- void * trigger_data)
++void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data)
+ {
+ int ux_sock;
+ int rlen;
+@@ -171,12 +188,16 @@ void * uxsock_listen(int (*uxsock_trigge
+ struct client *next = c->next;
+
+ if (polls[i].revents & POLLIN) {
+- if (recv_packet(c->fd, &inbuf) != 0) {
++ if (recv_packet_from_client(c->fd,
++ &inbuf) != 0) {
+ dead_client(c);
++ } else if (!inbuf) {
++ condlog(4, "recv_packet_from_client "
++ "get null request");
++ continue;
+ } else {
+ condlog(4, "Got request [%s]", inbuf);
+- uxsock_trigger(inbuf, &reply, &rlen,
+- trigger_data);
++ uxsock_trigger(inbuf, &reply, &rlen, _socket_client_is_root(c->fd), trigger_data);
+ if (reply) {
+ if (send_packet(c->fd,
+ reply) != 0) {
+Index: multipath-tools-130222/multipathd/uxlsnr.h
+===================================================================
+--- multipath-tools-130222.orig/multipathd/uxlsnr.h
++++ multipath-tools-130222/multipathd/uxlsnr.h
+@@ -1,9 +1,11 @@
+ #ifndef _UXLSNR_H
+ #define _UXLSNR_H
+
+-void * uxsock_listen(int (*uxsock_trigger)
+- (char *, char **, int *, void *),
+- void * trigger_data);
++#include <stdbool.h>
++
++typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *);
++
++void *uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data);
+
+ extern volatile sig_atomic_t reconfig_sig;
+ extern volatile sig_atomic_t log_reset_sig;
diff --git a/0209-UPBZ-1430097-multipath-C-API.patch b/0209-UPBZ-1430097-multipath-C-API.patch
new file mode 100644
index 0000000..08a6f1d
--- /dev/null
+++ b/0209-UPBZ-1430097-multipath-C-API.patch
@@ -0,0 +1,5632 @@
+From 4335abb36f33f12eadc943729901fac31f3dc012 Mon Sep 17 00:00:00 2001
+From: Gris Ge <fge@redhat.com>
+Date: Fri, 24 Feb 2017 20:50:26 +0800
+Subject: [PATCH] multipath-tools: Introducing multipath C API
+
+Features:
+
+ * Use mpath_cmd.h for IPC connection and use output of 'show maps json'.
+ * Library user guide will be 'man 3 libdmmp.h'.
+ * Every public function has its own manpage in section 3 which is
+ generated by linux 'kernel-doc' tool.
+
+Usage:
+
+ make -j5
+ sudo make install \
+ bindir=/usr/sbin/ \
+ syslibdir=/usr/lib64/ \
+ libdir=/usr/lib64/multipath \
+ rcdir=/etc/rc.d/init.d \
+ unitdir=/usr/lib/systemd/system \
+ includedir=/usr/include
+ make -C libdmmp check
+ make -C libdmmp speed_test
+
+ man libdmmp.h
+ man dmmp_mpath_array_get
+ man <dmmp function name>
+
+Performance:
+
+ * 10k scsi_debug sdX with 2 disks per mpath (i7-6820HQ 16GiB RAM):
+ $ make -C libdmmp speed_test
+ Got 5000 mpath
+ real 3.22
+ user 0.15
+ sys 0.01
+
+Misc:
+ * Developer note is libdmmp/DEV_NOTES.
+
+Changes since V4:
+
+ * Add new function dmmp_mpath_kdev_name_get() to query the '/dev/dm-01' for
+ mpath.
+ * Updated manpages.
+ * Rebased to current master ea4367159d32444e48a409a4f1c4f18324b737a9.
+
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ .gitignore | 8
+ Makefile | 1
+ Makefile.inc | 6
+ libdmmp/DEV_NOTES | 41
+ libdmmp/Makefile | 84 +
+ libdmmp/docs/doc-preclean.pl | 28
+ libdmmp/docs/kernel-doc | 3156 ++++++++++++++++++++++++++++++++++++++
+ libdmmp/docs/libdmmp.h.3 | 113 +
+ libdmmp/docs/split-man.pl | 40
+ libdmmp/libdmmp.c | 285 +++
+ libdmmp/libdmmp.pc.in | 9
+ libdmmp/libdmmp/libdmmp.h | 653 +++++++
+ libdmmp/libdmmp_misc.c | 87 +
+ libdmmp/libdmmp_mp.c | 159 +
+ libdmmp/libdmmp_path.c | 115 +
+ libdmmp/libdmmp_pg.c | 208 ++
+ libdmmp/libdmmp_private.h | 208 ++
+ libdmmp/test/Makefile | 30
+ libdmmp/test/libdmmp_speed_test.c | 49
+ libdmmp/test/libdmmp_test.c | 147 +
+ 20 files changed, 5426 insertions(+), 1 deletion(-)
+ create mode 100644 libdmmp/DEV_NOTES
+ create mode 100644 libdmmp/Makefile
+ create mode 100644 libdmmp/docs/doc-preclean.pl
+ create mode 100644 libdmmp/docs/kernel-doc
+ create mode 100644 libdmmp/docs/libdmmp.h.3
+ create mode 100644 libdmmp/docs/split-man.pl
+ create mode 100644 libdmmp/libdmmp.c
+ create mode 100644 libdmmp/libdmmp.pc.in
+ create mode 100644 libdmmp/libdmmp/libdmmp.h
+ create mode 100644 libdmmp/libdmmp_misc.c
+ create mode 100644 libdmmp/libdmmp_mp.c
+ create mode 100644 libdmmp/libdmmp_path.c
+ create mode 100644 libdmmp/libdmmp_pg.c
+ create mode 100644 libdmmp/libdmmp_private.h
+ create mode 100644 libdmmp/test/Makefile
+ create mode 100644 libdmmp/test/libdmmp_speed_test.c
+ create mode 100644 libdmmp/test/libdmmp_test.c
+
+Index: multipath-tools-130222/.gitignore
+===================================================================
+--- multipath-tools-130222.orig/.gitignore
++++ multipath-tools-130222/.gitignore
+@@ -10,3 +10,11 @@ multipath/multipath
+ multipathd/multipathd
+ mpathpersist/mpathpersist
+ .nfs*
++*.swp
++*.patch
++*.rej
++*.orig
++libdmmp/docs/man/*.3.gz
++libdmmp/*.so.*
++libdmmp/test/libdmmp_test
++libdmmp/test/libdmmp_speed_test
+Index: multipath-tools-130222/Makefile
+===================================================================
+--- multipath-tools-130222.orig/Makefile
++++ multipath-tools-130222/Makefile
+@@ -25,6 +25,7 @@ BUILDDIRS = \
+ libmultipath/prioritizers \
+ libmultipath/checkers \
+ libmpathpersist \
++ libdmmp \
+ multipath \
+ multipathd \
+ mpathpersist \
+Index: multipath-tools-130222/Makefile.inc
+===================================================================
+--- multipath-tools-130222.orig/Makefile.inc
++++ multipath-tools-130222/Makefile.inc
+@@ -36,8 +36,12 @@ unitdir = $(prefix)/lib/systemd/syst
+ mpathpersistdir = $(TOPDIR)/libmpathpersist
+ includedir = $(prefix)/usr/include
+ mpathcmddir = $(TOPDIR)/libmpathcmd
++libdmmpdir = $(TOPDIR)/libdmmp
++pkgconfdir = $(prefix)/usr/$(LIB)/pkgconfig
+
+-GZIP = /bin/gzip -9 -c
++GZIP = /bin/gzip -9 -c
++RM = rm -f
++LN = ln -sf
+ INSTALL_PROGRAM = install
+
+ ifndef RPM_OPT_FLAGS
+Index: multipath-tools-130222/libdmmp/DEV_NOTES
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/DEV_NOTES
+@@ -0,0 +1,41 @@
++== Planed features ==
++ * Expose all properties used by /usr/bin/multipath
++
++== Code style ==
++ * Keep things as simple as possible.
++ * Linux Kernel code style.
++ * Don't use typedef.
++ * Don't use enum.
++ * We are not smarter than API user, so don't create wrapping function like:
++
++ ```
++ dmmp_mpath_search_by_id(struct dmmp_context *ctx,
++ struct dmmp_mpath **dmmp_mp,
++ uint32_t dmmp_mp_count, const char *id)
++
++ dmmp_path_group_id_search(struct dmmp_mpath *dmmp_mp,
++ const char *blk_name)
++ ```
++ * The performance is the same for query single mpath and query all mpaths,
++ so no `dmmp_mpath_of_wwid(struct dmmp_context *ctx, const char *wwid)` yet.
++
++== Naming scheme ==
++ * Public constants should be named as `DMMP_XXX_YYY`.
++ * Public functions should be named as `dmmp_<noun>_<verb>`.
++ * Private constants should be named as `_DMMP_XXX_YYY`.
++ * Private functions should be named as `_dmmp_<noun>_<verb>`.
++
++== Code Layout ==
++ * libdmmp_private.h
++ Internal functions or macros.
++ * libdmmp.c
++ Handling multipathd IPC and generate dmmp_context and
++ dmmp_mpath_array_get().
++ * libdmmp_mp.c
++ For `struct dmmp_mpath`
++ * libdmmp_pg.c
++ For `struct dmmp_path_group`
++ * libdmmp_path.c
++ For `struct dmmp_path`
++ * libdmmp_misc.c
++ Misc functions.
+Index: multipath-tools-130222/libdmmp/Makefile
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/Makefile
+@@ -0,0 +1,84 @@
++# Makefile
++#
++# Copyright (C) 2015 - 2016 Red Hat, Inc.
++# Gris Ge <fge@redhat.com>
++#
++include ../Makefile.inc
++
++LIBDMMP_VERSION=0.1.0
++SONAME=$(LIBDMMP_VERSION)
++DEVLIB = libdmmp.so
++LIBS = $(DEVLIB).$(SONAME)
++LIBDEPS = -pthread
++PKGFILE = libdmmp.pc
++EXTRA_MAN_FILES = libdmmp.h.3
++HEADERS = libdmmp/libdmmp.h
++OBJS = libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o
++
++CFLAGS += -fPIC -fvisibility=hidden -I$(libdmmpdir) -I$(mpathcmddir) \
++ $(shell pkg-config --cflags json-c)
++LDFLAGS += $(shell pkg-config --libs json-c) -L$(mpathcmddir) -lmpathcmd
++
++all: $(LIBS) doc
++
++$(LIBS): $(OBJS)
++ $(CC) $(LDFLAGS) $(SHARED_FLAGS) \
++ -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS)
++ $(LN) $@ $(DEVLIB)
++
++install:
++ $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
++ $(INSTALL_PROGRAM) -m 644 -D \
++ $(HEADERS) $(DESTDIR)$(includedir)/$(HEADERS)
++ $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
++ $(INSTALL_PROGRAM) -m 644 -D \
++ $(PKGFILE).in $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
++ perl -i -pe 's|__VERSION__|$(LIBDMMP_VERSION)|g' \
++ $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
++ perl -i -pe 's|__LIBDIR__|$(syslibdir)|g' \
++ $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
++ perl -i -pe 's|__INCLUDEDIR__|$(includedir)|g' \
++ $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
++ @for file in docs/man/*.3.gz; do \
++ $(INSTALL_PROGRAM) -m 644 -D \
++ $$file \
++ $(DESTDIR)$(man3dir)/ || exit $?; \
++ done
++
++uninstall:
++ $(RM) $(DESTDIR)$(syslibdir)/$(LIBS)
++ $(RM) $(DESTDIR)$(includedir)/$(HEADERS)
++ $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB)
++ @for file in $(DESTDIR)$(man3dir)/dmmp_*; do \
++ $(RM) $$file; \
++ done
++ $(RM) $(DESTDIR)$(man3dir)/libdmmp.h*
++
++clean:
++ $(RM) core *.a *.o *.gz *.so *.so.*
++ $(RM) docs/man/*.3.gz
++ $(MAKE) -C test clean
++
++check: all
++ $(MAKE) -C test check
++
++speed_test: all
++ $(MAKE) -C test speed_test
++
++doc: docs/man/$(EXTRA_MAN_FILES).gz
++
++TEMPFILE := $(shell mktemp)
++
++docs/man/$(EXTRA_MAN_FILES).gz: $(HEADERS)
++ @for file in $(EXTRA_MAN_FILES); do \
++ $(INSTALL_PROGRAM) -v -m 644 -D docs/$$file docs/man/$$file; \
++ done
++ cat $(HEADERS) | \
++ perl docs/doc-preclean.pl > $(TEMPFILE)
++ perl docs/kernel-doc -man $(TEMPFILE) | \
++ perl docs/split-man.pl docs/man
++ -rm -f $(TEMPFILE)
++ @for file in docs/man/*.3; do \
++ gzip -f $$file; \
++ done
++ find docs/man -type f -name \*[0-9].gz
+Index: multipath-tools-130222/libdmmp/docs/doc-preclean.pl
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/docs/doc-preclean.pl
+@@ -0,0 +1,28 @@
++#!/usr/bin/perl
++# Copyright (C) 2016 Red Hat, Inc.
++#
++# 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 3 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++#
++# Author: Gris Ge <fge@redhat.com>
++
++use strict;
++
++my @REMOVE_KEY_LIST=("DMMP_DLL_EXPORT");
++
++while (<>) {
++ for my $key (@REMOVE_KEY_LIST) {
++ (s/$key//g);
++ }
++ print;
++}
+Index: multipath-tools-130222/libdmmp/docs/kernel-doc
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/docs/kernel-doc
+@@ -0,0 +1,3156 @@
++#!/usr/bin/perl -w
++
++use strict;
++
++## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##
++## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ##
++## Copyright (C) 2001 Simon Huggins ##
++## Copyright (C) 2005-2012 Randy Dunlap ##
++## Copyright (C) 2012 Dan Luedtke ##
++## ##
++## #define enhancements by Armin Kuster <akuster@mvista.com> ##
++## Copyright (c) 2000 MontaVista Software, Inc. ##
++## ##
++## This software falls under the GNU General Public License. ##
++## Please read the COPYING file for more information ##
++
++# 18/01/2001 - Cleanups
++# Functions prototyped as foo(void) same as foo()
++# Stop eval'ing where we don't need to.
++# -- huggie@earth.li
++
++# 27/06/2001 - Allowed whitespace after initial "/**" and
++# allowed comments before function declarations.
++# -- Christian Kreibich <ck@whoop.org>
++
++# Still to do:
++# - add perldoc documentation
++# - Look more closely at some of the scarier bits :)
++
++# 26/05/2001 - Support for separate source and object trees.
++# Return error code.
++# Keith Owens <kaos@ocs.com.au>
++
++# 23/09/2001 - Added support for typedefs, structs, enums and unions
++# Support for Context section; can be terminated using empty line
++# Small fixes (like spaces vs. \s in regex)
++# -- Tim Jansen <tim@tjansen.de>
++
++# 25/07/2012 - Added support for HTML5
++# -- Dan Luedtke <mail@danrl.de>
++
++sub usage {
++ my $message = <<"EOF";
++Usage: $0 [OPTION ...] FILE ...
++
++Read C language source or header FILEs, extract embedded documentation comments,
++and print formatted documentation to standard output.
++
++The documentation comments are identified by "/**" opening comment mark. See
++Documentation/kernel-doc-nano-HOWTO.txt for the documentation comment syntax.
++
++Output format selection (mutually exclusive):
++ -docbook Output DocBook format.
++ -html Output HTML format.
++ -html5 Output HTML5 format.
++ -list Output symbol list format. This is for use by docproc.
++ -man Output troff manual page format. This is the default.
++ -rst Output reStructuredText format.
++ -text Output plain text format.
++
++Output selection (mutually exclusive):
++ -export Only output documentation for symbols that have been
++ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()
++ in any input FILE or -export-file FILE.
++ -internal Only output documentation for symbols that have NOT been
++ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()
++ in any input FILE or -export-file FILE.
++ -function NAME Only output documentation for the given function(s)
++ or DOC: section title(s). All other functions and DOC:
++ sections are ignored. May be specified multiple times.
++ -nofunction NAME Do NOT output documentation for the given function(s);
++ only output documentation for the other functions and
++ DOC: sections. May be specified multiple times.
++
++Output selection modifiers:
++ -no-doc-sections Do not output DOC: sections.
++ -enable-lineno Enable output of #define LINENO lines. Only works with
++ reStructuredText format.
++ -export-file FILE Specify an additional FILE in which to look for
++ EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with
++ -export or -internal. May be specified multiple times.
++
++Other parameters:
++ -v Verbose output, more warnings and other information.
++ -h Print this help.
++
++EOF
++ print $message;
++ exit 1;
++}
++
++#
++# format of comments.
++# In the following table, (...)? signifies optional structure.
++# (...)* signifies 0 or more structure elements
++# /**
++# * function_name(:)? (- short description)?
++# (* @parameterx: (description of parameter x)?)*
++# (* a blank line)?
++# * (Description:)? (Description of function)?
++# * (section header: (section description)? )*
++# (*)?*/
++#
++# So .. the trivial example would be:
++#
++# /**
++# * my_function
++# */
++#
++# If the Description: header tag is omitted, then there must be a blank line
++# after the last parameter specification.
++# e.g.
++# /**
++# * my_function - does my stuff
++# * @my_arg: its mine damnit
++# *
++# * Does my stuff explained.
++# */
++#
++# or, could also use:
++# /**
++# * my_function - does my stuff
++# * @my_arg: its mine damnit
++# * Description: Does my stuff explained.
++# */
++# etc.
++#
++# Besides functions you can also write documentation for structs, unions,
++# enums and typedefs. Instead of the function name you must write the name
++# of the declaration; the struct/union/enum/typedef must always precede
++# the name. Nesting of declarations is not supported.
++# Use the argument mechanism to document members or constants.
++# e.g.
++# /**
++# * struct my_struct - short description
++# * @a: first member
++# * @b: second member
++# *
++# * Longer description
++# */
++# struct my_struct {
++# int a;
++# int b;
++# /* private: */
++# int c;
++# };
++#
++# All descriptions can be multiline, except the short function description.
++#
++# For really longs structs, you can also describe arguments inside the
++# body of the struct.
++# eg.
++# /**
++# * struct my_struct - short description
++# * @a: first member
++# * @b: second member
++# *
++# * Longer description
++# */
++# struct my_struct {
++# int a;
++# int b;
++# /**
++# * @c: This is longer description of C
++# *
++# * You can use paragraphs to describe arguments
++# * using this method.
++# */
++# int c;
++# };
++#
++# This should be use only for struct/enum members.
++#
++# You can also add additional sections. When documenting kernel functions you
++# should document the "Context:" of the function, e.g. whether the functions
++# can be called form interrupts. Unlike other sections you can end it with an
++# empty line.
++# A non-void function should have a "Return:" section describing the return
++# value(s).
++# Example-sections should contain the string EXAMPLE so that they are marked
++# appropriately in DocBook.
++#
++# Example:
++# /**
++# * user_function - function that can only be called in user context
++# * @a: some argument
++# * Context: !in_interrupt()
++# *
++# * Some description
++# * Example:
++# * user_function(22);
++# */
++# ...
++#
++#
++# All descriptive text is further processed, scanning for the following special
++# patterns, which are highlighted appropriately.
++#
++# 'funcname()' - function
++# '$ENVVAR' - environmental variable
++# '&struct_name' - name of a structure (up to two words including 'struct')
++# '@parameter' - name of a parameter
++# '%CONST' - name of a constant.
++
++## init lots of data
++
++
++my $errors = 0;
++my $warnings = 0;
++my $anon_struct_union = 0;
++
++# match expressions used to find embedded type information
++my $type_constant = '\%([-_\w]+)';
++my $type_func = '(\w+)\(\)';
++my $type_param = '\@(\w+(\.\.\.)?)';
++my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
++my $type_struct = '\&((struct\s*)*[_\w]+)';
++my $type_struct_xml = '\\&amp;((struct\s*)*[_\w]+)';
++my $type_env = '(\$\w+)';
++my $type_enum_full = '\&(enum)\s*([_\w]+)';
++my $type_struct_full = '\&(struct)\s*([_\w]+)';
++my $type_typedef_full = '\&(typedef)\s*([_\w]+)';
++my $type_union_full = '\&(union)\s*([_\w]+)';
++my $type_member = '\&([_\w]+)((\.|->)[_\w]+)';
++my $type_member_func = $type_member . '\(\)';
++
++# Output conversion substitutions.
++# One for each output format
++
++# these work fairly well
++my @highlights_html = (
++ [$type_constant, "<i>\$1</i>"],
++ [$type_func, "<b>\$1</b>"],
++ [$type_struct_xml, "<i>\$1</i>"],
++ [$type_env, "<b><i>\$1</i></b>"],
++ [$type_param, "<tt><b>\$1</b></tt>"]
++ );
++my $local_lt = "\\\\\\\\lt:";
++my $local_gt = "\\\\\\\\gt:";
++my $blankline_html = $local_lt . "p" . $local_gt; # was "<p>"
++
++# html version 5
++my @highlights_html5 = (
++ [$type_constant, "<span class=\"const\">\$1</span>"],
++ [$type_func, "<span class=\"func\">\$1</span>"],
++ [$type_struct_xml, "<span class=\"struct\">\$1</span>"],
++ [$type_env, "<span class=\"env\">\$1</span>"],
++ [$type_param, "<span class=\"param\">\$1</span>]"]
++ );
++my $blankline_html5 = $local_lt . "br /" . $local_gt;
++
++# XML, docbook format
++my @highlights_xml = (
++ ["([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>"],
++ [$type_constant, "<constant>\$1</constant>"],
++ [$type_struct_xml, "<structname>\$1</structname>"],
++ [$type_param, "<parameter>\$1</parameter>"],
++ [$type_func, "<function>\$1</function>"],
++ [$type_env, "<envar>\$1</envar>"]
++ );
++my $blankline_xml = $local_lt . "/para" . $local_gt . $local_lt . "para" . $local_gt . "\n";
++
++# gnome, docbook format
++my @highlights_gnome = (
++ [$type_constant, "<replaceable class=\"option\">\$1</replaceable>"],
++ [$type_func, "<function>\$1</function>"],
++ [$type_struct, "<structname>\$1</structname>"],
++ [$type_env, "<envar>\$1</envar>"],
++ [$type_param, "<parameter>\$1</parameter>" ]
++ );
++my $blankline_gnome = "</para><para>\n";
++
++# these are pretty rough
++my @highlights_man = (
++ [$type_constant, "\$1"],
++ [$type_func, "\\\\fB\$1\\\\fP"],
++ [$type_struct, "\\\\fI\$1\\\\fP"],
++ [$type_param, "\\\\fI\$1\\\\fP"]
++ );
++my $blankline_man = "";
++
++# text-mode
++my @highlights_text = (
++ [$type_constant, "\$1"],
++ [$type_func, "\$1"],
++ [$type_struct, "\$1"],
++ [$type_param, "\$1"]
++ );
++my $blankline_text = "";
++
++# rst-mode
++my @highlights_rst = (
++ [$type_constant, "``\$1``"],
++ # Note: need to escape () to avoid func matching later
++ [$type_member_func, "\\:c\\:type\\:`\$1\$2\\\\(\\\\) <\$1>`"],
++ [$type_member, "\\:c\\:type\\:`\$1\$2 <\$1>`"],
++ [$type_fp_param, "**\$1\\\\(\\\\)**"],
++ [$type_func, "\\:c\\:func\\:`\$1()`"],
++ [$type_struct_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],
++ [$type_enum_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],
++ [$type_typedef_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],
++ [$type_union_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],
++ # in rst this can refer to any type
++ [$type_struct, "\\:c\\:type\\:`\$1`"],
++ [$type_param, "**\$1**"]
++ );
++my $blankline_rst = "\n";
++
++# list mode
++my @highlights_list = (
++ [$type_constant, "\$1"],
++ [$type_func, "\$1"],
++ [$type_struct, "\$1"],
++ [$type_param, "\$1"]
++ );
++my $blankline_list = "";
++
++# read arguments
++if ($#ARGV == -1) {
++ usage();
++}
++
++my $kernelversion;
++my $dohighlight = "";
++
++my $verbose = 0;
++my $output_mode = "man";
++my $output_preformatted = 0;
++my $no_doc_sections = 0;
++my $enable_lineno = 0;
++my @highlights = @highlights_man;
++my $blankline = $blankline_man;
++my $modulename = "Kernel API";
++
++use constant {
++ OUTPUT_ALL => 0, # output all symbols and doc sections
++ OUTPUT_INCLUDE => 1, # output only specified symbols
++ OUTPUT_EXCLUDE => 2, # output everything except specified symbols
++ OUTPUT_EXPORTED => 3, # output exported symbols
++ OUTPUT_INTERNAL => 4, # output non-exported symbols
++};
++my $output_selection = OUTPUT_ALL;
++my $show_not_found = 0;
++
++my @export_file_list;
++
++my @build_time;
++if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) &&
++ (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') {
++ @build_time = gmtime($seconds);
++} else {
++ @build_time = localtime;
++}
++
++my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',
++ 'July', 'August', 'September', 'October',
++ 'November', 'December')[$build_time[4]] .
++ " " . ($build_time[5]+1900);
++
++# Essentially these are globals.
++# They probably want to be tidied up, made more localised or something.
++# CAVEAT EMPTOR! Some of the others I localised may not want to be, which
++# could cause "use of undefined value" or other bugs.
++my ($function, %function_table, %parametertypes, $declaration_purpose);
++my $declaration_start_line;
++my ($type, $declaration_name, $return_type);
++my ($newsection, $newcontents, $prototype, $brcount, %source_map);
++
++if (defined($ENV{'KBUILD_VERBOSE'})) {
++ $verbose = "$ENV{'KBUILD_VERBOSE'}";
++}
++
++# Generated docbook code is inserted in a template at a point where
++# docbook v3.1 requires a non-zero sequence of RefEntry's; see:
++# http://www.oasis-open.org/docbook/documentation/reference/html/refentry.html
++# We keep track of number of generated entries and generate a dummy
++# if needs be to ensure the expanded template can be postprocessed
++# into html.
++my $section_counter = 0;
++
++my $lineprefix="";
++
++# Parser states
++use constant {
++ STATE_NORMAL => 0, # normal code
++ STATE_NAME => 1, # looking for function name
++ STATE_FIELD => 2, # scanning field start
++ STATE_PROTO => 3, # scanning prototype
++ STATE_DOCBLOCK => 4, # documentation block
++ STATE_INLINE => 5, # gathering documentation outside main block
++};
++my $state;
++my $in_doc_sect;
++
++# Inline documentation state
++use constant {
++ STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE)
++ STATE_INLINE_NAME => 1, # looking for member name (@foo:)
++ STATE_INLINE_TEXT => 2, # looking for member documentation
++ STATE_INLINE_END => 3, # done
++ STATE_INLINE_ERROR => 4, # error - Comment without header was found.
++ # Spit a warning as it's not
++ # proper kernel-doc and ignore the rest.
++};
++my $inline_doc_state;
++
++#declaration types: can be
++# 'function', 'struct', 'union', 'enum', 'typedef'
++my $decl_type;
++
++my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start.
++my $doc_end = '\*/';
++my $doc_com = '\s*\*\s*';
++my $doc_com_body = '\s*\* ?';
++my $doc_decl = $doc_com . '(\w+)';
++# @params and a strictly limited set of supported section names
++my $doc_sect = $doc_com .
++ '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)';
++my $doc_content = $doc_com_body . '(.*)';
++my $doc_block = $doc_com . 'DOC:\s*(.*)?';
++my $doc_inline_start = '^\s*/\*\*\s*$';
++my $doc_inline_sect = '\s*\*\s*(@[\w\s]+):(.*)';
++my $doc_inline_end = '^\s*\*/\s*$';
++my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$';
++my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;';
++
++my %parameterdescs;
++my %parameterdesc_start_lines;
++my @parameterlist;
++my %sections;
++my @sectionlist;
++my %section_start_lines;
++my $sectcheck;
++my $struct_actual;
++
++my $contents = "";
++my $new_start_line = 0;
++
++# the canonical section names. see also $doc_sect above.
++my $section_default = "Description"; # default section
++my $section_intro = "Introduction";
++my $section = $section_default;
++my $section_context = "Context";
++my $section_return = "Return";
++
++my $undescribed = "-- undescribed --";
++
++reset_state();
++
++while ($ARGV[0] =~ m/^-(.*)/) {
++ my $cmd = shift @ARGV;
++ if ($cmd eq "-html") {
++ $output_mode = "html";
++ @highlights = @highlights_html;
++ $blankline = $blankline_html;
++ } elsif ($cmd eq "-html5") {
++ $output_mode = "html5";
++ @highlights = @highlights_html5;
++ $blankline = $blankline_html5;
++ } elsif ($cmd eq "-man") {
++ $output_mode = "man";
++ @highlights = @highlights_man;
++ $blankline = $blankline_man;
++ } elsif ($cmd eq "-text") {
++ $output_mode = "text";
++ @highlights = @highlights_text;
++ $blankline = $blankline_text;
++ } elsif ($cmd eq "-rst") {
++ $output_mode = "rst";
++ @highlights = @highlights_rst;
++ $blankline = $blankline_rst;
++ } elsif ($cmd eq "-docbook") {
++ $output_mode = "xml";
++ @highlights = @highlights_xml;
++ $blankline = $blankline_xml;
++ } elsif ($cmd eq "-list") {
++ $output_mode = "list";
++ @highlights = @highlights_list;
++ $blankline = $blankline_list;
++ } elsif ($cmd eq "-gnome") {
++ $output_mode = "gnome";
++ @highlights = @highlights_gnome;
++ $blankline = $blankline_gnome;
++ } elsif ($cmd eq "-module") { # not needed for XML, inherits from calling document
++ $modulename = shift @ARGV;
++ } elsif ($cmd eq "-function") { # to only output specific functions
++ $output_selection = OUTPUT_INCLUDE;
++ $function = shift @ARGV;
++ $function_table{$function} = 1;
++ } elsif ($cmd eq "-nofunction") { # output all except specific functions
++ $output_selection = OUTPUT_EXCLUDE;
++ $function = shift @ARGV;
++ $function_table{$function} = 1;
++ } elsif ($cmd eq "-export") { # only exported symbols
++ $output_selection = OUTPUT_EXPORTED;
++ %function_table = ();
++ } elsif ($cmd eq "-internal") { # only non-exported symbols
++ $output_selection = OUTPUT_INTERNAL;
++ %function_table = ();
++ } elsif ($cmd eq "-export-file") {
++ my $file = shift @ARGV;
++ push(@export_file_list, $file);
++ } elsif ($cmd eq "-v") {
++ $verbose = 1;
++ } elsif (($cmd eq "-h") || ($cmd eq "--help")) {
++ usage();
++ } elsif ($cmd eq '-no-doc-sections') {
++ $no_doc_sections = 1;
++ } elsif ($cmd eq '-enable-lineno') {
++ $enable_lineno = 1;
++ } elsif ($cmd eq '-show-not-found') {
++ $show_not_found = 1;
++ }
++}
++
++# continue execution near EOF;
++
++# get kernel version from env
++sub get_kernel_version() {
++ my $version = 'unknown kernel version';
++
++ if (defined($ENV{'KERNELVERSION'})) {
++ $version = $ENV{'KERNELVERSION'};
++ }
++ return $version;
++}
++
++#
++sub print_lineno {
++ my $lineno = shift;
++ if ($enable_lineno && defined($lineno)) {
++ print "#define LINENO " . $lineno . "\n";
++ }
++}
++##
++# dumps section contents to arrays/hashes intended for that purpose.
++#
++sub dump_section {
++ my $file = shift;
++ my $name = shift;
++ my $contents = join "\n", @_;
++
++ if ($name =~ m/$type_param/) {
++ $name = $1;
++ $parameterdescs{$name} = $contents;
++ $sectcheck = $sectcheck . $name . " ";
++ $parameterdesc_start_lines{$name} = $new_start_line;
++ $new_start_line = 0;
++ } elsif ($name eq "@\.\.\.") {
++ $name = "...";
++ $parameterdescs{$name} = $contents;
++ $sectcheck = $sectcheck . $name . " ";
++ $parameterdesc_start_lines{$name} = $new_start_line;
++ $new_start_line = 0;
++ } else {
++ if (defined($sections{$name}) && ($sections{$name} ne "")) {
++ # Only warn on user specified duplicate section names.
++ if ($name ne $section_default) {
++ print STDERR "${file}:$.: warning: duplicate section name '$name'\n";
++ ++$warnings;
++ }
++ $sections{$name} .= $contents;
++ } else {
++ $sections{$name} = $contents;
++ push @sectionlist, $name;
++ $section_start_lines{$name} = $new_start_line;
++ $new_start_line = 0;
++ }
++ }
++}
++
++##
++# dump DOC: section after checking that it should go out
++#
++sub dump_doc_section {
++ my $file = shift;
++ my $name = shift;
++ my $contents = join "\n", @_;
++
++ if ($no_doc_sections) {
++ return;
++ }
++
++ if (($output_selection == OUTPUT_ALL) ||
++ ($output_selection == OUTPUT_INCLUDE &&
++ defined($function_table{$name})) ||
++ ($output_selection == OUTPUT_EXCLUDE &&
++ !defined($function_table{$name})))
++ {
++ dump_section($file, $name, $contents);
++ output_blockhead({'sectionlist' => \@sectionlist,
++ 'sections' => \%sections,
++ 'module' => $modulename,
++ 'content-only' => ($output_selection != OUTPUT_ALL), });
++ }
++}
++
++##
++# output function
++#
++# parameterdescs, a hash.
++# function => "function name"
++# parameterlist => @list of parameters
++# parameterdescs => %parameter descriptions
++# sectionlist => @list of sections
++# sections => %section descriptions
++#
++
++sub output_highlight {
++ my $contents = join "\n",@_;
++ my $line;
++
++# DEBUG
++# if (!defined $contents) {
++# use Carp;
++# confess "output_highlight got called with no args?\n";
++# }
++
++ if ($output_mode eq "html" || $output_mode eq "html5" ||
++ $output_mode eq "xml") {
++ $contents = local_unescape($contents);
++ # convert data read & converted thru xml_escape() into &xyz; format:
++ $contents =~ s/\\\\\\/\&/g;
++ }
++# print STDERR "contents b4:$contents\n";
++ eval $dohighlight;
++ die $@ if $@;
++# print STDERR "contents af:$contents\n";
++
++# strip whitespaces when generating html5
++ if ($output_mode eq "html5") {
++ $contents =~ s/^\s+//;
++ $contents =~ s/\s+$//;
++ }
++ foreach $line (split "\n", $contents) {
++ if (! $output_preformatted) {
++ $line =~ s/^\s*//;
++ }
++ if ($line eq ""){
++ if (! $output_preformatted) {
++ print $lineprefix, local_unescape($blankline);
++ }
++ } else {
++ $line =~ s/\\\\\\/\&/g;
++ if ($output_mode eq "man" && substr($line, 0, 1) eq ".") {
++ print "\\&$line";
++ } else {
++ print $lineprefix, $line;
++ }
++ }
++ print "\n";
++ }
++}
++
++# output sections in html
++sub output_section_html(%) {
++ my %args = %{$_[0]};
++ my $section;
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "<h3>$section</h3>\n";
++ print "<blockquote>\n";
++ output_highlight($args{'sections'}{$section});
++ print "</blockquote>\n";
++ }
++}
++
++# output enum in html
++sub output_enum_html(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $count;
++ print "<h2>enum " . $args{'enum'} . "</h2>\n";
++
++ print "<b>enum " . $args{'enum'} . "</b> {<br>\n";
++ $count = 0;
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print " <b>" . $parameter . "</b>";
++ if ($count != $#{$args{'parameterlist'}}) {
++ $count++;
++ print ",\n";
++ }
++ print "<br>";
++ }
++ print "};<br>\n";
++
++ print "<h3>Constants</h3>\n";
++ print "<dl>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "<dt><b>" . $parameter . "</b>\n";
++ print "<dd>";
++ output_highlight($args{'parameterdescs'}{$parameter});
++ }
++ print "</dl>\n";
++ output_section_html(@_);
++ print "<hr>\n";
++}
++
++# output typedef in html
++sub output_typedef_html(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $count;
++ print "<h2>typedef " . $args{'typedef'} . "</h2>\n";
++
++ print "<b>typedef " . $args{'typedef'} . "</b>\n";
++ output_section_html(@_);
++ print "<hr>\n";
++}
++
++# output struct in html
++sub output_struct_html(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++
++ print "<h2>" . $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "</h2>\n";
++ print "<b>" . $args{'type'} . " " . $args{'struct'} . "</b> {<br>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ if ($parameter =~ /^#/) {
++ print "$parameter<br>\n";
++ next;
++ }
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print "&nbsp; &nbsp; <i>$1</i><b>$parameter</b>) <i>($2)</i>;<br>\n";
++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {
++ # bitfield
++ print "&nbsp; &nbsp; <i>$1</i> <b>$parameter</b>$2;<br>\n";
++ } else {
++ print "&nbsp; &nbsp; <i>$type</i> <b>$parameter</b>;<br>\n";
++ }
++ }
++ print "};<br>\n";
++
++ print "<h3>Members</h3>\n";
++ print "<dl>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ ($parameter =~ /^#/) && next;
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ print "<dt><b>" . $parameter . "</b>\n";
++ print "<dd>";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ }
++ print "</dl>\n";
++ output_section_html(@_);
++ print "<hr>\n";
++}
++
++# output function in html
++sub output_function_html(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++
++ print "<h2>" . $args{'function'} . " - " . $args{'purpose'} . "</h2>\n";
++ print "<i>" . $args{'functiontype'} . "</i>\n";
++ print "<b>" . $args{'function'} . "</b>\n";
++ print "(";
++ $count = 0;
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print "<i>$1</i><b>$parameter</b>) <i>($2)</i>";
++ } else {
++ print "<i>" . $type . "</i> <b>" . $parameter . "</b>";
++ }
++ if ($count != $#{$args{'parameterlist'}}) {
++ $count++;
++ print ",\n";
++ }
++ }
++ print ")\n";
++
++ print "<h3>Arguments</h3>\n";
++ print "<dl>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ print "<dt><b>" . $parameter . "</b>\n";
++ print "<dd>";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ }
++ print "</dl>\n";
++ output_section_html(@_);
++ print "<hr>\n";
++}
++
++# output DOC: block header in html
++sub output_blockhead_html(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "<h3>$section</h3>\n";
++ print "<ul>\n";
++ output_highlight($args{'sections'}{$section});
++ print "</ul>\n";
++ }
++ print "<hr>\n";
++}
++
++# output sections in html5
++sub output_section_html5(%) {
++ my %args = %{$_[0]};
++ my $section;
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "<section>\n";
++ print "<h1>$section</h1>\n";
++ print "<p>\n";
++ output_highlight($args{'sections'}{$section});
++ print "</p>\n";
++ print "</section>\n";
++ }
++}
++
++# output enum in html5
++sub output_enum_html5(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $count;
++ my $html5id;
++
++ $html5id = $args{'enum'};
++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
++ print "<article class=\"enum\" id=\"enum:". $html5id . "\">";
++ print "<h1>enum " . $args{'enum'} . "</h1>\n";
++ print "<ol class=\"code\">\n";
++ print "<li>";
++ print "<span class=\"keyword\">enum</span> ";
++ print "<span class=\"identifier\">" . $args{'enum'} . "</span> {";
++ print "</li>\n";
++ $count = 0;
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "<li class=\"indent\">";
++ print "<span class=\"param\">" . $parameter . "</span>";
++ if ($count != $#{$args{'parameterlist'}}) {
++ $count++;
++ print ",";
++ }
++ print "</li>\n";
++ }
++ print "<li>};</li>\n";
++ print "</ol>\n";
++
++ print "<section>\n";
++ print "<h1>Constants</h1>\n";
++ print "<dl>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "<dt>" . $parameter . "</dt>\n";
++ print "<dd>";
++ output_highlight($args{'parameterdescs'}{$parameter});
++ print "</dd>\n";
++ }
++ print "</dl>\n";
++ print "</section>\n";
++ output_section_html5(@_);
++ print "</article>\n";
++}
++
++# output typedef in html5
++sub output_typedef_html5(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $count;
++ my $html5id;
++
++ $html5id = $args{'typedef'};
++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
++ print "<article class=\"typedef\" id=\"typedef:" . $html5id . "\">\n";
++ print "<h1>typedef " . $args{'typedef'} . "</h1>\n";
++
++ print "<ol class=\"code\">\n";
++ print "<li>";
++ print "<span class=\"keyword\">typedef</span> ";
++ print "<span class=\"identifier\">" . $args{'typedef'} . "</span>";
++ print "</li>\n";
++ print "</ol>\n";
++ output_section_html5(@_);
++ print "</article>\n";
++}
++
++# output struct in html5
++sub output_struct_html5(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $html5id;
++
++ $html5id = $args{'struct'};
++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
++ print "<article class=\"struct\" id=\"struct:" . $html5id . "\">\n";
++ print "<hgroup>\n";
++ print "<h1>" . $args{'type'} . " " . $args{'struct'} . "</h1>";
++ print "<h2>". $args{'purpose'} . "</h2>\n";
++ print "</hgroup>\n";
++ print "<ol class=\"code\">\n";
++ print "<li>";
++ print "<span class=\"type\">" . $args{'type'} . "</span> ";
++ print "<span class=\"identifier\">" . $args{'struct'} . "</span> {";
++ print "</li>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "<li class=\"indent\">";
++ if ($parameter =~ /^#/) {
++ print "<span class=\"param\">" . $parameter ."</span>\n";
++ print "</li>\n";
++ next;
++ }
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print "<span class=\"type\">$1</span> ";
++ print "<span class=\"param\">$parameter</span>";
++ print "<span class=\"type\">)</span> ";
++ print "(<span class=\"args\">$2</span>);";
++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {
++ # bitfield
++ print "<span class=\"type\">$1</span> ";
++ print "<span class=\"param\">$parameter</span>";
++ print "<span class=\"bits\">$2</span>;";
++ } else {
++ print "<span class=\"type\">$type</span> ";
++ print "<span class=\"param\">$parameter</span>;";
++ }
++ print "</li>\n";
++ }
++ print "<li>};</li>\n";
++ print "</ol>\n";
++
++ print "<section>\n";
++ print "<h1>Members</h1>\n";
++ print "<dl>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ ($parameter =~ /^#/) && next;
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ print "<dt>" . $parameter . "</dt>\n";
++ print "<dd>";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ print "</dd>\n";
++ }
++ print "</dl>\n";
++ print "</section>\n";
++ output_section_html5(@_);
++ print "</article>\n";
++}
++
++# output function in html5
++sub output_function_html5(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++ my $html5id;
++
++ $html5id = $args{'function'};
++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
++ print "<article class=\"function\" id=\"func:". $html5id . "\">\n";
++ print "<hgroup>\n";
++ print "<h1>" . $args{'function'} . "</h1>";
++ print "<h2>" . $args{'purpose'} . "</h2>\n";
++ print "</hgroup>\n";
++ print "<ol class=\"code\">\n";
++ print "<li>";
++ print "<span class=\"type\">" . $args{'functiontype'} . "</span> ";
++ print "<span class=\"identifier\">" . $args{'function'} . "</span> (";
++ print "</li>";
++ $count = 0;
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "<li class=\"indent\">";
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print "<span class=\"type\">$1</span> ";
++ print "<span class=\"param\">$parameter</span>";
++ print "<span class=\"type\">)</span> ";
++ print "(<span class=\"args\">$2</span>)";
++ } else {
++ print "<span class=\"type\">$type</span> ";
++ print "<span class=\"param\">$parameter</span>";
++ }
++ if ($count != $#{$args{'parameterlist'}}) {
++ $count++;
++ print ",";
++ }
++ print "</li>\n";
++ }
++ print "<li>)</li>\n";
++ print "</ol>\n";
++
++ print "<section>\n";
++ print "<h1>Arguments</h1>\n";
++ print "<p>\n";
++ print "<dl>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ print "<dt>" . $parameter . "</dt>\n";
++ print "<dd>";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ print "</dd>\n";
++ }
++ print "</dl>\n";
++ print "</section>\n";
++ output_section_html5(@_);
++ print "</article>\n";
++}
++
++# output DOC: block header in html5
++sub output_blockhead_html5(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++ my $html5id;
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ $html5id = $section;
++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;
++ print "<article class=\"doc\" id=\"doc:". $html5id . "\">\n";
++ print "<h1>$section</h1>\n";
++ print "<p>\n";
++ output_highlight($args{'sections'}{$section});
++ print "</p>\n";
++ }
++ print "</article>\n";
++}
++
++sub output_section_xml(%) {
++ my %args = %{$_[0]};
++ my $section;
++ # print out each section
++ $lineprefix=" ";
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "<refsect1>\n";
++ print "<title>$section</title>\n";
++ if ($section =~ m/EXAMPLE/i) {
++ print "<informalexample><programlisting>\n";
++ $output_preformatted = 1;
++ } else {
++ print "<para>\n";
++ }
++ output_highlight($args{'sections'}{$section});
++ $output_preformatted = 0;
++ if ($section =~ m/EXAMPLE/i) {
++ print "</programlisting></informalexample>\n";
++ } else {
++ print "</para>\n";
++ }
++ print "</refsect1>\n";
++ }
++}
++
++# output function in XML DocBook
++sub output_function_xml(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++ my $id;
++
++ $id = "API-" . $args{'function'};
++ $id =~ s/[^A-Za-z0-9]/-/g;
++
++ print "<refentry id=\"$id\">\n";
++ print "<refentryinfo>\n";
++ print " <title>LINUX</title>\n";
++ print " <productname>Kernel Hackers Manual</productname>\n";
++ print " <date>$man_date</date>\n";
++ print "</refentryinfo>\n";
++ print "<refmeta>\n";
++ print " <refentrytitle><phrase>" . $args{'function'} . "</phrase></refentrytitle>\n";
++ print " <manvolnum>9</manvolnum>\n";
++ print " <refmiscinfo class=\"version\">" . $kernelversion . "</refmiscinfo>\n";
++ print "</refmeta>\n";
++ print "<refnamediv>\n";
++ print " <refname>" . $args{'function'} . "</refname>\n";
++ print " <refpurpose>\n";
++ print " ";
++ output_highlight ($args{'purpose'});
++ print " </refpurpose>\n";
++ print "</refnamediv>\n";
++
++ print "<refsynopsisdiv>\n";
++ print " <title>Synopsis</title>\n";
++ print " <funcsynopsis><funcprototype>\n";
++ print " <funcdef>" . $args{'functiontype'} . " ";
++ print "<function>" . $args{'function'} . " </function></funcdef>\n";
++
++ $count = 0;
++ if ($#{$args{'parameterlist'}} >= 0) {
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print " <paramdef>$1<parameter>$parameter</parameter>)\n";
++ print " <funcparams>$2</funcparams></paramdef>\n";
++ } else {
++ print " <paramdef>" . $type;
++ print " <parameter>$parameter</parameter></paramdef>\n";
++ }
++ }
++ } else {
++ print " <void/>\n";
++ }
++ print " </funcprototype></funcsynopsis>\n";
++ print "</refsynopsisdiv>\n";
++
++ # print parameters
++ print "<refsect1>\n <title>Arguments</title>\n";
++ if ($#{$args{'parameterlist'}} >= 0) {
++ print " <variablelist>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ print " <varlistentry>\n <term><parameter>$parameter</parameter></term>\n";
++ print " <listitem>\n <para>\n";
++ $lineprefix=" ";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ print " </para>\n </listitem>\n </varlistentry>\n";
++ }
++ print " </variablelist>\n";
++ } else {
++ print " <para>\n None\n </para>\n";
++ }
++ print "</refsect1>\n";
++
++ output_section_xml(@_);
++ print "</refentry>\n\n";
++}
++
++# output struct in XML DocBook
++sub output_struct_xml(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $id;
++
++ $id = "API-struct-" . $args{'struct'};
++ $id =~ s/[^A-Za-z0-9]/-/g;
++
++ print "<refentry id=\"$id\">\n";
++ print "<refentryinfo>\n";
++ print " <title>LINUX</title>\n";
++ print " <productname>Kernel Hackers Manual</productname>\n";
++ print " <date>$man_date</date>\n";
++ print "</refentryinfo>\n";
++ print "<refmeta>\n";
++ print " <refentrytitle><phrase>" . $args{'type'} . " " . $args{'struct'} . "</phrase></refentrytitle>\n";
++ print " <manvolnum>9</manvolnum>\n";
++ print " <refmiscinfo class=\"version\">" . $kernelversion . "</refmiscinfo>\n";
++ print "</refmeta>\n";
++ print "<refnamediv>\n";
++ print " <refname>" . $args{'type'} . " " . $args{'struct'} . "</refname>\n";
++ print " <refpurpose>\n";
++ print " ";
++ output_highlight ($args{'purpose'});
++ print " </refpurpose>\n";
++ print "</refnamediv>\n";
++
++ print "<refsynopsisdiv>\n";
++ print " <title>Synopsis</title>\n";
++ print " <programlisting>\n";
++ print $args{'type'} . " " . $args{'struct'} . " {\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ if ($parameter =~ /^#/) {
++ my $prm = $parameter;
++ # convert data read & converted thru xml_escape() into &xyz; format:
++ # This allows us to have #define macros interspersed in a struct.
++ $prm =~ s/\\\\\\/\&/g;
++ print "$prm\n";
++ next;
++ }
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ defined($args{'parameterdescs'}{$parameter_name}) || next;
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print " $1 $parameter) ($2);\n";
++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {
++ # bitfield
++ print " $1 $parameter$2;\n";
++ } else {
++ print " " . $type . " " . $parameter . ";\n";
++ }
++ }
++ print "};";
++ print " </programlisting>\n";
++ print "</refsynopsisdiv>\n";
++
++ print " <refsect1>\n";
++ print " <title>Members</title>\n";
++
++ if ($#{$args{'parameterlist'}} >= 0) {
++ print " <variablelist>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ ($parameter =~ /^#/) && next;
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ defined($args{'parameterdescs'}{$parameter_name}) || next;
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ print " <varlistentry>";
++ print " <term>$parameter</term>\n";
++ print " <listitem><para>\n";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ print " </para></listitem>\n";
++ print " </varlistentry>\n";
++ }
++ print " </variablelist>\n";
++ } else {
++ print " <para>\n None\n </para>\n";
++ }
++ print " </refsect1>\n";
++
++ output_section_xml(@_);
++
++ print "</refentry>\n\n";
++}
++
++# output enum in XML DocBook
++sub output_enum_xml(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++ my $id;
++
++ $id = "API-enum-" . $args{'enum'};
++ $id =~ s/[^A-Za-z0-9]/-/g;
++
++ print "<refentry id=\"$id\">\n";
++ print "<refentryinfo>\n";
++ print " <title>LINUX</title>\n";
++ print " <productname>Kernel Hackers Manual</productname>\n";
++ print " <date>$man_date</date>\n";
++ print "</refentryinfo>\n";
++ print "<refmeta>\n";
++ print " <refentrytitle><phrase>enum " . $args{'enum'} . "</phrase></refentrytitle>\n";
++ print " <manvolnum>9</manvolnum>\n";
++ print " <refmiscinfo class=\"version\">" . $kernelversion . "</refmiscinfo>\n";
++ print "</refmeta>\n";
++ print "<refnamediv>\n";
++ print " <refname>enum " . $args{'enum'} . "</refname>\n";
++ print " <refpurpose>\n";
++ print " ";
++ output_highlight ($args{'purpose'});
++ print " </refpurpose>\n";
++ print "</refnamediv>\n";
++
++ print "<refsynopsisdiv>\n";
++ print " <title>Synopsis</title>\n";
++ print " <programlisting>\n";
++ print "enum " . $args{'enum'} . " {\n";
++ $count = 0;
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print " $parameter";
++ if ($count != $#{$args{'parameterlist'}}) {
++ $count++;
++ print ",";
++ }
++ print "\n";
++ }
++ print "};";
++ print " </programlisting>\n";
++ print "</refsynopsisdiv>\n";
++
++ print "<refsect1>\n";
++ print " <title>Constants</title>\n";
++ print " <variablelist>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ print " <varlistentry>";
++ print " <term>$parameter</term>\n";
++ print " <listitem><para>\n";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ print " </para></listitem>\n";
++ print " </varlistentry>\n";
++ }
++ print " </variablelist>\n";
++ print "</refsect1>\n";
++
++ output_section_xml(@_);
++
++ print "</refentry>\n\n";
++}
++
++# output typedef in XML DocBook
++sub output_typedef_xml(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $id;
++
++ $id = "API-typedef-" . $args{'typedef'};
++ $id =~ s/[^A-Za-z0-9]/-/g;
++
++ print "<refentry id=\"$id\">\n";
++ print "<refentryinfo>\n";
++ print " <title>LINUX</title>\n";
++ print " <productname>Kernel Hackers Manual</productname>\n";
++ print " <date>$man_date</date>\n";
++ print "</refentryinfo>\n";
++ print "<refmeta>\n";
++ print " <refentrytitle><phrase>typedef " . $args{'typedef'} . "</phrase></refentrytitle>\n";
++ print " <manvolnum>9</manvolnum>\n";
++ print "</refmeta>\n";
++ print "<refnamediv>\n";
++ print " <refname>typedef " . $args{'typedef'} . "</refname>\n";
++ print " <refpurpose>\n";
++ print " ";
++ output_highlight ($args{'purpose'});
++ print " </refpurpose>\n";
++ print "</refnamediv>\n";
++
++ print "<refsynopsisdiv>\n";
++ print " <title>Synopsis</title>\n";
++ print " <synopsis>typedef " . $args{'typedef'} . ";</synopsis>\n";
++ print "</refsynopsisdiv>\n";
++
++ output_section_xml(@_);
++
++ print "</refentry>\n\n";
++}
++
++# output in XML DocBook
++sub output_blockhead_xml(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++
++ my $id = $args{'module'};
++ $id =~ s/[^A-Za-z0-9]/-/g;
++
++ # print out each section
++ $lineprefix=" ";
++ foreach $section (@{$args{'sectionlist'}}) {
++ if (!$args{'content-only'}) {
++ print "<refsect1>\n <title>$section</title>\n";
++ }
++ if ($section =~ m/EXAMPLE/i) {
++ print "<example><para>\n";
++ $output_preformatted = 1;
++ } else {
++ print "<para>\n";
++ }
++ output_highlight($args{'sections'}{$section});
++ $output_preformatted = 0;
++ if ($section =~ m/EXAMPLE/i) {
++ print "</para></example>\n";
++ } else {
++ print "</para>";
++ }
++ if (!$args{'content-only'}) {
++ print "\n</refsect1>\n";
++ }
++ }
++
++ print "\n\n";
++}
++
++# output in XML DocBook
++sub output_function_gnome {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++ my $id;
++
++ $id = $args{'module'} . "-" . $args{'function'};
++ $id =~ s/[^A-Za-z0-9]/-/g;
++
++ print "<sect2>\n";
++ print " <title id=\"$id\">" . $args{'function'} . "</title>\n";
++
++ print " <funcsynopsis>\n";
++ print " <funcdef>" . $args{'functiontype'} . " ";
++ print "<function>" . $args{'function'} . " ";
++ print "</function></funcdef>\n";
++
++ $count = 0;
++ if ($#{$args{'parameterlist'}} >= 0) {
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print " <paramdef>$1 <parameter>$parameter</parameter>)\n";
++ print " <funcparams>$2</funcparams></paramdef>\n";
++ } else {
++ print " <paramdef>" . $type;
++ print " <parameter>$parameter</parameter></paramdef>\n";
++ }
++ }
++ } else {
++ print " <void>\n";
++ }
++ print " </funcsynopsis>\n";
++ if ($#{$args{'parameterlist'}} >= 0) {
++ print " <informaltable pgwide=\"1\" frame=\"none\" role=\"params\">\n";
++ print "<tgroup cols=\"2\">\n";
++ print "<colspec colwidth=\"2*\">\n";
++ print "<colspec colwidth=\"8*\">\n";
++ print "<tbody>\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ print " <row><entry align=\"right\"><parameter>$parameter</parameter></entry>\n";
++ print " <entry>\n";
++ $lineprefix=" ";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ print " </entry></row>\n";
++ }
++ print " </tbody></tgroup></informaltable>\n";
++ } else {
++ print " <para>\n None\n </para>\n";
++ }
++
++ # print out each section
++ $lineprefix=" ";
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "<simplesect>\n <title>$section</title>\n";
++ if ($section =~ m/EXAMPLE/i) {
++ print "<example><programlisting>\n";
++ $output_preformatted = 1;
++ } else {
++ }
++ print "<para>\n";
++ output_highlight($args{'sections'}{$section});
++ $output_preformatted = 0;
++ print "</para>\n";
++ if ($section =~ m/EXAMPLE/i) {
++ print "</programlisting></example>\n";
++ } else {
++ }
++ print " </simplesect>\n";
++ }
++
++ print "</sect2>\n\n";
++}
++
++##
++# output function in man
++sub output_function_man(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++
++ print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n";
++
++ print ".SH NAME\n";
++ print $args{'function'} . " \\- " . $args{'purpose'} . "\n";
++
++ print ".SH SYNOPSIS\n";
++ if ($args{'functiontype'} ne "") {
++ print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n";
++ } else {
++ print ".B \"" . $args{'function'} . "\n";
++ }
++ $count = 0;
++ my $parenth = "(";
++ my $post = ",";
++ foreach my $parameter (@{$args{'parameterlist'}}) {
++ if ($count == $#{$args{'parameterlist'}}) {
++ $post = ");";
++ }
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print ".BI \"" . $parenth . $1 . "\" " . $parameter . " \") (" . $2 . ")" . $post . "\"\n";
++ } else {
++ $type =~ s/([^\*])$/$1 /;
++ print ".BI \"" . $parenth . $type . "\" " . $parameter . " \"" . $post . "\"\n";
++ }
++ $count++;
++ $parenth = "";
++ }
++
++ print ".SH ARGUMENTS\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ print ".IP \"" . $parameter . "\" 12\n";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ }
++ foreach $section (@{$args{'sectionlist'}}) {
++ print ".SH \"", uc $section, "\"\n";
++ output_highlight($args{'sections'}{$section});
++ }
++}
++
++##
++# output enum in man
++sub output_enum_man(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++
++ print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n";
++
++ print ".SH NAME\n";
++ print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n";
++
++ print ".SH SYNOPSIS\n";
++ print "enum " . $args{'enum'} . " {\n";
++ $count = 0;
++ foreach my $parameter (@{$args{'parameterlist'}}) {
++ print ".br\n.BI \" $parameter\"\n";
++ if ($count == $#{$args{'parameterlist'}}) {
++ print "\n};\n";
++ last;
++ }
++ else {
++ print ", \n.br\n";
++ }
++ $count++;
++ }
++
++ print ".SH Constants\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ print ".IP \"" . $parameter . "\" 12\n";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ }
++ foreach $section (@{$args{'sectionlist'}}) {
++ print ".SH \"$section\"\n";
++ output_highlight($args{'sections'}{$section});
++ }
++}
++
++##
++# output struct in man
++sub output_struct_man(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++
++ print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n";
++
++ print ".SH NAME\n";
++ print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n";
++
++ print ".SH SYNOPSIS\n";
++ print $args{'type'} . " " . $args{'struct'} . " {\n.br\n";
++
++ foreach my $parameter (@{$args{'parameterlist'}}) {
++ if ($parameter =~ /^#/) {
++ print ".BI \"$parameter\"\n.br\n";
++ next;
++ }
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print ".BI \" " . $1 . "\" " . $parameter . " \") (" . $2 . ")" . "\"\n;\n";
++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {
++ # bitfield
++ print ".BI \" " . $1 . "\ \" " . $parameter . $2 . " \"" . "\"\n;\n";
++ } else {
++ $type =~ s/([^\*])$/$1 /;
++ print ".BI \" " . $type . "\" " . $parameter . " \"" . "\"\n;\n";
++ }
++ print "\n.br\n";
++ }
++ print "};\n.br\n";
++
++ print ".SH Members\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ ($parameter =~ /^#/) && next;
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ print ".IP \"" . $parameter . "\" 12\n";
++ output_highlight($args{'parameterdescs'}{$parameter_name});
++ }
++ foreach $section (@{$args{'sectionlist'}}) {
++ print ".SH \"$section\"\n";
++ output_highlight($args{'sections'}{$section});
++ }
++}
++
++##
++# output typedef in man
++sub output_typedef_man(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++
++ print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n";
++
++ print ".SH NAME\n";
++ print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n";
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print ".SH \"$section\"\n";
++ output_highlight($args{'sections'}{$section});
++ }
++}
++
++sub output_blockhead_man(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $count;
++
++ print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n";
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print ".SH \"$section\"\n";
++ output_highlight($args{'sections'}{$section});
++ }
++}
++
++##
++# output in text
++sub output_function_text(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $start;
++
++ print "Name:\n\n";
++ print $args{'function'} . " - " . $args{'purpose'} . "\n";
++
++ print "\nSynopsis:\n\n";
++ if ($args{'functiontype'} ne "") {
++ $start = $args{'functiontype'} . " " . $args{'function'} . " (";
++ } else {
++ $start = $args{'function'} . " (";
++ }
++ print $start;
++
++ my $count = 0;
++ foreach my $parameter (@{$args{'parameterlist'}}) {
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print $1 . $parameter . ") (" . $2;
++ } else {
++ print $type . " " . $parameter;
++ }
++ if ($count != $#{$args{'parameterlist'}}) {
++ $count++;
++ print ",\n";
++ print " " x length($start);
++ } else {
++ print ");\n\n";
++ }
++ }
++
++ print "Arguments:\n\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ print $parameter . "\n\t" . $args{'parameterdescs'}{$parameter_name} . "\n";
++ }
++ output_section_text(@_);
++}
++
++#output sections in text
++sub output_section_text(%) {
++ my %args = %{$_[0]};
++ my $section;
++
++ print "\n";
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "$section:\n\n";
++ output_highlight($args{'sections'}{$section});
++ }
++ print "\n\n";
++}
++
++# output enum in text
++sub output_enum_text(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $count;
++ print "Enum:\n\n";
++
++ print "enum " . $args{'enum'} . " - " . $args{'purpose'} . "\n\n";
++ print "enum " . $args{'enum'} . " {\n";
++ $count = 0;
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "\t$parameter";
++ if ($count != $#{$args{'parameterlist'}}) {
++ $count++;
++ print ",";
++ }
++ print "\n";
++ }
++ print "};\n\n";
++
++ print "Constants:\n\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "$parameter\n\t";
++ print $args{'parameterdescs'}{$parameter} . "\n";
++ }
++
++ output_section_text(@_);
++}
++
++# output typedef in text
++sub output_typedef_text(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $count;
++ print "Typedef:\n\n";
++
++ print "typedef " . $args{'typedef'} . " - " . $args{'purpose'} . "\n";
++ output_section_text(@_);
++}
++
++# output struct as text
++sub output_struct_text(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++
++ print $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "\n\n";
++ print $args{'type'} . " " . $args{'struct'} . " {\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ if ($parameter =~ /^#/) {
++ print "$parameter\n";
++ next;
++ }
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print "\t$1 $parameter) ($2);\n";
++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {
++ # bitfield
++ print "\t$1 $parameter$2;\n";
++ } else {
++ print "\t" . $type . " " . $parameter . ";\n";
++ }
++ }
++ print "};\n\n";
++
++ print "Members:\n\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ ($parameter =~ /^#/) && next;
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ print "$parameter\n\t";
++ print $args{'parameterdescs'}{$parameter_name} . "\n";
++ }
++ print "\n";
++ output_section_text(@_);
++}
++
++sub output_blockhead_text(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print " $section:\n";
++ print " -> ";
++ output_highlight($args{'sections'}{$section});
++ }
++}
++
++##
++# output in restructured text
++#
++
++#
++# This could use some work; it's used to output the DOC: sections, and
++# starts by putting out the name of the doc section itself, but that tends
++# to duplicate a header already in the template file.
++#
++sub output_blockhead_rst(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ if ($output_selection != OUTPUT_INCLUDE) {
++ print "**$section**\n\n";
++ }
++ print_lineno($section_start_lines{$section});
++ output_highlight_rst($args{'sections'}{$section});
++ print "\n";
++ }
++}
++
++sub output_highlight_rst {
++ my $contents = join "\n",@_;
++ my $line;
++
++ # undo the evil effects of xml_escape() earlier
++ $contents = xml_unescape($contents);
++
++ eval $dohighlight;
++ die $@ if $@;
++
++ foreach $line (split "\n", $contents) {
++ print $lineprefix . $line . "\n";
++ }
++}
++
++sub output_function_rst(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++ my $oldprefix = $lineprefix;
++ my $start = "";
++
++ if ($args{'typedef'}) {
++ print ".. c:type:: ". $args{'function'} . "\n\n";
++ print_lineno($declaration_start_line);
++ print " **Typedef**: ";
++ $lineprefix = "";
++ output_highlight_rst($args{'purpose'});
++ $start = "\n\n**Syntax**\n\n ``";
++ } else {
++ print ".. c:function:: ";
++ }
++ if ($args{'functiontype'} ne "") {
++ $start .= $args{'functiontype'} . " " . $args{'function'} . " (";
++ } else {
++ $start .= $args{'function'} . " (";
++ }
++ print $start;
++
++ my $count = 0;
++ foreach my $parameter (@{$args{'parameterlist'}}) {
++ if ($count ne 0) {
++ print ", ";
++ }
++ $count++;
++ $type = $args{'parametertypes'}{$parameter};
++
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print $1 . $parameter . ") (" . $2;
++ } else {
++ print $type . " " . $parameter;
++ }
++ }
++ if ($args{'typedef'}) {
++ print ");``\n\n";
++ } else {
++ print ")\n\n";
++ print_lineno($declaration_start_line);
++ $lineprefix = " ";
++ output_highlight_rst($args{'purpose'});
++ print "\n";
++ }
++
++ print "**Parameters**\n\n";
++ $lineprefix = " ";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ my $parameter_name = $parameter;
++ #$parameter_name =~ s/\[.*//;
++ $type = $args{'parametertypes'}{$parameter};
++
++ if ($type ne "") {
++ print "``$type $parameter``\n";
++ } else {
++ print "``$parameter``\n";
++ }
++
++ print_lineno($parameterdesc_start_lines{$parameter_name});
++
++ if (defined($args{'parameterdescs'}{$parameter_name}) &&
++ $args{'parameterdescs'}{$parameter_name} ne $undescribed) {
++ output_highlight_rst($args{'parameterdescs'}{$parameter_name});
++ } else {
++ print " *undescribed*\n";
++ }
++ print "\n";
++ }
++
++ $lineprefix = $oldprefix;
++ output_section_rst(@_);
++}
++
++sub output_section_rst(%) {
++ my %args = %{$_[0]};
++ my $section;
++ my $oldprefix = $lineprefix;
++ $lineprefix = "";
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "**$section**\n\n";
++ print_lineno($section_start_lines{$section});
++ output_highlight_rst($args{'sections'}{$section});
++ print "\n";
++ }
++ print "\n";
++ $lineprefix = $oldprefix;
++}
++
++sub output_enum_rst(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $oldprefix = $lineprefix;
++ my $count;
++ my $name = "enum " . $args{'enum'};
++
++ print "\n\n.. c:type:: " . $name . "\n\n";
++ print_lineno($declaration_start_line);
++ $lineprefix = " ";
++ output_highlight_rst($args{'purpose'});
++ print "\n";
++
++ print "**Constants**\n\n";
++ $lineprefix = " ";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ print "``$parameter``\n";
++ if ($args{'parameterdescs'}{$parameter} ne $undescribed) {
++ output_highlight_rst($args{'parameterdescs'}{$parameter});
++ } else {
++ print " *undescribed*\n";
++ }
++ print "\n";
++ }
++
++ $lineprefix = $oldprefix;
++ output_section_rst(@_);
++}
++
++sub output_typedef_rst(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $oldprefix = $lineprefix;
++ my $name = "typedef " . $args{'typedef'};
++
++ print "\n\n.. c:type:: " . $name . "\n\n";
++ print_lineno($declaration_start_line);
++ $lineprefix = " ";
++ output_highlight_rst($args{'purpose'});
++ print "\n";
++
++ $lineprefix = $oldprefix;
++ output_section_rst(@_);
++}
++
++sub output_struct_rst(%) {
++ my %args = %{$_[0]};
++ my ($parameter);
++ my $oldprefix = $lineprefix;
++ my $name = $args{'type'} . " " . $args{'struct'};
++
++ print "\n\n.. c:type:: " . $name . "\n\n";
++ print_lineno($declaration_start_line);
++ $lineprefix = " ";
++ output_highlight_rst($args{'purpose'});
++ print "\n";
++
++ print "**Definition**\n\n";
++ print "::\n\n";
++ print " " . $args{'type'} . " " . $args{'struct'} . " {\n";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ if ($parameter =~ /^#/) {
++ print " " . "$parameter\n";
++ next;
++ }
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ $type = $args{'parametertypes'}{$parameter};
++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
++ # pointer-to-function
++ print " $1 $parameter) ($2);\n";
++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {
++ # bitfield
++ print " $1 $parameter$2;\n";
++ } else {
++ print " " . $type . " " . $parameter . ";\n";
++ }
++ }
++ print " };\n\n";
++
++ print "**Members**\n\n";
++ $lineprefix = " ";
++ foreach $parameter (@{$args{'parameterlist'}}) {
++ ($parameter =~ /^#/) && next;
++
++ my $parameter_name = $parameter;
++ $parameter_name =~ s/\[.*//;
++
++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
++ $type = $args{'parametertypes'}{$parameter};
++ print_lineno($parameterdesc_start_lines{$parameter_name});
++ print "``" . $parameter . "``\n";
++ output_highlight_rst($args{'parameterdescs'}{$parameter_name});
++ print "\n";
++ }
++ print "\n";
++
++ $lineprefix = $oldprefix;
++ output_section_rst(@_);
++}
++
++
++## list mode output functions
++
++sub output_function_list(%) {
++ my %args = %{$_[0]};
++
++ print $args{'function'} . "\n";
++}
++
++# output enum in list
++sub output_enum_list(%) {
++ my %args = %{$_[0]};
++ print $args{'enum'} . "\n";
++}
++
++# output typedef in list
++sub output_typedef_list(%) {
++ my %args = %{$_[0]};
++ print $args{'typedef'} . "\n";
++}
++
++# output struct as list
++sub output_struct_list(%) {
++ my %args = %{$_[0]};
++
++ print $args{'struct'} . "\n";
++}
++
++sub output_blockhead_list(%) {
++ my %args = %{$_[0]};
++ my ($parameter, $section);
++
++ foreach $section (@{$args{'sectionlist'}}) {
++ print "DOC: $section\n";
++ }
++}
++
++##
++# generic output function for all types (function, struct/union, typedef, enum);
++# calls the generated, variable output_ function name based on
++# functype and output_mode
++sub output_declaration {
++ no strict 'refs';
++ my $name = shift;
++ my $functype = shift;
++ my $func = "output_${functype}_$output_mode";
++ if (($output_selection == OUTPUT_ALL) ||
++ (($output_selection == OUTPUT_INCLUDE ||
++ $output_selection == OUTPUT_EXPORTED) &&
++ defined($function_table{$name})) ||
++ (($output_selection == OUTPUT_EXCLUDE ||
++ $output_selection == OUTPUT_INTERNAL) &&
++ !($functype eq "function" && defined($function_table{$name}))))
++ {
++ &$func(@_);
++ $section_counter++;
++ }
++}
++
++##
++# generic output function - calls the right one based on current output mode.
++sub output_blockhead {
++ no strict 'refs';
++ my $func = "output_blockhead_" . $output_mode;
++ &$func(@_);
++ $section_counter++;
++}
++
++##
++# takes a declaration (struct, union, enum, typedef) and
++# invokes the right handler. NOT called for functions.
++sub dump_declaration($$) {
++ no strict 'refs';
++ my ($prototype, $file) = @_;
++ my $func = "dump_" . $decl_type;
++ &$func(@_);
++}
++
++sub dump_union($$) {
++ dump_struct(@_);
++}
++
++sub dump_struct($$) {
++ my $x = shift;
++ my $file = shift;
++ my $nested;
++
++ if ($x =~ /(struct|union)\s+(\w+)\s*{(.*)}/) {
++ #my $decl_type = $1;
++ $declaration_name = $2;
++ my $members = $3;
++
++ # ignore embedded structs or unions
++ $members =~ s/({.*})//g;
++ $nested = $1;
++
++ # ignore members marked private:
++ $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi;
++ $members =~ s/\/\*\s*private:.*//gosi;
++ # strip comments:
++ $members =~ s/\/\*.*?\*\///gos;
++ $nested =~ s/\/\*.*?\*\///gos;
++ # strip kmemcheck_bitfield_{begin,end}.*;
++ $members =~ s/kmemcheck_bitfield_.*?;//gos;
++ # strip attributes
++ $members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i;
++ $members =~ s/__aligned\s*\([^;]*\)//gos;
++ $members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos;
++ # replace DECLARE_BITMAP
++ $members =~ s/DECLARE_BITMAP\s*\(([^,)]+), ([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
++
++ create_parameterlist($members, ';', $file);
++ check_sections($file, $declaration_name, "struct", $sectcheck, $struct_actual, $nested);
++
++ output_declaration($declaration_name,
++ 'struct',
++ {'struct' => $declaration_name,
++ 'module' => $modulename,
++ 'parameterlist' => \@parameterlist,
++ 'parameterdescs' => \%parameterdescs,
++ 'parametertypes' => \%parametertypes,
++ 'sectionlist' => \@sectionlist,
++ 'sections' => \%sections,
++ 'purpose' => $declaration_purpose,
++ 'type' => $decl_type
++ });
++ }
++ else {
++ print STDERR "${file}:$.: error: Cannot parse struct or union!\n";
++ ++$errors;
++ }
++}
++
++sub dump_enum($$) {
++ my $x = shift;
++ my $file = shift;
++
++ $x =~ s@/\*.*?\*/@@gos; # strip comments.
++ # strip #define macros inside enums
++ $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos;
++
++ if ($x =~ /enum\s+(\w+)\s*{(.*)}/) {
++ $declaration_name = $1;
++ my $members = $2;
++
++ foreach my $arg (split ',', $members) {
++ $arg =~ s/^\s*(\w+).*/$1/;
++ push @parameterlist, $arg;
++ if (!$parameterdescs{$arg}) {
++ $parameterdescs{$arg} = $undescribed;
++ print STDERR "${file}:$.: warning: Enum value '$arg' ".
++ "not described in enum '$declaration_name'\n";
++ }
++
++ }
++
++ output_declaration($declaration_name,
++ 'enum',
++ {'enum' => $declaration_name,
++ 'module' => $modulename,
++ 'parameterlist' => \@parameterlist,
++ 'parameterdescs' => \%parameterdescs,
++ 'sectionlist' => \@sectionlist,
++ 'sections' => \%sections,
++ 'purpose' => $declaration_purpose
++ });
++ }
++ else {
++ print STDERR "${file}:$.: error: Cannot parse enum!\n";
++ ++$errors;
++ }
++}
++
++sub dump_typedef($$) {
++ my $x = shift;
++ my $file = shift;
++
++ $x =~ s@/\*.*?\*/@@gos; # strip comments.
++
++ # Parse function prototypes
++ if ($x =~ /typedef\s+(\w+)\s*\(\*\s*(\w\S+)\s*\)\s*\((.*)\);/ ||
++ $x =~ /typedef\s+(\w+)\s*(\w\S+)\s*\s*\((.*)\);/) {
++
++ # Function typedefs
++ $return_type = $1;
++ $declaration_name = $2;
++ my $args = $3;
++
++ create_parameterlist($args, ',', $file);
++
++ output_declaration($declaration_name,
++ 'function',
++ {'function' => $declaration_name,
++ 'typedef' => 1,
++ 'module' => $modulename,
++ 'functiontype' => $return_type,
++ 'parameterlist' => \@parameterlist,
++ 'parameterdescs' => \%parameterdescs,
++ 'parametertypes' => \%parametertypes,
++ 'sectionlist' => \@sectionlist,
++ 'sections' => \%sections,
++ 'purpose' => $declaration_purpose
++ });
++ return;
++ }
++
++ while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) {
++ $x =~ s/\(*.\)\s*;$/;/;
++ $x =~ s/\[*.\]\s*;$/;/;
++ }
++
++ if ($x =~ /typedef.*\s+(\w+)\s*;/) {
++ $declaration_name = $1;
++
++ output_declaration($declaration_name,
++ 'typedef',
++ {'typedef' => $declaration_name,
++ 'module' => $modulename,
++ 'sectionlist' => \@sectionlist,
++ 'sections' => \%sections,
++ 'purpose' => $declaration_purpose
++ });
++ }
++ else {
++ print STDERR "${file}:$.: error: Cannot parse typedef!\n";
++ ++$errors;
++ }
++}
++
++sub save_struct_actual($) {
++ my $actual = shift;
++
++ # strip all spaces from the actual param so that it looks like one string item
++ $actual =~ s/\s*//g;
++ $struct_actual = $struct_actual . $actual . " ";
++}
++
++sub create_parameterlist($$$) {
++ my $args = shift;
++ my $splitter = shift;
++ my $file = shift;
++ my $type;
++ my $param;
++
++ # temporarily replace commas inside function pointer definition
++ while ($args =~ /(\([^\),]+),/) {
++ $args =~ s/(\([^\),]+),/$1#/g;
++ }
++
++ foreach my $arg (split($splitter, $args)) {
++ # strip comments
++ $arg =~ s/\/\*.*\*\///;
++ # strip leading/trailing spaces
++ $arg =~ s/^\s*//;
++ $arg =~ s/\s*$//;
++ $arg =~ s/\s+/ /;
++
++ if ($arg =~ /^#/) {
++ # Treat preprocessor directive as a typeless variable just to fill
++ # corresponding data structures "correctly". Catch it later in
++ # output_* subs.
++ push_parameter($arg, "", $file);
++ } elsif ($arg =~ m/\(.+\)\s*\(/) {
++ # pointer-to-function
++ $arg =~ tr/#/,/;
++ $arg =~ m/[^\(]+\(\*?\s*(\w*)\s*\)/;
++ $param = $1;
++ $type = $arg;
++ $type =~ s/([^\(]+\(\*?)\s*$param/$1/;
++ save_struct_actual($param);
++ push_parameter($param, $type, $file);
++ } elsif ($arg) {
++ $arg =~ s/\s*:\s*/:/g;
++ $arg =~ s/\s*\[/\[/g;
++
++ my @args = split('\s*,\s*', $arg);
++ if ($args[0] =~ m/\*/) {
++ $args[0] =~ s/(\*+)\s*/ $1/;
++ }
++
++ my @first_arg;
++ if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) {
++ shift @args;
++ push(@first_arg, split('\s+', $1));
++ push(@first_arg, $2);
++ } else {
++ @first_arg = split('\s+', shift @args);
++ }
++
++ unshift(@args, pop @first_arg);
++ $type = join " ", @first_arg;
++
++ foreach $param (@args) {
++ if ($param =~ m/^(\*+)\s*(.*)/) {
++ save_struct_actual($2);
++ push_parameter($2, "$type $1", $file);
++ }
++ elsif ($param =~ m/(.*?):(\d+)/) {
++ if ($type ne "") { # skip unnamed bit-fields
++ save_struct_actual($1);
++ push_parameter($1, "$type:$2", $file)
++ }
++ }
++ else {
++ save_struct_actual($param);
++ push_parameter($param, $type, $file);
++ }
++ }
++ }
++ }
++}
++
++sub push_parameter($$$) {
++ my $param = shift;
++ my $type = shift;
++ my $file = shift;
++
++ if (($anon_struct_union == 1) && ($type eq "") &&
++ ($param eq "}")) {
++ return; # ignore the ending }; from anon. struct/union
++ }
++
++ $anon_struct_union = 0;
++ my $param_name = $param;
++ $param_name =~ s/\[.*//;
++
++ if ($type eq "" && $param =~ /\.\.\.$/)
++ {
++ if (!$param =~ /\w\.\.\.$/) {
++ # handles unnamed variable parameters
++ $param = "...";
++ }
++ if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {
++ $parameterdescs{$param} = "variable arguments";
++ }
++ }
++ elsif ($type eq "" && ($param eq "" or $param eq "void"))
++ {
++ $param="void";
++ $parameterdescs{void} = "no arguments";
++ }
++ elsif ($type eq "" && ($param eq "struct" or $param eq "union"))
++ # handle unnamed (anonymous) union or struct:
++ {
++ $type = $param;
++ $param = "{unnamed_" . $param . "}";
++ $parameterdescs{$param} = "anonymous\n";
++ $anon_struct_union = 1;
++ }
++
++ # warn if parameter has no description
++ # (but ignore ones starting with # as these are not parameters
++ # but inline preprocessor statements);
++ # also ignore unnamed structs/unions;
++ if (!$anon_struct_union) {
++ if (!defined $parameterdescs{$param_name} && $param_name !~ /^#/) {
++
++ $parameterdescs{$param_name} = $undescribed;
++
++ if (($type eq 'function') || ($type eq 'enum')) {
++ print STDERR "${file}:$.: warning: Function parameter ".
++ "or member '$param' not " .
++ "described in '$declaration_name'\n";
++ }
++ print STDERR "${file}:$.: warning:" .
++ " No description found for parameter '$param'\n";
++ ++$warnings;
++ }
++ }
++
++ $param = xml_escape($param);
++
++ # strip spaces from $param so that it is one continuous string
++ # on @parameterlist;
++ # this fixes a problem where check_sections() cannot find
++ # a parameter like "addr[6 + 2]" because it actually appears
++ # as "addr[6", "+", "2]" on the parameter list;
++ # but it's better to maintain the param string unchanged for output,
++ # so just weaken the string compare in check_sections() to ignore
++ # "[blah" in a parameter string;
++ ###$param =~ s/\s*//g;
++ push @parameterlist, $param;
++ $parametertypes{$param} = $type;
++}
++
++sub check_sections($$$$$$) {
++ my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck, $nested) = @_;
++ my @sects = split ' ', $sectcheck;
++ my @prms = split ' ', $prmscheck;
++ my $err;
++ my ($px, $sx);
++ my $prm_clean; # strip trailing "[array size]" and/or beginning "*"
++
++ foreach $sx (0 .. $#sects) {
++ $err = 1;
++ foreach $px (0 .. $#prms) {
++ $prm_clean = $prms[$px];
++ $prm_clean =~ s/\[.*\]//;
++ $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i;
++ # ignore array size in a parameter string;
++ # however, the original param string may contain
++ # spaces, e.g.: addr[6 + 2]
++ # and this appears in @prms as "addr[6" since the
++ # parameter list is split at spaces;
++ # hence just ignore "[..." for the sections check;
++ $prm_clean =~ s/\[.*//;
++
++ ##$prm_clean =~ s/^\**//;
++ if ($prm_clean eq $sects[$sx]) {
++ $err = 0;
++ last;
++ }
++ }
++ if ($err) {
++ if ($decl_type eq "function") {
++ print STDERR "${file}:$.: warning: " .
++ "Excess function parameter " .
++ "'$sects[$sx]' " .
++ "description in '$decl_name'\n";
++ ++$warnings;
++ } else {
++ if ($nested !~ m/\Q$sects[$sx]\E/) {
++ print STDERR "${file}:$.: warning: " .
++ "Excess struct/union/enum/typedef member " .
++ "'$sects[$sx]' " .
++ "description in '$decl_name'\n";
++ ++$warnings;
++ }
++ }
++ }
++ }
++}
++
++##
++# Checks the section describing the return value of a function.
++sub check_return_section {
++ my $file = shift;
++ my $declaration_name = shift;
++ my $return_type = shift;
++
++ # Ignore an empty return type (It's a macro)
++ # Ignore functions with a "void" return type. (But don't ignore "void *")
++ if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) {
++ return;
++ }
++
++ if (!defined($sections{$section_return}) ||
++ $sections{$section_return} eq "") {
++ print STDERR "${file}:$.: warning: " .
++ "No description found for return value of " .
++ "'$declaration_name'\n";
++ ++$warnings;
++ }
++}
++
++##
++# takes a function prototype and the name of the current file being
++# processed and spits out all the details stored in the global
++# arrays/hashes.
++sub dump_function($$) {
++ my $prototype = shift;
++ my $file = shift;
++ my $noret = 0;
++
++ $prototype =~ s/^static +//;
++ $prototype =~ s/^extern +//;
++ $prototype =~ s/^asmlinkage +//;
++ $prototype =~ s/^inline +//;
++ $prototype =~ s/^__inline__ +//;
++ $prototype =~ s/^__inline +//;
++ $prototype =~ s/^__always_inline +//;
++ $prototype =~ s/^noinline +//;
++ $prototype =~ s/__init +//;
++ $prototype =~ s/__init_or_module +//;
++ $prototype =~ s/__meminit +//;
++ $prototype =~ s/__must_check +//;
++ $prototype =~ s/__weak +//;
++ my $define = $prototype =~ s/^#\s*define\s+//; #ak added
++ $prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//;
++
++ # Yes, this truly is vile. We are looking for:
++ # 1. Return type (may be nothing if we're looking at a macro)
++ # 2. Function name
++ # 3. Function parameters.
++ #
++ # All the while we have to watch out for function pointer parameters
++ # (which IIRC is what the two sections are for), C types (these
++ # regexps don't even start to express all the possibilities), and
++ # so on.
++ #
++ # If you mess with these regexps, it's a good idea to check that
++ # the following functions' documentation still comes out right:
++ # - parport_register_device (function pointer parameters)
++ # - atomic_set (macro)
++ # - pci_match_device, __copy_to_user (long return type)
++
++ if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) {
++ # This is an object-like macro, it has no return type and no parameter
++ # list.
++ # Function-like macros are not allowed to have spaces between
++ # declaration_name and opening parenthesis (notice the \s+).
++ $return_type = $1;
++ $declaration_name = $2;
++ $noret = 1;
++ } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
++ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
++ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
++ $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
++ $prototype =~ m/^(\w+\s+\w+\s*\*\s*\w+\s*\*\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) {
++ $return_type = $1;
++ $declaration_name = $2;
++ my $args = $3;
++
++ create_parameterlist($args, ',', $file);
++ } else {
++ print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n";
++ return;
++ }
++
++ my $prms = join " ", @parameterlist;
++ check_sections($file, $declaration_name, "function", $sectcheck, $prms, "");
++
++ # This check emits a lot of warnings at the moment, because many
++ # functions don't have a 'Return' doc section. So until the number
++ # of warnings goes sufficiently down, the check is only performed in
++ # verbose mode.
++ # TODO: always perform the check.
++ if ($verbose && !$noret) {
++ check_return_section($file, $declaration_name, $return_type);
++ }
++
++ output_declaration($declaration_name,
++ 'function',
++ {'function' => $declaration_name,
++ 'module' => $modulename,
++ 'functiontype' => $return_type,
++ 'parameterlist' => \@parameterlist,
++ 'parameterdescs' => \%parameterdescs,
++ 'parametertypes' => \%parametertypes,
++ 'sectionlist' => \@sectionlist,
++ 'sections' => \%sections,
++ 'purpose' => $declaration_purpose
++ });
++}
++
++sub reset_state {
++ $function = "";
++ %parameterdescs = ();
++ %parametertypes = ();
++ @parameterlist = ();
++ %sections = ();
++ @sectionlist = ();
++ $sectcheck = "";
++ $struct_actual = "";
++ $prototype = "";
++
++ $state = STATE_NORMAL;
++ $inline_doc_state = STATE_INLINE_NA;
++}
++
++sub tracepoint_munge($) {
++ my $file = shift;
++ my $tracepointname = 0;
++ my $tracepointargs = 0;
++
++ if ($prototype =~ m/TRACE_EVENT\((.*?),/) {
++ $tracepointname = $1;
++ }
++ if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) {
++ $tracepointname = $1;
++ }
++ if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) {
++ $tracepointname = $2;
++ }
++ $tracepointname =~ s/^\s+//; #strip leading whitespace
++ if ($prototype =~ m/TP_PROTO\((.*?)\)/) {
++ $tracepointargs = $1;
++ }
++ if (($tracepointname eq 0) || ($tracepointargs eq 0)) {
++ print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n".
++ "$prototype\n";
++ } else {
++ $prototype = "static inline void trace_$tracepointname($tracepointargs)";
++ }
++}
++
++sub syscall_munge() {
++ my $void = 0;
++
++ $prototype =~ s@[\r\n\t]+@ @gos; # strip newlines/CR's/tabs
++## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) {
++ if ($prototype =~ m/SYSCALL_DEFINE0/) {
++ $void = 1;
++## $prototype = "long sys_$1(void)";
++ }
++
++ $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name
++ if ($prototype =~ m/long (sys_.*?),/) {
++ $prototype =~ s/,/\(/;
++ } elsif ($void) {
++ $prototype =~ s/\)/\(void\)/;
++ }
++
++ # now delete all of the odd-number commas in $prototype
++ # so that arg types & arg names don't have a comma between them
++ my $count = 0;
++ my $len = length($prototype);
++ if ($void) {
++ $len = 0; # skip the for-loop
++ }
++ for (my $ix = 0; $ix < $len; $ix++) {
++ if (substr($prototype, $ix, 1) eq ',') {
++ $count++;
++ if ($count % 2 == 1) {
++ substr($prototype, $ix, 1) = ' ';
++ }
++ }
++ }
++}
++
++sub process_proto_function($$) {
++ my $x = shift;
++ my $file = shift;
++
++ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
++
++ if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) {
++ # do nothing
++ }
++ elsif ($x =~ /([^\{]*)/) {
++ $prototype .= $1;
++ }
++
++ if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) {
++ $prototype =~ s@/\*.*?\*/@@gos; # strip comments.
++ $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
++ $prototype =~ s@^\s+@@gos; # strip leading spaces
++ if ($prototype =~ /SYSCALL_DEFINE/) {
++ syscall_munge();
++ }
++ if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ ||
++ $prototype =~ /DEFINE_SINGLE_EVENT/)
++ {
++ tracepoint_munge($file);
++ }
++ dump_function($prototype, $file);
++ reset_state();
++ }
++}
++
++sub process_proto_type($$) {
++ my $x = shift;
++ my $file = shift;
++
++ $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
++ $x =~ s@^\s+@@gos; # strip leading spaces
++ $x =~ s@\s+$@@gos; # strip trailing spaces
++ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
++
++ if ($x =~ /^#/) {
++ # To distinguish preprocessor directive from regular declaration later.
++ $x .= ";";
++ }
++
++ while (1) {
++ if ( $x =~ /([^{};]*)([{};])(.*)/ ) {
++ $prototype .= $1 . $2;
++ ($2 eq '{') && $brcount++;
++ ($2 eq '}') && $brcount--;
++ if (($2 eq ';') && ($brcount == 0)) {
++ dump_declaration($prototype, $file);
++ reset_state();
++ last;
++ }
++ $x = $3;
++ } else {
++ $prototype .= $x;
++ last;
++ }
++ }
++}
++
++# xml_escape: replace <, >, and & in the text stream;
++#
++# however, formatting controls that are generated internally/locally in the
++# kernel-doc script are not escaped here; instead, they begin life like
++# $blankline_html (4 of '\' followed by a mnemonic + ':'), then these strings
++# are converted to their mnemonic-expected output, without the 4 * '\' & ':',
++# just before actual output; (this is done by local_unescape())
++sub xml_escape($) {
++ my $text = shift;
++ if (($output_mode eq "text") || ($output_mode eq "man")) {
++ return $text;
++ }
++ $text =~ s/\&/\\\\\\amp;/g;
++ $text =~ s/\</\\\\\\lt;/g;
++ $text =~ s/\>/\\\\\\gt;/g;
++ return $text;
++}
++
++# xml_unescape: reverse the effects of xml_escape
++sub xml_unescape($) {
++ my $text = shift;
++ if (($output_mode eq "text") || ($output_mode eq "man")) {
++ return $text;
++ }
++ $text =~ s/\\\\\\amp;/\&/g;
++ $text =~ s/\\\\\\lt;/</g;
++ $text =~ s/\\\\\\gt;/>/g;
++ return $text;
++}
++
++# convert local escape strings to html
++# local escape strings look like: '\\\\menmonic:' (that's 4 backslashes)
++sub local_unescape($) {
++ my $text = shift;
++ if (($output_mode eq "text") || ($output_mode eq "man")) {
++ return $text;
++ }
++ $text =~ s/\\\\\\\\lt:/</g;
++ $text =~ s/\\\\\\\\gt:/>/g;
++ return $text;
++}
++
++sub map_filename($) {
++ my $file;
++ my ($orig_file) = @_;
++
++ if (defined($ENV{'SRCTREE'})) {
++ $file = "$ENV{'SRCTREE'}" . "/" . $orig_file;
++ } else {
++ $file = $orig_file;
++ }
++
++ if (defined($source_map{$file})) {
++ $file = $source_map{$file};
++ }
++
++ return $file;
++}
++
++sub process_export_file($) {
++ my ($orig_file) = @_;
++ my $file = map_filename($orig_file);
++
++ if (!open(IN,"<$file")) {
++ print STDERR "Error: Cannot open file $file\n";
++ ++$errors;
++ return;
++ }
++
++ while (<IN>) {
++ if (/$export_symbol/) {
++ $function_table{$2} = 1;
++ }
++ }
++
++ close(IN);
++}
++
++sub process_file($) {
++ my $file;
++ my $identifier;
++ my $func;
++ my $descr;
++ my $in_purpose = 0;
++ my $initial_section_counter = $section_counter;
++ my ($orig_file) = @_;
++ my $leading_space;
++
++ $file = map_filename($orig_file);
++
++ if (!open(IN,"<$file")) {
++ print STDERR "Error: Cannot open file $file\n";
++ ++$errors;
++ return;
++ }
++
++ $. = 1;
++
++ $section_counter = 0;
++ while (<IN>) {
++ while (s/\\\s*$//) {
++ $_ .= <IN>;
++ }
++ if ($state == STATE_NORMAL) {
++ if (/$doc_start/o) {
++ $state = STATE_NAME; # next line is always the function name
++ $in_doc_sect = 0;
++ $declaration_start_line = $. + 1;
++ }
++ } elsif ($state == STATE_NAME) {# this line is the function name (always)
++ if (/$doc_block/o) {
++ $state = STATE_DOCBLOCK;
++ $contents = "";
++ $new_start_line = $. + 1;
++
++ if ( $1 eq "" ) {
++ $section = $section_intro;
++ } else {
++ $section = $1;
++ }
++ }
++ elsif (/$doc_decl/o) {
++ $identifier = $1;
++ if (/\s*([\w\s]+?)\s*-/) {
++ $identifier = $1;
++ }
++
++ $state = STATE_FIELD;
++ # if there's no @param blocks need to set up default section
++ # here
++ $contents = "";
++ $section = $section_default;
++ $new_start_line = $. + 1;
++ if (/-(.*)/) {
++ # strip leading/trailing/multiple spaces
++ $descr= $1;
++ $descr =~ s/^\s*//;
++ $descr =~ s/\s*$//;
++ $descr =~ s/\s+/ /g;
++ $declaration_purpose = xml_escape($descr);
++ $in_purpose = 1;
++ } else {
++ $declaration_purpose = "";
++ }
++
++ if (($declaration_purpose eq "") && $verbose) {
++ print STDERR "${file}:$.: warning: missing initial short description on line:\n";
++ print STDERR $_;
++ ++$warnings;
++ }
++
++ if ($identifier =~ m/^struct/) {
++ $decl_type = 'struct';
++ } elsif ($identifier =~ m/^union/) {
++ $decl_type = 'union';
++ } elsif ($identifier =~ m/^enum/) {
++ $decl_type = 'enum';
++ } elsif ($identifier =~ m/^typedef/) {
++ $decl_type = 'typedef';
++ } else {
++ $decl_type = 'function';
++ }
++
++ if ($verbose) {
++ print STDERR "${file}:$.: info: Scanning doc for $identifier\n";
++ }
++ } else {
++ print STDERR "${file}:$.: warning: Cannot understand $_ on line $.",
++ " - I thought it was a doc line\n";
++ ++$warnings;
++ $state = STATE_NORMAL;
++ }
++ } elsif ($state == STATE_FIELD) { # look for head: lines, and include content
++ if (/$doc_sect/i) { # case insensitive for supported section names
++ $newsection = $1;
++ $newcontents = $2;
++
++ # map the supported section names to the canonical names
++ if ($newsection =~ m/^description$/i) {
++ $newsection = $section_default;
++ } elsif ($newsection =~ m/^context$/i) {
++ $newsection = $section_context;
++ } elsif ($newsection =~ m/^returns?$/i) {
++ $newsection = $section_return;
++ } elsif ($newsection =~ m/^\@return$/) {
++ # special: @return is a section, not a param description
++ $newsection = $section_return;
++ }
++
++ if (($contents ne "") && ($contents ne "\n")) {
++ if (!$in_doc_sect && $verbose) {
++ print STDERR "${file}:$.: warning: contents before sections\n";
++ ++$warnings;
++ }
++ dump_section($file, $section, xml_escape($contents));
++ $section = $section_default;
++ }
++
++ $in_doc_sect = 1;
++ $in_purpose = 0;
++ $contents = $newcontents;
++ $new_start_line = $.;
++ while ((substr($contents, 0, 1) eq " ") ||
++ substr($contents, 0, 1) eq "\t") {
++ $contents = substr($contents, 1);
++ }
++ if ($contents ne "") {
++ $contents .= "\n";
++ }
++ $section = $newsection;
++ $leading_space = undef;
++ } elsif (/$doc_end/) {
++ if (($contents ne "") && ($contents ne "\n")) {
++ dump_section($file, $section, xml_escape($contents));
++ $section = $section_default;
++ $contents = "";
++ }
++ # look for doc_com + <text> + doc_end:
++ if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') {
++ print STDERR "${file}:$.: warning: suspicious ending line: $_";
++ ++$warnings;
++ }
++
++ $prototype = "";
++ $state = STATE_PROTO;
++ $brcount = 0;
++# print STDERR "end of doc comment, looking for prototype\n";
++ } elsif (/$doc_content/) {
++ # miguel-style comment kludge, look for blank lines after
++ # @parameter line to signify start of description
++ if ($1 eq "") {
++ if ($section =~ m/^@/ || $section eq $section_context) {
++ dump_section($file, $section, xml_escape($contents));
++ $section = $section_default;
++ $contents = "";
++ $new_start_line = $.;
++ } else {
++ $contents .= "\n";
++ }
++ $in_purpose = 0;
++ } elsif ($in_purpose == 1) {
++ # Continued declaration purpose
++ chomp($declaration_purpose);
++ $declaration_purpose .= " " . xml_escape($1);
++ $declaration_purpose =~ s/\s+/ /g;
++ } else {
++ my $cont = $1;
++ if ($section =~ m/^@/ || $section eq $section_context) {
++ if (!defined $leading_space) {
++ if ($cont =~ m/^(\s+)/) {
++ $leading_space = $1;
++ } else {
++ $leading_space = "";
++ }
++ }
++
++ $cont =~ s/^$leading_space//;
++ }
++ $contents .= $cont . "\n";
++ }
++ } else {
++ # i dont know - bad line? ignore.
++ print STDERR "${file}:$.: warning: bad line: $_";
++ ++$warnings;
++ }
++ } elsif ($state == STATE_INLINE) { # scanning for inline parameters
++ # First line (state 1) needs to be a @parameter
++ if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) {
++ $section = $1;
++ $contents = $2;
++ $new_start_line = $.;
++ if ($contents ne "") {
++ while ((substr($contents, 0, 1) eq " ") ||
++ substr($contents, 0, 1) eq "\t") {
++ $contents = substr($contents, 1);
++ }
++ $contents .= "\n";
++ }
++ $inline_doc_state = STATE_INLINE_TEXT;
++ # Documentation block end */
++ } elsif (/$doc_inline_end/) {
++ if (($contents ne "") && ($contents ne "\n")) {
++ dump_section($file, $section, xml_escape($contents));
++ $section = $section_default;
++ $contents = "";
++ }
++ $state = STATE_PROTO;
++ $inline_doc_state = STATE_INLINE_NA;
++ # Regular text
++ } elsif (/$doc_content/) {
++ if ($inline_doc_state == STATE_INLINE_TEXT) {
++ $contents .= $1 . "\n";
++ # nuke leading blank lines
++ if ($contents =~ /^\s*$/) {
++ $contents = "";
++ }
++ } elsif ($inline_doc_state == STATE_INLINE_NAME) {
++ $inline_doc_state = STATE_INLINE_ERROR;
++ print STDERR "${file}:$.: warning: ";
++ print STDERR "Incorrect use of kernel-doc format: $_";
++ ++$warnings;
++ }
++ }
++ } elsif ($state == STATE_PROTO) { # scanning for function '{' (end of prototype)
++ if (/$doc_inline_oneline/) {
++ $section = $1;
++ $contents = $2;
++ if ($contents ne "") {
++ $contents .= "\n";
++ dump_section($file, $section, xml_escape($contents));
++ $section = $section_default;
++ $contents = "";
++ }
++ } elsif (/$doc_inline_start/) {
++ $state = STATE_INLINE;
++ $inline_doc_state = STATE_INLINE_NAME;
++ } elsif ($decl_type eq 'function') {
++ process_proto_function($_, $file);
++ } else {
++ process_proto_type($_, $file);
++ }
++ } elsif ($state == STATE_DOCBLOCK) {
++ if (/$doc_end/)
++ {
++ dump_doc_section($file, $section, xml_escape($contents));
++ $section = $section_default;
++ $contents = "";
++ $function = "";
++ %parameterdescs = ();
++ %parametertypes = ();
++ @parameterlist = ();
++ %sections = ();
++ @sectionlist = ();
++ $prototype = "";
++ $state = STATE_NORMAL;
++ }
++ elsif (/$doc_content/)
++ {
++ if ( $1 eq "" )
++ {
++ $contents .= $blankline;
++ }
++ else
++ {
++ $contents .= $1 . "\n";
++ }
++ }
++ }
++ }
++ if ($initial_section_counter == $section_counter) {
++ print STDERR "${file}:1: warning: no structured comments found\n";
++ if (($output_selection == OUTPUT_INCLUDE) && ($show_not_found == 1)) {
++ print STDERR " Was looking for '$_'.\n" for keys %function_table;
++ }
++ if ($output_mode eq "xml") {
++ # The template wants at least one RefEntry here; make one.
++ print "<refentry>\n";
++ print " <refnamediv>\n";
++ print " <refname>\n";
++ print " ${orig_file}\n";
++ print " </refname>\n";
++ print " <refpurpose>\n";
++ print " Document generation inconsistency\n";
++ print " </refpurpose>\n";
++ print " </refnamediv>\n";
++ print " <refsect1>\n";
++ print " <title>\n";
++ print " Oops\n";
++ print " </title>\n";
++ print " <warning>\n";
++ print " <para>\n";
++ print " The template for this document tried to insert\n";
++ print " the structured comment from the file\n";
++ print " <filename>${orig_file}</filename> at this point,\n";
++ print " but none was found.\n";
++ print " This dummy section is inserted to allow\n";
++ print " generation to continue.\n";
++ print " </para>\n";
++ print " </warning>\n";
++ print " </refsect1>\n";
++ print "</refentry>\n";
++ }
++ }
++}
++
++
++$kernelversion = get_kernel_version();
++
++# generate a sequence of code that will splice in highlighting information
++# using the s// operator.
++for (my $k = 0; $k < @highlights; $k++) {
++ my $pattern = $highlights[$k][0];
++ my $result = $highlights[$k][1];
++# print STDERR "scanning pattern:$pattern, highlight:($result)\n";
++ $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n";
++}
++
++# Read the file that maps relative names to absolute names for
++# separate source and object directories and for shadow trees.
++if (open(SOURCE_MAP, "<.tmp_filelist.txt")) {
++ my ($relname, $absname);
++ while(<SOURCE_MAP>) {
++ chop();
++ ($relname, $absname) = (split())[0..1];
++ $relname =~ s:^/+::;
++ $source_map{$relname} = $absname;
++ }
++ close(SOURCE_MAP);
++}
++
++if ($output_selection == OUTPUT_EXPORTED ||
++ $output_selection == OUTPUT_INTERNAL) {
++
++ push(@export_file_list, @ARGV);
++
++ foreach (@export_file_list) {
++ chomp;
++ process_export_file($_);
++ }
++}
++
++foreach (@ARGV) {
++ chomp;
++ process_file($_);
++}
++if ($verbose && $errors) {
++ print STDERR "$errors errors\n";
++}
++if ($verbose && $warnings) {
++ print STDERR "$warnings warnings\n";
++}
++
++exit($errors);
+Index: multipath-tools-130222/libdmmp/docs/libdmmp.h.3
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/docs/libdmmp.h.3
+@@ -0,0 +1,113 @@
++.TH "libdmmp.h" 3 "January 2016" "Device Mapper Multipath API - libdmmp Manual"
++
++.SH NAME
++libdmmp.h \- Device Mapper Multipath API.
++
++.SH SYNOPSIS
++#include <libdmmp/libdmmp.h>
++
++.SH "DESCRIPTION"
++
++All the libdmmp public functions ships its own man pages.
++Use 'man 3 <function_name>' to check the detail usage.
++
++.SH "USAGE"
++
++To use libdmmp in your project, we suggest to use the 'pkg-config' way:
++
++ * Add this line into your configure.ac:
++
++ PKG_CHECK_MODULES([LIBDMMP], [libdmmp])
++
++ * Add these lines into your Makefile.am:
++
++ foo_LDFLAGS += $(LIBDMMP_LIBS)
++ foo_CFLAGS += $(LIBDMMP_CFLAGS)
++
++.SH LOG HANDLING
++
++The log handler function could be set via 'dmmp_context_log_func_set()'.
++The log priority could be set via 'dmmp_context_log_priority_set()'.
++
++By default, the log priorities is 'DMMP_LOG_PRIORITY_WARNING'.
++By default, the log handler is print log to STDERR, and its code is listed
++below in case you want to create your own log handler.
++
++ static int _DMMP_LOG_STRERR_ALIGN_WIDTH = 80;
++
++ static void _log_stderr(struct dmmp_context *ctx,
++ enum dmmp_log_priority priority,
++ const char *file, int line,
++ const char *func_name,
++ const char *format, va_list args)
++ {
++ int printed_bytes = 0;
++
++ printed_bytes += fprintf(stderr, "libdmmp %s: ",
++ dmmp_log_priority_str(priority));
++ printed_bytes += vfprintf(stderr, format, args);
++ userdata = dmmp_context_userdata_get(ctx);
++ if (userdata != NULL)
++ fprintf(stderr, "(with user data at memory address %p)",
++ userdata);
++
++ if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) {
++ fprintf(stderr, "%*s # %s:%s():%d\n",
++ _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file,
++ func_name, line);
++ } else {
++ fprintf(stderr, " # %s:%s():%d\n", file, func_name, line);
++ }
++ }
++
++
++.SH "SAMPLE CODE"
++
++ #include <libdmmp/libdmmp.h>
++
++ int main(int argc, char *argv[]) {
++ struct dmmp_context *ctx = NULL;
++ struct dmmp_mpath **dmmp_mps = NULL;
++ struct dmmp_path_group **dmmp_pgs = NULL;
++ struct dmmp_path **dmmp_ps = NULL;
++ uint32_t dmmp_mp_count = 0;
++ uint32_t dmmp_pg_count = 0;
++ uint32_t dmmp_p_count = 0;
++ const char *name = NULL;
++ const char *wwid = NULL;
++ uint32_t i = 0;
++ int rc = DMMP_OK;
++
++ ctx = dmmp_context_new();
++ dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG);
++ // By default, log will be printed to STDERR, you could
++ // change that via dmmp_context_log_func_set()
++ rc = dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count);
++ if (rc != DMMP_OK) {
++ printf("dmmp_mpath_array_get() failed with %d: %s", rc,
++ dmmp_strerror(rc));
++ goto out;
++ }
++ for (i = 0; i < dmmp_mp_count; ++i) {
++ name = dmmp_mpath_name_get(dmmp_mps[i]);
++ wwid = dmmp_mpath_wwid_get(dmmp_mps[i]);
++ printf("dmmp_mpath_array_get(): Got mpath: %s %s\n", name,
++ wwid);
++ // You could use dmmp_path_group_array_get() to retrieve
++ // path group information and then invoke dmmp_path_array_get()
++ // for path information.
++ }
++
++ out:
++ dmmp_context_free(ctx);
++ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count);
++ if (rc != DMMP_OK)
++ exit(1);
++ exit(0);
++ }
++
++.SH "LICENSE"
++GPLv2+
++
++.SH "BUG"
++Please report bug to <dm-devel@redhat.com>
+Index: multipath-tools-130222/libdmmp/docs/split-man.pl
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/docs/split-man.pl
+@@ -0,0 +1,40 @@
++#!/usr/bin/perl
++# Originally From:
++# https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt
++#
++# Changes:
++# * Create manpage section 3 instead of 9.
++# * Replace 'Kernel Hackers Manual' to
++# 'Device Mapper Multipath API - libdmmp Manual'
++# * Remove LINUX from header.
++# * Remove DMMP_DLL_EXPORT.
++$man_sec_num = 3;
++$title = 'Device Mapper Multipath API - libdmmp Manual';
++
++if ( $#ARGV < 0 ) {
++ die "where do I put the results?\n";
++}
++
++mkdir $ARGV[0], 0777;
++$state = 0;
++while (<STDIN>) {
++ if (/^\.TH \"[^\"]*\" 9 \"([^\"]*)\"/) {
++ if ( $state == 1 ) { close OUT }
++ $state = 1;
++ $fn = "$ARGV[0]/$1.$man_sec_num";
++ print STDERR "Creating $fn\n";
++ open OUT, ">$fn" or die "can't open $fn: $!\n";
++
++ # Change man page code from 9 to $man_sec_num;
++ s/^\.TH (\"[^\"]*\") 9 \"([^\"]*)\"/\.TH $1 $man_sec_num \"$2\"/;
++ s/Kernel Hacker's Manual/$title/g;
++ s/LINUX//g;
++
++ print OUT $_;
++ }
++ elsif ( $state != 0 ) {
++ print OUT $_;
++ }
++}
++
++close OUT;
+Index: multipath-tools-130222/libdmmp/libdmmp.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp.c
+@@ -0,0 +1,285 @@
++/*
++ * Copyright (C) 2015 - 2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ * Todd Gill <tgill@redhat.com>
++ */
++
++#include <stdint.h>
++#include <string.h>
++#include <sys/time.h>
++#include <sys/resource.h>
++#include <libudev.h>
++#include <errno.h>
++#include <libdevmapper.h>
++#include <stdbool.h>
++#include <unistd.h>
++#include <assert.h>
++#include <json.h>
++#include <mpath_cmd.h>
++
++#include "libdmmp/libdmmp.h"
++#include "libdmmp_private.h"
++
++#define _DEFAULT_UXSOCK_TIMEOUT 60000
++/* ^ 60 seconds. On system with 10k sdX, dmmp_mpath_array_get()
++ * only take 3.5 seconds, so this default value should be OK for most users.
++ */
++
++#define _DMMP_IPC_SHOW_JSON_CMD "show maps json"
++#define _DMMP_JSON_MAJOR_KEY "major_version"
++#define _DMMP_JSON_MAJOR_VERSION 0
++#define _DMMP_JSON_MAPS_KEY "maps"
++#define _ERRNO_STR_BUFF_SIZE 256
++
++struct dmmp_context {
++ void (*log_func)(struct dmmp_context *ctx, int priority,
++ const char *file, int line, const char *func_name,
++ const char *format, va_list args);
++ int log_priority;
++ void *userdata;
++ unsigned int tmo;
++};
++
++_dmmp_getter_func_gen(dmmp_context_log_priority_get,
++ struct dmmp_context, ctx, log_priority,
++ int);
++
++_dmmp_getter_func_gen(dmmp_context_userdata_get, struct dmmp_context, ctx,
++ userdata, void *);
++
++_dmmp_getter_func_gen(dmmp_context_timeout_get, struct dmmp_context, ctx, tmo,
++ unsigned int);
++
++_dmmp_array_free_func_gen(dmmp_mpath_array_free, struct dmmp_mpath,
++ _dmmp_mpath_free);
++
++void _dmmp_log(struct dmmp_context *ctx, int priority, const char *file,
++ int line, const char *func_name, const char *format, ...)
++{
++ va_list args;
++
++ va_start(args, format);
++ ctx->log_func(ctx, priority, file, line, func_name, format, args);
++ va_end(args);
++}
++
++struct dmmp_context *dmmp_context_new(void)
++{
++ struct dmmp_context *ctx = NULL;
++
++ ctx = (struct dmmp_context *) malloc(sizeof(struct dmmp_context));
++
++ if (ctx == NULL)
++ return NULL;
++
++ ctx->log_func = _dmmp_log_stderr;
++ ctx->log_priority = DMMP_LOG_PRIORITY_DEFAULT;
++ ctx->userdata = NULL;
++ ctx->tmo = _DEFAULT_UXSOCK_TIMEOUT;
++
++ return ctx;
++}
++
++void dmmp_context_free(struct dmmp_context *ctx)
++{
++ free(ctx);
++}
++
++void dmmp_context_log_priority_set(struct dmmp_context *ctx, int priority)
++{
++ assert(ctx != NULL);
++ ctx->log_priority = priority;
++}
++
++void dmmp_context_timeout_set(struct dmmp_context *ctx, unsigned int tmo)
++{
++ assert(ctx != NULL);
++ ctx->tmo = tmo;
++}
++
++void dmmp_context_log_func_set
++ (struct dmmp_context *ctx,
++ void (*log_func)(struct dmmp_context *ctx, int priority,
++ const char *file, int line, const char *func_name,
++ const char *format, va_list args))
++{
++ assert(ctx != NULL);
++ ctx->log_func = log_func;
++}
++
++void dmmp_context_userdata_set(struct dmmp_context *ctx, void *userdata)
++{
++ assert(ctx != NULL);
++ ctx->userdata = userdata;
++}
++
++int dmmp_mpath_array_get(struct dmmp_context *ctx,
++ struct dmmp_mpath ***dmmp_mps, uint32_t *dmmp_mp_count)
++{
++ struct dmmp_mpath *dmmp_mp = NULL;
++ int rc = DMMP_OK;
++ char *j_str = NULL;
++ json_object *j_obj = NULL;
++ json_object *j_obj_map = NULL;
++ enum json_tokener_error j_err = json_tokener_success;
++ json_tokener *j_token = NULL;
++ struct array_list *ar_maps = NULL;
++ uint32_t i = 0;
++ int cur_json_major_version = -1;
++ int ar_maps_len = -1;
++ int socket_fd = -1;
++ int errno_save = 0;
++ char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
++
++ assert(ctx != NULL);
++ assert(dmmp_mps != NULL);
++ assert(dmmp_mp_count != NULL);
++
++ *dmmp_mps = NULL;
++ *dmmp_mp_count = 0;
++
++ socket_fd = mpath_connect();
++ if (socket_fd == -1) {
++ errno_save = errno;
++ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
++ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
++ if (errno_save == ECONNREFUSED) {
++ rc = DMMP_ERR_NO_DAEMON;
++ _error(ctx, "Socket connection refuse. "
++ "Maybe multipathd daemon is not running");
++ } else {
++ _error(ctx, "IPC failed with error %d(%s)", errno_save,
++ errno_str_buff);
++ rc = DMMP_ERR_IPC_ERROR;
++ }
++ goto out;
++ }
++
++ if (mpath_process_cmd(socket_fd, _DMMP_IPC_SHOW_JSON_CMD,
++ &j_str, ctx->tmo) != 0) {
++ errno_save = errno;
++ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
++ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
++ if (errno_save == ETIMEDOUT) {
++ rc = DMMP_ERR_IPC_TIMEOUT;
++ _error(ctx, "IPC communication timeout, try to "
++ "increase it via dmmp_context_timeout_set()");
++ goto out;
++ }
++ _error(ctx, "IPC failed when process command '%s' with "
++ "error %d(%s)", _DMMP_IPC_SHOW_JSON_CMD, errno_save,
++ errno_str_buff);
++ rc = DMMP_ERR_IPC_ERROR;
++ goto out;
++ }
++
++ if ((j_str == NULL) || (strlen(j_str) == 0)) {
++ _error(ctx, "IPC return empty reply for command %s",
++ _DMMP_IPC_SHOW_JSON_CMD);
++ rc = DMMP_ERR_IPC_ERROR;
++ goto out;
++ }
++
++ _debug(ctx, "Got json output from multipathd: '%s'", j_str);
++ j_token = json_tokener_new();
++ if (j_token == NULL) {
++ rc = DMMP_ERR_BUG;
++ _error(ctx, "BUG: json_tokener_new() retuned NULL");
++ goto out;
++ }
++ j_obj = json_tokener_parse_ex(j_token, j_str, strlen(j_str) + 1);
++
++ if (j_obj == NULL) {
++ rc = DMMP_ERR_IPC_ERROR;
++ j_err = json_tokener_get_error(j_token);
++ _error(ctx, "Failed to parse JSON output from multipathd IPC: "
++ "%s", json_tokener_error_desc(j_err));
++ goto out;
++ }
++
++ _json_obj_get_value(ctx, j_obj, cur_json_major_version,
++ _DMMP_JSON_MAJOR_KEY, json_type_int,
++ json_object_get_int, rc, out);
++
++ if (cur_json_major_version != _DMMP_JSON_MAJOR_VERSION) {
++ rc = DMMP_ERR_INCOMPATIBLE;
++ _error(ctx, "Incompatible multipathd JSON major version %d, "
++ "should be %d", cur_json_major_version,
++ _DMMP_JSON_MAJOR_VERSION);
++ goto out;
++ }
++ _debug(ctx, "multipathd JSON major version(%d) check pass",
++ _DMMP_JSON_MAJOR_VERSION);
++
++ _json_obj_get_value(ctx, j_obj, ar_maps, _DMMP_JSON_MAPS_KEY,
++ json_type_array, json_object_get_array, rc, out);
++
++ if (ar_maps == NULL) {
++ rc = DMMP_ERR_BUG;
++ _error(ctx, "BUG: Got NULL map array from "
++ "_json_obj_get_value()");
++ goto out;
++ }
++
++ ar_maps_len = array_list_length(ar_maps);
++ if (ar_maps_len < 0) {
++ rc = DMMP_ERR_BUG;
++ _error(ctx, "BUG: Got negative length for ar_maps");
++ goto out;
++ }
++ else if (ar_maps_len == 0)
++ goto out;
++ else
++ *dmmp_mp_count = ar_maps_len & UINT32_MAX;
++
++ *dmmp_mps = (struct dmmp_mpath **)
++ malloc(sizeof(struct dmmp_mpath *) * (*dmmp_mp_count));
++ _dmmp_alloc_null_check(ctx, dmmp_mps, rc, out);
++ for (; i < *dmmp_mp_count; ++i)
++ (*dmmp_mps)[i] = NULL;
++
++ for (i = 0; i < *dmmp_mp_count; ++i) {
++ j_obj_map = array_list_get_idx(ar_maps, i);
++ if (j_obj_map == NULL) {
++ rc = DMMP_ERR_BUG;
++ _error(ctx, "BUG: array_list_get_idx() return NULL");
++ goto out;
++ }
++
++ dmmp_mp = _dmmp_mpath_new();
++ _dmmp_alloc_null_check(ctx, dmmp_mp, rc, out);
++ (*dmmp_mps)[i] = dmmp_mp;
++ _good(_dmmp_mpath_update(ctx, dmmp_mp, j_obj_map), rc, out);
++ }
++
++out:
++ if (socket_fd >= 0)
++ mpath_disconnect(socket_fd);
++ free(j_str);
++ if (j_token != NULL)
++ json_tokener_free(j_token);
++ if (j_obj != NULL)
++ json_object_put(j_obj);
++
++ if (rc != DMMP_OK) {
++ dmmp_mpath_array_free(*dmmp_mps, *dmmp_mp_count);
++ *dmmp_mps = NULL;
++ *dmmp_mp_count = 0;
++ }
++
++ return rc;
++}
+Index: multipath-tools-130222/libdmmp/libdmmp.pc.in
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp.pc.in
+@@ -0,0 +1,9 @@
++includedir=__INCLUDEDIR__
++libdir=__LIBDIR__
++
++Name: libdmmp
++Version: __VERSION__
++Description: Device mapper multipath management library
++Requires:
++Libs: -L${libdir} -ldmmp
++Cflags: -I${includedir}
+Index: multipath-tools-130222/libdmmp/libdmmp/libdmmp.h
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp/libdmmp.h
+@@ -0,0 +1,653 @@
++/*
++ * Copyright (C) 2015 - 2017 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ * Todd Gill <tgill@redhat.com>
++ */
++
++
++#ifndef _LIB_DMMP_H_
++#define _LIB_DMMP_H_
++
++#include <stdint.h>
++#include <stdarg.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define DMMP_DLL_EXPORT __attribute__ ((visibility ("default")))
++#define DMMP_DLL_LOCAL __attribute__ ((visibility ("hidden")))
++
++#define DMMP_OK 0
++#define DMMP_ERR_BUG 1
++#define DMMP_ERR_NO_MEMORY 2
++#define DMMP_ERR_IPC_TIMEOUT 3
++#define DMMP_ERR_IPC_ERROR 4
++#define DMMP_ERR_NO_DAEMON 5
++#define DMMP_ERR_INCOMPATIBLE 6
++
++/*
++ * Use the syslog severity level as log priority
++ */
++#define DMMP_LOG_PRIORITY_ERROR 3
++#define DMMP_LOG_PRIORITY_WARNING 4
++#define DMMP_LOG_PRIORITY_INFO 6
++#define DMMP_LOG_PRIORITY_DEBUG 7
++
++#define DMMP_LOG_PRIORITY_DEFAULT DMMP_LOG_PRIORITY_WARNING
++
++/**
++ * dmmp_log_priority_str() - Convert log priority to string.
++ *
++ * Convert log priority to string (const char *).
++ *
++ * @priority:
++ * int. Log priority.
++ *
++ * Return:
++ * const char *. Valid string are:
++ *
++ * * "ERROR" for DMMP_LOG_PRIORITY_ERROR
++ *
++ * * "WARN " for DMMP_LOG_PRIORITY_WARNING
++ *
++ * * "INFO " for DMMP_LOG_PRIORITY_INFO
++ *
++ * * "DEBUG" for DMMP_LOG_PRIORITY_DEBUG
++ *
++ * * "Invalid argument" for invalid log priority.
++ */
++DMMP_DLL_EXPORT const char *dmmp_log_priority_str(int priority);
++
++DMMP_DLL_EXPORT struct dmmp_context;
++
++DMMP_DLL_EXPORT struct dmmp_mpath;
++
++DMMP_DLL_EXPORT struct dmmp_path_group;
++
++#define DMMP_PATH_GROUP_STATUS_UNKNOWN 0
++#define DMMP_PATH_GROUP_STATUS_ENABLED 1
++#define DMMP_PATH_GROUP_STATUS_DISABLED 2
++#define DMMP_PATH_GROUP_STATUS_ACTIVE 3
++
++DMMP_DLL_EXPORT struct dmmp_path;
++
++#define DMMP_PATH_STATUS_UNKNOWN 0
++//#define DMMP_PATH_STATUS_UNCHECKED 1
++// ^ print.h does not expose this.
++#define DMMP_PATH_STATUS_DOWN 2
++#define DMMP_PATH_STATUS_UP 3
++#define DMMP_PATH_STATUS_SHAKY 4
++#define DMMP_PATH_STATUS_GHOST 5
++#define DMMP_PATH_STATUS_PENDING 6
++#define DMMP_PATH_STATUS_TIMEOUT 7
++//#define DMMP_PATH_STATUS_REMOVED 8
++// ^ print.h does not expose this.
++#define DMMP_PATH_STATUS_DELAYED 9
++
++/**
++ * dmmp_strerror() - Convert error code to string.
++ *
++ * Convert error code (int) to string (const char *):
++ *
++ * * DMMP_OK -- "OK"
++ *
++ * * DMMP_ERR_BUG -- "BUG of libdmmp library"
++ *
++ * * DMMP_ERR_NO_MEMORY -- "Out of memory"
++ *
++ * * DMMP_ERR_IPC_TIMEOUT -- "Timeout when communicate with multipathd,
++ * try to set bigger timeout value via dmmp_context_timeout_set ()"
++ *
++ * * DMMP_ERR_IPC_ERROR -- "Error when communicate with multipathd daemon"
++ *
++ * * DMMP_ERR_NO_DAEMON -- "The multipathd daemon not started"
++ *
++ * * DMMP_ERR_INCOMPATIBLE -- "The multipathd daemon version is not
++ * compatible with current library"
++ *
++ * * Other invalid error number -- "Invalid argument"
++ *
++ * @rc:
++ * int. Return code by libdmmp functions. When provided error code is not a
++ * valid error code, return "Invalid argument".
++ *
++ * Return:
++ * const char *. The meaning of provided error code.
++ *
++ */
++DMMP_DLL_EXPORT const char *dmmp_strerror(int rc);
++
++/**
++ * dmmp_context_new() - Create struct dmmp_context.
++ *
++ * The default logging level (DMMP_LOG_PRIORITY_DEFAULT) is
++ * DMMP_LOG_PRIORITY_WARNING which means only warning and error message will be
++ * forward to log handler function. The default log handler function will print
++ * log message to STDERR, to change so, please use dmmp_context_log_func_set()
++ * to set your own log handler, check manpage libdmmp.h(3) for detail.
++ *
++ * Return:
++ * Pointer of 'struct dmmp_context'. Should be freed by
++ * dmmp_context_free().
++ */
++DMMP_DLL_EXPORT struct dmmp_context *dmmp_context_new(void);
++
++/**
++ * dmmp_context_free() - Release the memory of struct dmmp_context.
++ *
++ * Release the memory of struct dmmp_context, but the userdata memory defined
++ * via dmmp_context_userdata_set() will not be touched.
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_context_free(struct dmmp_context *ctx);
++
++/**
++ * dmmp_context_timeout_set() - Set IPC timeout.
++ *
++ * By default, the IPC to multipathd daemon will timeout after 60 seconds.
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * @tmo:
++ * Timeout in milliseconds(1 seconds equal 1000 milliseconds).
++ *
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_context_timeout_set(struct dmmp_context *ctx,
++ unsigned int tmo);
++
++/**
++ * dmmp_context_timeout_get() - Get IPC timeout.
++ *
++ * Retrieve timeout value of IPC connection to multipathd daemon.
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * unsigned int. Timeout in milliseconds.
++ */
++DMMP_DLL_EXPORT unsigned int dmmp_context_timeout_get(struct dmmp_context *ctx);
++
++/**
++ * dmmp_context_log_priority_set() - Set log priority.
++ *
++ *
++ * When library generates log message, only equal or more important(less value)
++ * message will be forwarded to log handler function. Valid log priority values
++ * are:
++ *
++ * * DMMP_LOG_PRIORITY_ERROR -- 3
++ *
++ * * DMMP_LOG_PRIORITY_WARNING -- 4
++ *
++ * * DMMP_LOG_PRIORITY_INFO -- 5
++ *
++ * * DMMP_LOG_PRIORITY_DEBUG -- 7
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * @priority:
++ * int, log priority.
++ *
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_context_log_priority_set(struct dmmp_context *ctx,
++ int priority);
++
++/**
++ * dmmp_context_log_priority_get() - Get log priority.
++ *
++ * Retrieve current log priority. Valid log priority values are:
++ *
++ * * DMMP_LOG_PRIORITY_ERROR -- 3
++ *
++ * * DMMP_LOG_PRIORITY_WARNING -- 4
++ *
++ * * DMMP_LOG_PRIORITY_INFO -- 5
++ *
++ * * DMMP_LOG_PRIORITY_DEBUG -- 7
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * int, log priority.
++ */
++DMMP_DLL_EXPORT int dmmp_context_log_priority_get(struct dmmp_context *ctx);
++
++/**
++ * dmmp_context_log_func_set() - Set log handler function.
++ *
++ * Set custom log handler. The log handler will be invoked when log message
++ * is equal or more important(less value) than log priority setting.
++ * Please check manpage libdmmp.h(3) for detail usage.
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @log_func:
++ * Pointer of log handler function.
++ *
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_context_log_func_set
++ (struct dmmp_context *ctx,
++ void (*log_func)
++ (struct dmmp_context *ctx, int priority,
++ const char *file, int line, const char *func_name,
++ const char *format, va_list args));
++
++/**
++ * dmmp_context_userdata_set() - Set user data pointer.
++ *
++ * Store user data pointer into 'struct dmmp_context'.
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @userdata:
++ * Pointer of user defined data.
++ *
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_context_userdata_set(struct dmmp_context *ctx,
++ void *userdata);
++
++/**
++ * dmmp_context_userdata_get() - Get user data pointer.
++ *
++ * Retrieve user data pointer from 'struct dmmp_context'.
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * void *. Pointer of user defined data.
++ */
++DMMP_DLL_EXPORT void *dmmp_context_userdata_get(struct dmmp_context *ctx);
++
++/**
++ * dmmp_mpath_array_get() - Query all existing multipath devices.
++ *
++ * Query all existing multipath devices and store them into a pointer array.
++ * The memory of 'dmmp_mps' should be freed via dmmp_mpath_array_free().
++ *
++ * @ctx:
++ * Pointer of 'struct dmmp_context'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @dmmp_mps:
++ * Output pointer array of 'struct dmmp_mpath'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @dmmp_mp_count:
++ * Output pointer of uint32_t. Hold the size of 'dmmp_mps' pointer array.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * int. Valid error codes are:
++ *
++ * * DMMP_OK
++ *
++ * * DMMP_ERR_BUG
++ *
++ * * DMMP_ERR_NO_MEMORY
++ *
++ * * DMMP_ERR_NO_DAEMON
++ *
++ * * DMMP_ERR_INCONSISTENT_DATA
++ *
++ * Error number could be converted to string by dmmp_strerror().
++ */
++DMMP_DLL_EXPORT int dmmp_mpath_array_get(struct dmmp_context *ctx,
++ struct dmmp_mpath ***dmmp_mps,
++ uint32_t *dmmp_mp_count);
++
++/**
++ * dmmp_mpath_array_free() - Free 'struct dmmp_mpath' pointer array.
++ *
++ * Free the 'dmmp_mps' pointer array generated by dmmp_mpath_array_get().
++ * If provided 'dmmp_mps' pointer is NULL or dmmp_mp_count == 0, do nothing.
++ *
++ * @dmmp_mps:
++ * Pointer of 'struct dmmp_mpath' array.
++ * @dmmp_mp_count:
++ * uint32_t, the size of 'dmmp_mps' pointer array.
++ *
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_mpath_array_free(struct dmmp_mpath **dmmp_mps,
++ uint32_t dmmp_mp_count);
++
++/**
++ * dmmp_mpath_wwid_get() - Retrieve WWID of certain mpath.
++ *
++ * @dmmp_mp:
++ * Pointer of 'struct dmmp_mpath'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * const char *. No need to free this memory, the resources will get
++ * freed when dmmp_mpath_array_free().
++ */
++DMMP_DLL_EXPORT const char *dmmp_mpath_wwid_get(struct dmmp_mpath *dmmp_mp);
++
++/**
++ * dmmp_mpath_name_get() - Retrieve name(alias) of certain mpath.
++ *
++ * Retrieve the name (also known as alias) of certain mpath.
++ * When the config 'user_friendly_names' been set 'no', the name will be
++ * identical to WWID retrieved by dmmp_mpath_wwid_get().
++ *
++ * @dmmp_mp:
++ * Pointer of 'struct dmmp_mpath'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * const char *. No need to free this memory, the resources will get
++ * freed when dmmp_mpath_array_free().
++ */
++DMMP_DLL_EXPORT const char *dmmp_mpath_name_get(struct dmmp_mpath *dmmp_mp);
++
++/**
++ * dmmp_mpath_kdev_name_get() - Retrieve kernel DEVNAME of certain mpath.
++ *
++ * Retrieve DEVNAME name used by kernel uevent of specified mpath.
++ * Example: 'dm-1'.
++ *
++ * @dmmp_mp:
++ * Pointer of 'struct dmmp_mpath'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * const char *. No need to free this memory, the resources will get
++ * freed when dmmp_mpath_array_free().
++ */
++DMMP_DLL_EXPORT const char *dmmp_mpath_kdev_name_get
++ (struct dmmp_mpath *dmmp_mp);
++
++/**
++ * dmmp_path_group_array_get() - Retrieve path groups pointer array.
++ *
++ * Retrieve the path groups of certain mpath.
++ *
++ * The memory of output pointer array is hold by 'struct dmmp_mpath', no
++ * need to free this memory, the resources will got freed when
++ * dmmp_mpath_array_free().
++ *
++ * @dmmp_mp:
++ * Pointer of 'struct dmmp_mpath'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @dmmp_pgs:
++ * Output pointer of 'struct dmmp_path_group' pointer array.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @dmmp_pg_count:
++ * Output pointer of uint32_t. Hold the size of 'dmmp_pgs' pointer array.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_path_group_array_get
++ (struct dmmp_mpath *dmmp_mp, struct dmmp_path_group ***dmmp_pgs,
++ uint32_t *dmmp_pg_count);
++
++/**
++ * dmmp_path_group_id_get() - Retrieve path group ID.
++ *
++ * Retrieve the path group ID which could be used to switch active path group
++ * via command:
++ *
++ * multipathd -k'switch multipath mpathb group $id'
++ *
++ * @dmmp_pg:
++ * Pointer of 'struct dmmp_path_group'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * uint32_t.
++ */
++DMMP_DLL_EXPORT uint32_t dmmp_path_group_id_get
++ (struct dmmp_path_group *dmmp_pg);
++
++/**
++ * dmmp_path_group_priority_get() - Retrieve path group priority.
++ *
++ * The enabled path group with highest priority will be next active path group
++ * if active path group down.
++ *
++ * @dmmp_pg:
++ * Pointer of 'struct dmmp_path_group'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * uint32_t.
++ */
++DMMP_DLL_EXPORT uint32_t dmmp_path_group_priority_get
++ (struct dmmp_path_group *dmmp_pg);
++
++/**
++ * dmmp_path_group_status_get() - Retrieve path group status.
++ *
++ * The valid path group statuses are:
++ *
++ * * DMMP_PATH_GROUP_STATUS_UNKNOWN
++ *
++ * * DMMP_PATH_GROUP_STATUS_ENABLED -- standby to be active
++ *
++ * * DMMP_PATH_GROUP_STATUS_DISABLED -- disabled due to all path down
++ *
++ * * DMMP_PATH_GROUP_STATUS_ACTIVE -- selected to handle I/O
++ *
++ * @dmmp_pg:
++ * Pointer of 'struct dmmp_path_group'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * uint32_t.
++ */
++DMMP_DLL_EXPORT uint32_t dmmp_path_group_status_get
++ (struct dmmp_path_group *dmmp_pg);
++
++/**
++ * dmmp_path_group_status_str() - Convert path group status to string.
++ *
++ * Convert path group status uint32_t to string (const char *).
++ *
++ * @pg_status:
++ * uint32_t. Path group status.
++ * When provided value is not a valid path group status, return "Invalid
++ * argument".
++ *
++ * Return:
++ * const char *. Valid string are:
++ *
++ * * "Invalid argument"
++ *
++ * * "undef"
++ *
++ * * "enabled"
++ *
++ * * "disabled"
++ *
++ * * "active"
++ */
++DMMP_DLL_EXPORT const char *dmmp_path_group_status_str(uint32_t pg_status);
++
++/**
++ * dmmp_path_group_selector_get() - Retrieve path group selector.
++ *
++ * Path group selector determine which path in active path group will be
++ * use to next I/O.
++ *
++ * @dmmp_pg:
++ * Pointer of 'struct dmmp_path_group'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * const char *.
++ */
++DMMP_DLL_EXPORT const char *dmmp_path_group_selector_get
++ (struct dmmp_path_group *dmmp_pg);
++
++/**
++ * dmmp_path_array_get() - Retrieve path pointer array.
++ *
++ * The memory of output pointer array is hold by 'struct dmmp_mpath', no
++ * need to free this memory, the resources will got freed when
++ * dmmp_mpath_array_free().
++ *
++ * @dmmp_pg:
++ * Pointer of 'struct dmmp_path_group'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @dmmp_ps:
++ * Output pointer of 'struct dmmp_path' pointer array.
++ * If this pointer is NULL, your program will be terminated by assert.
++ * @dmmp_p_count:
++ * Output pointer of uint32_t. Hold the size of 'dmmp_ps' pointer array.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * void
++ */
++DMMP_DLL_EXPORT void dmmp_path_array_get(struct dmmp_path_group *dmmp_pg,
++ struct dmmp_path ***dmmp_ps,
++ uint32_t *dmmp_p_count);
++
++/**
++ * dmmp_path_blk_name_get() - Retrieve block name.
++ *
++ * Retrieve block name of certain path. The example of block names are 'sda',
++ * 'nvme0n1'.
++ *
++ * @dmmp_p:
++ * Pointer of 'struct dmmp_path'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * const char *. No need to free this memory, the resources will get
++ * freed when dmmp_mpath_array_free().
++ */
++DMMP_DLL_EXPORT const char *dmmp_path_blk_name_get(struct dmmp_path *dmmp_p);
++
++/**
++ * dmmp_path_status_get() - Retrieve the path status.
++ *
++ * The valid path statuses are:
++ *
++ * * DMMP_PATH_STATUS_UNKNOWN
++ *
++ * * DMMP_PATH_STATUS_DOWN
++ *
++ * Path is down and you shouldn't try to send commands to it.
++ *
++ * * DMMP_PATH_STATUS_UP
++ *
++ * Path is up and I/O can be sent to it.
++ *
++ * * DMMP_PATH_STATUS_SHAKY
++ *
++ * Only emc_clariion checker when path not available for "normal"
++ * operations.
++ *
++ * * DMMP_PATH_STATUS_GHOST
++ *
++ * Only hp_sw and rdac checkers. Indicates a "passive/standby"
++ * path on active/passive HP arrays. These paths will return valid
++ * answers to certain SCSI commands (tur, read_capacity, inquiry,
++ * start_stop), but will fail I/O commands. The path needs an
++ * initialization command to be sent to it in order for I/Os to
++ * succeed.
++ *
++ * * DMMP_PATH_STATUS_PENDING
++ *
++ * Available for all async checkers when a check IO is in flight.
++ *
++ * * DMMP_PATH_STATUS_TIMEOUT
++ *
++ * Only tur checker when command timed out.
++ *
++ * * DMMP_PATH_STATUS_DELAYED
++ *
++ * If a path fails after being up for less than delay_watch_checks checks,
++ * when it comes back up again, it will not be marked as up until it has
++ * been up for delay_wait_checks checks. During this time, it is marked as
++ * "delayed".
++ *
++ * @dmmp_p:
++ * Pointer of 'struct dmmp_path'.
++ * If this pointer is NULL, your program will be terminated by assert.
++ *
++ * Return:
++ * uint32_t.
++ */
++DMMP_DLL_EXPORT uint32_t dmmp_path_status_get(struct dmmp_path *dmmp_p);
++
++/**
++ * dmmp_path_status_str() - Convert path status to string.
++ *
++ * Convert path status uint32_t to string (const char *):
++ *
++ * * DMMP_PATH_STATUS_UNKNOWN -- "undef"
++ *
++ * * DMMP_PATH_STATUS_DOWN -- "faulty"
++ *
++ * * DMMP_PATH_STATUS_UP -- "ready"
++ *
++ * * DMMP_PATH_STATUS_SHAKY -- "shaky"
++ *
++ * * DMMP_PATH_STATUS_GHOST -- "ghost"
++ *
++ * * DMMP_PATH_STATUS_PENDING -- "pending"
++ *
++ * * DMMP_PATH_STATUS_TIMEOUT -- "timeout"
++ *
++ * * DMMP_PATH_STATUS_REMOVED -- "removed"
++ *
++ * * DMMP_PATH_STATUS_DELAYED -- "delayed"
++ *
++ * @path_status:
++ * uint32_t. Path status.
++ * When provided value is not a valid path status, return
++ * "Invalid argument".
++ *
++ * Return:
++ * const char *. The meaning of status value.
++ */
++DMMP_DLL_EXPORT const char *dmmp_path_status_str(uint32_t path_status);
++
++#ifdef __cplusplus
++} /* End of extern "C" */
++#endif
++
++#endif /* End of _LIB_DMMP_H_ */
+Index: multipath-tools-130222/libdmmp/libdmmp_misc.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp_misc.c
+@@ -0,0 +1,87 @@
++/*
++ * Copyright (C) 2015 - 2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ * Todd Gill <tgill@redhat.com>
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdarg.h>
++#include <errno.h>
++#include <limits.h>
++#include <assert.h>
++#include <json.h>
++
++#include "libdmmp/libdmmp.h"
++#include "libdmmp_private.h"
++
++#define _DMMP_LOG_STRERR_ALIGN_WIDTH 80
++/* ^ Only used in _dmmp_log_stderr() for pretty log output.
++ * When provided log message is less than 80 bytes, fill it with space, then
++ * print code file name, function name, line after the 80th bytes.
++ */
++
++static const struct _num_str_conv _DMMP_RC_MSG_CONV[] = {
++ {DMMP_OK, "OK"},
++ {DMMP_ERR_NO_MEMORY, "Out of memory"},
++ {DMMP_ERR_BUG, "BUG of libdmmp library"},
++ {DMMP_ERR_IPC_TIMEOUT, "Timeout when communicate with multipathd, "
++ "try to increase it via "
++ "dmmp_context_timeout_set()"},
++ {DMMP_ERR_IPC_ERROR, "Error when communicate with multipathd daemon"},
++ {DMMP_ERR_NO_DAEMON, "The multipathd daemon not started"},
++ {DMMP_ERR_INCOMPATIBLE, "Incompatible multipathd daemon version"},
++};
++
++_dmmp_str_func_gen(dmmp_strerror, int, rc, _DMMP_RC_MSG_CONV);
++
++static const struct _num_str_conv _DMMP_PRI_CONV[] = {
++ {DMMP_LOG_PRIORITY_DEBUG, "DEBUG"},
++ {DMMP_LOG_PRIORITY_INFO, "INFO"},
++ {DMMP_LOG_PRIORITY_WARNING, "WARNING"},
++ {DMMP_LOG_PRIORITY_ERROR, "ERROR"},
++};
++_dmmp_str_func_gen(dmmp_log_priority_str, int, priority, _DMMP_PRI_CONV);
++
++void _dmmp_log_stderr(struct dmmp_context *ctx, int priority,
++ const char *file, int line, const char *func_name,
++ const char *format, va_list args)
++{
++ int printed_bytes = 0;
++ void *userdata = NULL;
++
++ printed_bytes += fprintf(stderr, "libdmmp %s: ",
++ dmmp_log_priority_str(priority));
++ printed_bytes += vfprintf(stderr, format, args);
++
++ userdata = dmmp_context_userdata_get(ctx);
++ if (userdata != NULL)
++ fprintf(stderr, "(userdata address: %p)",
++ userdata);
++ /* ^ Just demonstrate how userdata could be used and
++ * bypass clang static analyzer about unused ctx argument warning
++ */
++
++ if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) {
++ fprintf(stderr, "%*s # %s:%s():%d\n",
++ _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file,
++ func_name, line);
++ } else {
++ fprintf(stderr, " # %s:%s():%d\n", file, func_name, line);
++ }
++}
+Index: multipath-tools-130222/libdmmp/libdmmp_mp.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp_mp.c
+@@ -0,0 +1,159 @@
++/*
++ * Copyright (C) 2015 - 2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ * Todd Gill <tgill@redhat.com>
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <inttypes.h>
++#include <string.h>
++#include <errno.h>
++#include <assert.h>
++#include <json.h>
++
++#include "libdmmp/libdmmp.h"
++#include "libdmmp_private.h"
++
++struct dmmp_mpath {
++ char *wwid;
++ char *alias;
++ uint32_t dmmp_pg_count;
++ struct dmmp_path_group **dmmp_pgs;
++ char *kdev_name;
++};
++
++_dmmp_getter_func_gen(dmmp_mpath_name_get, struct dmmp_mpath, dmmp_mp,
++ alias, const char *);
++_dmmp_getter_func_gen(dmmp_mpath_wwid_get, struct dmmp_mpath, dmmp_mp,
++ wwid, const char *);
++_dmmp_getter_func_gen(dmmp_mpath_kdev_name_get, struct dmmp_mpath, dmmp_mp,
++ kdev_name, const char *);
++
++struct dmmp_mpath *_dmmp_mpath_new(void)
++{
++ struct dmmp_mpath *dmmp_mp = NULL;
++
++ dmmp_mp = (struct dmmp_mpath *) malloc(sizeof(struct dmmp_mpath));
++
++ if (dmmp_mp != NULL) {
++ dmmp_mp->wwid = NULL;
++ dmmp_mp->alias = NULL;
++ dmmp_mp->dmmp_pg_count = 0;
++ dmmp_mp->dmmp_pgs = NULL;
++ }
++ return dmmp_mp;
++}
++
++int _dmmp_mpath_update(struct dmmp_context *ctx, struct dmmp_mpath *dmmp_mp,
++ json_object *j_obj_map)
++{
++ int rc = DMMP_OK;
++ const char *wwid = NULL;
++ const char *alias = NULL;
++ struct array_list *ar_pgs = NULL;
++ int ar_pgs_len = -1;
++ uint32_t i = 0;
++ struct dmmp_path_group *dmmp_pg = NULL;
++ const char *kdev_name = NULL;
++
++ assert(ctx != NULL);
++ assert(dmmp_mp != NULL);
++ assert(j_obj_map != NULL);
++
++ _json_obj_get_value(ctx, j_obj_map, wwid, "uuid", json_type_string,
++ json_object_get_string, rc, out);
++ _json_obj_get_value(ctx, j_obj_map, alias, "name", json_type_string,
++ json_object_get_string, rc, out);
++ _json_obj_get_value(ctx, j_obj_map, kdev_name, "sysfs",
++ json_type_string, json_object_get_string, rc, out);
++
++ _dmmp_null_or_empty_str_check(ctx, wwid, rc, out);
++ _dmmp_null_or_empty_str_check(ctx, alias, rc, out);
++
++ dmmp_mp->wwid = strdup(wwid);
++ _dmmp_alloc_null_check(ctx, dmmp_mp->wwid, rc, out);
++ dmmp_mp->alias = strdup(alias);
++ _dmmp_alloc_null_check(ctx, dmmp_mp->alias, rc, out);
++ dmmp_mp->kdev_name = strdup(kdev_name);
++ _dmmp_alloc_null_check(ctx, dmmp_mp->kdev_name, rc, out);
++
++ _json_obj_get_value(ctx, j_obj_map, ar_pgs, "path_groups",
++ json_type_array, json_object_get_array, rc, out);
++ ar_pgs_len = array_list_length(ar_pgs);
++ if (ar_pgs_len < 0) {
++ rc = DMMP_ERR_BUG;
++ _error(ctx, "BUG: Got negative length for ar_pgs");
++ goto out;
++ }
++ else if (ar_pgs_len == 0)
++ goto out;
++ else
++ dmmp_mp->dmmp_pg_count = ar_pgs_len & UINT32_MAX;
++
++ dmmp_mp->dmmp_pgs = (struct dmmp_path_group **)
++ malloc(sizeof(struct dmmp_path_group *) *
++ dmmp_mp->dmmp_pg_count);
++ _dmmp_alloc_null_check(ctx, dmmp_mp->dmmp_pgs, rc, out);
++ for (; i < dmmp_mp->dmmp_pg_count; ++i)
++ dmmp_mp->dmmp_pgs[i] = NULL;
++
++ for (i = 0; i < dmmp_mp->dmmp_pg_count; ++i) {
++ dmmp_pg = _dmmp_path_group_new();
++ _dmmp_alloc_null_check(ctx, dmmp_pg, rc, out);
++ dmmp_mp->dmmp_pgs[i] = dmmp_pg;
++ _good(_dmmp_path_group_update(ctx, dmmp_pg,
++ array_list_get_idx(ar_pgs, i)),
++ rc, out);
++ }
++
++ _debug(ctx, "Got mpath wwid: '%s', alias: '%s'", dmmp_mp->wwid,
++ dmmp_mp->alias);
++
++out:
++ if (rc != DMMP_OK)
++ _dmmp_mpath_free(dmmp_mp);
++ return rc;
++}
++
++void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp)
++{
++ if (dmmp_mp == NULL)
++ return ;
++
++ free((char *) dmmp_mp->alias);
++ free((char *) dmmp_mp->wwid);
++ free((char *) dmmp_mp->kdev_name);
++
++ if (dmmp_mp->dmmp_pgs != NULL)
++ _dmmp_path_group_array_free(dmmp_mp->dmmp_pgs,
++ dmmp_mp->dmmp_pg_count);
++
++ free(dmmp_mp);
++}
++
++void dmmp_path_group_array_get(struct dmmp_mpath *dmmp_mp,
++ struct dmmp_path_group ***dmmp_pgs,
++ uint32_t *dmmp_pg_count)
++{
++ assert(dmmp_mp != NULL);
++ assert(dmmp_pgs != NULL);
++ assert(dmmp_pg_count != NULL);
++
++ *dmmp_pgs = dmmp_mp->dmmp_pgs;
++ *dmmp_pg_count = dmmp_mp->dmmp_pg_count;
++}
+Index: multipath-tools-130222/libdmmp/libdmmp_path.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp_path.c
+@@ -0,0 +1,115 @@
++/*
++ * Copyright (C) 2015 - 2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ * Todd Gill <tgill@redhat.com>
++ */
++
++#include <stdlib.h>
++#include <inttypes.h>
++#include <string.h>
++#include <assert.h>
++#include <json.h>
++
++#include "libdmmp/libdmmp.h"
++#include "libdmmp_private.h"
++
++#define _DMMP_SHOW_PS_INDEX_BLK_NAME 0
++#define _DMMP_SHOW_PS_INDEX_SATAUS 1
++#define _DMMP_SHOW_PS_INDEX_WWID 2
++#define _DMMP_SHOW_PS_INDEX_PGID 3
++
++struct dmmp_path {
++ char *blk_name;
++ uint32_t status;
++};
++
++static const struct _num_str_conv _DMMP_PATH_STATUS_CONV[] = {
++ {DMMP_PATH_STATUS_UNKNOWN, "undef"},
++ {DMMP_PATH_STATUS_UP, "ready"},
++ {DMMP_PATH_STATUS_DOWN, "faulty"},
++ {DMMP_PATH_STATUS_SHAKY, "shaky"},
++ {DMMP_PATH_STATUS_GHOST, "ghost"},
++ {DMMP_PATH_STATUS_PENDING, "i/o pending"},
++ {DMMP_PATH_STATUS_TIMEOUT, "i/o timeout"},
++ {DMMP_PATH_STATUS_DELAYED, "delayed"},
++};
++
++_dmmp_str_func_gen(dmmp_path_status_str, uint32_t, path_status,
++ _DMMP_PATH_STATUS_CONV);
++_dmmp_str_conv_func_gen(_dmmp_path_status_str_conv, ctx, path_status_str,
++ uint32_t, DMMP_PATH_STATUS_UNKNOWN,
++ _DMMP_PATH_STATUS_CONV);
++
++_dmmp_getter_func_gen(dmmp_path_blk_name_get, struct dmmp_path, dmmp_p,
++ blk_name, const char *);
++_dmmp_getter_func_gen(dmmp_path_status_get, struct dmmp_path, dmmp_p,
++ status, uint32_t);
++
++struct dmmp_path *_dmmp_path_new(void)
++{
++ struct dmmp_path *dmmp_p = NULL;
++
++ dmmp_p = (struct dmmp_path *) malloc(sizeof(struct dmmp_path));
++
++ if (dmmp_p != NULL) {
++ dmmp_p->blk_name = NULL;
++ dmmp_p->status = DMMP_PATH_STATUS_UNKNOWN;
++ }
++ return dmmp_p;
++}
++
++int _dmmp_path_update(struct dmmp_context *ctx, struct dmmp_path *dmmp_p,
++ json_object *j_obj_p)
++{
++ int rc = DMMP_OK;
++ const char *blk_name = NULL;
++ const char *status_str = NULL;
++
++ assert(ctx != NULL);
++ assert(dmmp_p != NULL);
++ assert(j_obj_p != NULL);
++
++ _json_obj_get_value(ctx, j_obj_p, blk_name, "dev",
++ json_type_string, json_object_get_string, rc, out);
++ _json_obj_get_value(ctx, j_obj_p, status_str, "chk_st",
++ json_type_string, json_object_get_string, rc, out);
++
++ _dmmp_null_or_empty_str_check(ctx, blk_name, rc, out);
++ _dmmp_null_or_empty_str_check(ctx, status_str, rc, out);
++
++ dmmp_p->blk_name = strdup(blk_name);
++ _dmmp_alloc_null_check(ctx, dmmp_p->blk_name, rc, out);
++
++ dmmp_p->status = _dmmp_path_status_str_conv(ctx, status_str);
++
++ _debug(ctx, "Got path blk_name: '%s'", dmmp_p->blk_name);
++ _debug(ctx, "Got path status: %s(%" PRIu32 ")",
++ dmmp_path_status_str(dmmp_p->status), dmmp_p->status);
++
++out:
++ if (rc != DMMP_OK)
++ _dmmp_path_free(dmmp_p);
++ return rc;
++}
++
++void _dmmp_path_free(struct dmmp_path *dmmp_p)
++{
++ if (dmmp_p == NULL)
++ return;
++ free(dmmp_p->blk_name);
++ free(dmmp_p);
++}
+Index: multipath-tools-130222/libdmmp/libdmmp_pg.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp_pg.c
+@@ -0,0 +1,208 @@
++/*
++ * Copyright (C) 2015 - 2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ * Todd Gill <tgill@redhat.com>
++ */
++
++#include <stdlib.h>
++#include <stdint.h>
++#include <inttypes.h>
++#include <string.h>
++#include <assert.h>
++#include <json.h>
++
++#include "libdmmp/libdmmp.h"
++#include "libdmmp_private.h"
++
++#define _DMMP_SHOW_PGS_CMD "show groups raw format %w|%g|%p|%t|%s"
++#define _DMMP_SHOW_PG_INDEX_WWID 0
++#define _DMMP_SHOW_PG_INDEX_PG_ID 1
++#define _DMMP_SHOW_PG_INDEX_PRI 2
++#define _DMMP_SHOW_PG_INDEX_STATUS 3
++#define _DMMP_SHOW_PG_INDEX_SELECTOR 4
++
++struct dmmp_path_group {
++ uint32_t id;
++ /* ^ pgindex of struct path, will be used for path group switch */
++ uint32_t status;
++ uint32_t priority;
++ char *selector;
++ uint32_t dmmp_p_count;
++ struct dmmp_path **dmmp_ps;
++};
++
++static const struct _num_str_conv _DMMP_PATH_GROUP_STATUS_CONV[] = {
++ {DMMP_PATH_GROUP_STATUS_UNKNOWN, "undef"},
++ {DMMP_PATH_GROUP_STATUS_ACTIVE, "active"},
++ {DMMP_PATH_GROUP_STATUS_DISABLED, "disabled"},
++ {DMMP_PATH_GROUP_STATUS_ENABLED, "enabled"},
++};
++
++_dmmp_str_func_gen(dmmp_path_group_status_str, uint32_t, pg_status,
++ _DMMP_PATH_GROUP_STATUS_CONV);
++_dmmp_str_conv_func_gen(_dmmp_path_group_status_str_conv, ctx, pg_status_str,
++ uint32_t, DMMP_PATH_GROUP_STATUS_UNKNOWN,
++ _DMMP_PATH_GROUP_STATUS_CONV);
++
++_dmmp_getter_func_gen(dmmp_path_group_id_get, struct dmmp_path_group, dmmp_pg,
++ id, uint32_t);
++_dmmp_getter_func_gen(dmmp_path_group_status_get, struct dmmp_path_group,
++ dmmp_pg, status, uint32_t);
++_dmmp_getter_func_gen(dmmp_path_group_priority_get, struct dmmp_path_group,
++ dmmp_pg, priority, uint32_t);
++_dmmp_getter_func_gen(dmmp_path_group_selector_get, struct dmmp_path_group,
++ dmmp_pg, selector, const char *);
++_dmmp_array_free_func_gen(_dmmp_path_group_array_free, struct dmmp_path_group,
++ _dmmp_path_group_free);
++
++
++struct dmmp_path_group *_dmmp_path_group_new(void)
++{
++ struct dmmp_path_group *dmmp_pg = NULL;
++
++ dmmp_pg = (struct dmmp_path_group *)
++ malloc(sizeof(struct dmmp_path_group));
++
++ if (dmmp_pg != NULL) {
++ dmmp_pg->id = _DMMP_PATH_GROUP_ID_UNKNOWN;
++ dmmp_pg->status = DMMP_PATH_GROUP_STATUS_UNKNOWN;
++ dmmp_pg->priority = 0;
++ dmmp_pg->selector = NULL;
++ dmmp_pg->dmmp_p_count = 0;
++ dmmp_pg->dmmp_ps = NULL;
++ }
++ return dmmp_pg;
++}
++int _dmmp_path_group_update(struct dmmp_context *ctx,
++ struct dmmp_path_group *dmmp_pg,
++ json_object *j_obj_pg)
++{
++ int rc = DMMP_OK;
++ uint32_t id = 0;
++ int priority_int = -1 ;
++ const char *status_str = NULL;
++ const char *selector = NULL;
++ struct array_list *ar_ps = NULL;
++ int ar_ps_len = -1;
++ uint32_t i = 0;
++ struct dmmp_path *dmmp_p = NULL;
++
++ assert(ctx != NULL);
++ assert(dmmp_pg != NULL);
++ assert(j_obj_pg != NULL);
++
++ _json_obj_get_value(ctx, j_obj_pg, status_str, "dm_st",
++ json_type_string, json_object_get_string, rc, out);
++
++ _json_obj_get_value(ctx, j_obj_pg, selector, "selector",
++ json_type_string, json_object_get_string, rc, out);
++
++ _json_obj_get_value(ctx, j_obj_pg, priority_int, "pri",
++ json_type_int, json_object_get_int, rc, out);
++
++ _json_obj_get_value(ctx, j_obj_pg, id, "group",
++ json_type_int, json_object_get_int, rc, out);
++
++ dmmp_pg->priority = (priority_int <= 0) ? 0 : priority_int & UINT32_MAX;
++
++ _dmmp_null_or_empty_str_check(ctx, status_str, rc, out);
++ _dmmp_null_or_empty_str_check(ctx, selector, rc, out);
++
++ dmmp_pg->selector = strdup(selector);
++ _dmmp_alloc_null_check(ctx, dmmp_pg->selector, rc, out);
++
++ dmmp_pg->id = id;
++
++ if (dmmp_pg->id == _DMMP_PATH_GROUP_ID_UNKNOWN) {
++ rc = DMMP_ERR_BUG;
++ _error(ctx, "BUG: Got unknown(%d) path group ID",
++ _DMMP_PATH_GROUP_ID_UNKNOWN);
++ goto out;
++ }
++
++ dmmp_pg->status = _dmmp_path_group_status_str_conv(ctx, status_str);
++
++ _json_obj_get_value(ctx, j_obj_pg, ar_ps, "paths",
++ json_type_array, json_object_get_array, rc, out);
++
++ ar_ps_len = array_list_length(ar_ps);
++ if (ar_ps_len < 0) {
++ rc = DMMP_ERR_BUG;
++ _error(ctx, "BUG: Got negative length for ar_ps");
++ goto out;
++ }
++ else if (ar_ps_len == 0)
++ goto out;
++ else
++ dmmp_pg->dmmp_p_count = ar_ps_len & UINT32_MAX;
++
++ dmmp_pg->dmmp_ps = (struct dmmp_path **)
++ malloc(sizeof(struct dmmp_path *) * dmmp_pg->dmmp_p_count);
++ _dmmp_alloc_null_check(ctx, dmmp_pg->dmmp_ps, rc, out);
++ for (; i < dmmp_pg->dmmp_p_count; ++i)
++ dmmp_pg->dmmp_ps[i] = NULL;
++
++ for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) {
++ dmmp_p = _dmmp_path_new();
++ _dmmp_alloc_null_check(ctx, dmmp_p, rc, out);
++ dmmp_pg->dmmp_ps[i] = dmmp_p;
++ _good(_dmmp_path_update(ctx, dmmp_p,
++ array_list_get_idx(ar_ps, i)),
++ rc, out);
++ }
++
++ _debug(ctx, "Got path group id: %" PRIu32 "", dmmp_pg->id);
++ _debug(ctx, "Got path group priority: %" PRIu32 "", dmmp_pg->priority);
++ _debug(ctx, "Got path group status: %s(%" PRIu32 ")",
++ dmmp_path_group_status_str(dmmp_pg->status), dmmp_pg->status);
++ _debug(ctx, "Got path group selector: '%s'", dmmp_pg->selector);
++
++out:
++ if (rc != DMMP_OK)
++ _dmmp_path_group_free(dmmp_pg);
++ return rc;
++}
++
++void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg)
++{
++ uint32_t i = 0;
++
++ if (dmmp_pg == NULL)
++ return;
++
++ free((char *) dmmp_pg->selector);
++
++ if (dmmp_pg->dmmp_ps != NULL) {
++ for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) {
++ _dmmp_path_free(dmmp_pg->dmmp_ps[i]);
++ }
++ free(dmmp_pg->dmmp_ps);
++ }
++ free(dmmp_pg);
++}
++
++void dmmp_path_array_get(struct dmmp_path_group *mp_pg,
++ struct dmmp_path ***mp_paths,
++ uint32_t *dmmp_p_count)
++{
++ assert(mp_pg != NULL);
++ assert(mp_paths != NULL);
++ assert(dmmp_p_count != NULL);
++
++ *mp_paths = mp_pg->dmmp_ps;
++ *dmmp_p_count = mp_pg->dmmp_p_count;
++}
+Index: multipath-tools-130222/libdmmp/libdmmp_private.h
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/libdmmp_private.h
+@@ -0,0 +1,208 @@
++/*
++ * Copyright (C) 2015 - 2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ * Todd Gill <tgill@redhat.com>
++ */
++
++#ifndef _LIB_DMMP_PRIVATE_H_
++#define _LIB_DMMP_PRIVATE_H_
++
++/*
++ * Notes:
++ * Internal/Private functions does not check input argument but using
++ * assert() to abort if NULL pointer found in argument.
++ */
++
++#include <stdint.h>
++#include <string.h>
++#include <assert.h>
++#include <json.h>
++
++#include "libdmmp/libdmmp.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define _good(rc, rc_val, out) \
++ do { \
++ rc_val = rc; \
++ if (rc_val != DMMP_OK) \
++ goto out; \
++ } while(0)
++
++#define _DMMP_PATH_GROUP_ID_UNKNOWN 0
++
++DMMP_DLL_LOCAL struct _num_str_conv {
++ const uint32_t value;
++ const char *str;
++};
++
++#define _dmmp_str_func_gen(func_name, var_type, var, conv_array) \
++const char *func_name(var_type var) { \
++ size_t i = 0; \
++ uint32_t tmp_var = var & UINT32_MAX; \
++ /* In the whole libdmmp, we don't have negative value */ \
++ for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \
++ if ((conv_array[i].value) == tmp_var) \
++ return conv_array[i].str; \
++ } \
++ return "Invalid argument"; \
++}
++
++#define _dmmp_str_conv_func_gen(func_name, ctx, var_name, out_type, \
++ unknown_value, conv_array) \
++static out_type func_name(struct dmmp_context *ctx, const char *var_name) { \
++ size_t i = 0; \
++ for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \
++ if (strcmp(conv_array[i].str, var_name) == 0) \
++ return conv_array[i].value; \
++ } \
++ _warn(ctx, "Got unknown " #var_name ": '%s'", var_name); \
++ return unknown_value; \
++}
++
++#define _json_obj_get_value(ctx, j_obj, out_value, key, value_type, \
++ value_func, rc, out) \
++do { \
++ json_type j_type = json_type_null; \
++ json_object *j_obj_tmp = NULL; \
++ if (json_object_object_get_ex(j_obj, key, &j_obj_tmp) != TRUE) { \
++ _error(ctx, "Invalid JSON output from multipathd IPC: " \
++ "key '%s' not found", key); \
++ rc = DMMP_ERR_IPC_ERROR; \
++ goto out; \
++ } \
++ if (j_obj_tmp == NULL) { \
++ _error(ctx, "BUG: Got NULL j_obj_tmp from " \
++ "json_object_object_get_ex() while it return TRUE"); \
++ rc = DMMP_ERR_BUG; \
++ goto out; \
++ } \
++ j_type = json_object_get_type(j_obj_tmp); \
++ if (j_type != value_type) { \
++ _error(ctx, "Invalid value type for key'%s' of JSON output " \
++ "from multipathd IPC. Should be %s(%d), " \
++ "but got %s(%d)", key, json_type_to_name(value_type), \
++ value_type, json_type_to_name(j_type), j_type); \
++ rc = DMMP_ERR_IPC_ERROR; \
++ goto out; \
++ } \
++ out_value = value_func(j_obj_tmp); \
++} while(0);
++
++DMMP_DLL_LOCAL int _dmmp_ipc_exec(struct dmmp_context *ctx, const char *cmd,
++ char **output);
++
++DMMP_DLL_LOCAL struct dmmp_mpath *_dmmp_mpath_new(void);
++DMMP_DLL_LOCAL struct dmmp_path_group *_dmmp_path_group_new(void);
++DMMP_DLL_LOCAL struct dmmp_path *_dmmp_path_new(void);
++
++DMMP_DLL_LOCAL int _dmmp_mpath_update(struct dmmp_context *ctx,
++ struct dmmp_mpath *dmmp_mp,
++ json_object *j_obj_map);
++DMMP_DLL_LOCAL int _dmmp_path_group_update(struct dmmp_context *ctx,
++ struct dmmp_path_group *dmmp_pg,
++ json_object *j_obj_pg);
++DMMP_DLL_LOCAL int _dmmp_path_update(struct dmmp_context *ctx,
++ struct dmmp_path *dmmp_p,
++ json_object *j_obj_p);
++
++DMMP_DLL_LOCAL void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp);
++DMMP_DLL_LOCAL void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg);
++DMMP_DLL_LOCAL void _dmmp_path_group_array_free
++ (struct dmmp_path_group **dmmp_pgs, uint32_t dmmp_pg_count);
++DMMP_DLL_LOCAL void _dmmp_path_free(struct dmmp_path *dmmp_p);
++DMMP_DLL_LOCAL void _dmmp_log(struct dmmp_context *ctx, int priority,
++ const char *file, int line,
++ const char *func_name,
++ const char *format, ...);
++DMMP_DLL_LOCAL void _dmmp_log_err_str(struct dmmp_context *ctx, int rc);
++
++DMMP_DLL_LOCAL void _dmmp_log_stderr(struct dmmp_context *ctx, int priority,
++ const char *file, int line,
++ const char *func_name, const char *format,
++ va_list args);
++
++
++#define _dmmp_log_cond(ctx, prio, arg...) \
++ do { \
++ if (dmmp_context_log_priority_get(ctx) >= prio) \
++ _dmmp_log(ctx, prio, __FILE__, __LINE__, __FUNCTION__, \
++ ## arg); \
++ } while (0)
++
++#define _debug(ctx, arg...) \
++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_DEBUG, ## arg)
++#define _info(ctx, arg...) \
++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_INFO, ## arg)
++#define _warn(ctx, arg...) \
++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_WARNING, ## arg)
++#define _error(ctx, arg...) \
++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_ERROR, ## arg)
++
++/*
++ * Check pointer returned by malloc() or strdup(), if NULL, set
++ * rc as DMMP_ERR_NO_MEMORY, report error and goto goto_out.
++ */
++#define _dmmp_alloc_null_check(ctx, ptr, rc, goto_out) \
++ do { \
++ if (ptr == NULL) { \
++ rc = DMMP_ERR_NO_MEMORY; \
++ _error(ctx, dmmp_strerror(rc)); \
++ goto goto_out; \
++ } \
++ } while(0)
++
++#define _dmmp_null_or_empty_str_check(ctx, var, rc, goto_out) \
++ do { \
++ if (var == NULL) { \
++ rc = DMMP_ERR_BUG; \
++ _error(ctx, "BUG: Got NULL " #var); \
++ goto goto_out; \
++ } \
++ if (strlen(var) == 0) { \
++ rc = DMMP_ERR_BUG; \
++ _error(ctx, "BUG: Got empty " #var); \
++ goto goto_out; \
++ } \
++ } while(0)
++
++#define _dmmp_getter_func_gen(func_name, struct_name, struct_data, \
++ prop_name, prop_type) \
++ prop_type func_name(struct_name *struct_data) \
++ { \
++ assert(struct_data != NULL); \
++ return struct_data->prop_name; \
++ }
++
++#define _dmmp_array_free_func_gen(func_name, struct_name, struct_free_func) \
++ void func_name(struct_name **ptr_array, uint32_t ptr_count) \
++ { \
++ uint32_t i = 0; \
++ if (ptr_array == NULL) \
++ return; \
++ for (; i < ptr_count; ++i) \
++ struct_free_func(ptr_array[i]); \
++ free(ptr_array); \
++ }
++
++#ifdef __cplusplus
++} /* End of extern "C" */
++#endif
++
++#endif /* End of _LIB_DMMP_PRIVATE_H_ */
+Index: multipath-tools-130222/libdmmp/test/Makefile
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/test/Makefile
+@@ -0,0 +1,30 @@
++# Makefile
++#
++# Copyright (C) 2015-2016 Gris Ge <fge@redhat.com>
++#
++include ../../Makefile.inc
++
++_libdmmpdir=../$(libdmmpdir)
++_mpathcmddir=../$(mpathcmddir)
++
++TEST_EXEC = libdmmp_test
++SPD_TEST_EXEC = libdmmp_speed_test
++CFLAGS += -I$(_libdmmpdir)
++LDFLAGS += -L$(_libdmmpdir) -ldmmp
++
++all: $(TEST_EXEC) $(SPD_TEST_EXEC)
++
++check: $(TEST_EXEC) $(SPD_TEST_EXEC)
++ sudo env LD_LIBRARY_PATH=$(_libdmmpdir):$(_mpathcmddir) \
++ valgrind --quiet --leak-check=full \
++ --show-reachable=no --show-possibly-lost=no \
++ --trace-children=yes --error-exitcode=1 \
++ ./$(TEST_EXEC)
++ $(MAKE) speed_test
++
++speed_test: $(SPD_TEST_EXEC)
++ sudo env LD_LIBRARY_PATH=$(_libdmmpdir):$(_mpathcmddir) \
++ time -p ./$(SPD_TEST_EXEC)
++
++clean:
++ rm -f $(TEST_EXEC) $(SPD_TEST_EXEC)
+Index: multipath-tools-130222/libdmmp/test/libdmmp_speed_test.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/test/libdmmp_speed_test.c
+@@ -0,0 +1,49 @@
++/*
++ * Copyright (C) 2015-2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ */
++
++#include <stdint.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <inttypes.h>
++#include <string.h>
++#include <pthread.h>
++#include <unistd.h>
++
++#include <libdmmp/libdmmp.h>
++
++int main(int argc, char *argv[])
++{
++ struct dmmp_context *ctx = NULL;
++ struct dmmp_mpath **dmmp_mps = NULL;
++ uint32_t dmmp_mp_count = 0;
++ int rc = EXIT_SUCCESS;
++
++ ctx = dmmp_context_new();
++ dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_WARNING);
++
++ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) {
++ printf("FAILED\n");
++ rc = EXIT_FAILURE;
++ } else {
++ printf("Got %" PRIu32 " mpath\n", dmmp_mp_count);
++ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count);
++ }
++ dmmp_context_free(ctx);
++ exit(rc);
++}
+Index: multipath-tools-130222/libdmmp/test/libdmmp_test.c
+===================================================================
+--- /dev/null
++++ multipath-tools-130222/libdmmp/test/libdmmp_test.c
+@@ -0,0 +1,147 @@
++/*
++ * Copyright (C) 2015-2016 Red Hat, Inc.
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Gris Ge <fge@redhat.com>
++ */
++
++#include <stdint.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <inttypes.h>
++#include <string.h>
++#include <pthread.h>
++#include <unistd.h>
++
++#include <libdmmp/libdmmp.h>
++
++#define FAIL(rc, out, ...) \
++ do { \
++ rc = EXIT_FAILURE; \
++ fprintf(stderr, "FAIL: "__VA_ARGS__ ); \
++ goto out; \
++ } while(0)
++#define PASS(...) fprintf(stdout, "PASS: "__VA_ARGS__ );
++#define FILE_NAME_SIZE 256
++#define TMO 10000 /* Forcing timeout to 10 seconds */
++
++int test_paths(struct dmmp_path_group *mp_pg)
++{
++ struct dmmp_path **mp_ps = NULL;
++ uint32_t mp_p_count = 0;
++ uint32_t i = 0;
++ const char *blk_name = NULL;
++ int rc = EXIT_SUCCESS;
++
++ dmmp_path_array_get(mp_pg, &mp_ps, &mp_p_count);
++ if (mp_p_count == 0)
++ FAIL(rc, out, "dmmp_path_array_get(): Got no path\n");
++ for (i = 0; i < mp_p_count; ++i) {
++ blk_name = dmmp_path_blk_name_get(mp_ps[i]);
++ if (blk_name == NULL)
++ FAIL(rc, out, "dmmp_path_blk_name_get(): Got NULL\n");
++ PASS("dmmp_path_blk_name_get(): %s\n", blk_name);
++ PASS("dmmp_path_status_get(): %" PRIu32 " -- %s\n",
++ dmmp_path_status_get(mp_ps[i]),
++ dmmp_path_status_str(dmmp_path_status_get(mp_ps[i])));
++ }
++out:
++ return rc;
++}
++
++int test_path_groups(struct dmmp_mpath *dmmp_mp)
++{
++ struct dmmp_path_group **dmmp_pgs = NULL;
++ uint32_t dmmp_pg_count = 0;
++ uint32_t i = 0;
++ int rc = EXIT_SUCCESS;
++
++ dmmp_path_group_array_get(dmmp_mp, &dmmp_pgs, &dmmp_pg_count);
++ if ((dmmp_pg_count == 0) && (dmmp_pgs != NULL))
++ FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is not NULL "
++ "but mp_pg_count is 0\n");
++ if ((dmmp_pg_count != 0) && (dmmp_pgs == NULL))
++ FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is NULL "
++ "but mp_pg_count is not 0\n");
++ if (dmmp_pg_count == 0)
++ FAIL(rc, out, "dmmp_path_group_array_get(): "
++ "Got 0 path group\n");
++
++ PASS("dmmp_path_group_array_get(): Got %" PRIu32 " path groups\n",
++ dmmp_pg_count);
++
++ for (i = 0; i < dmmp_pg_count; ++i) {
++ PASS("dmmp_path_group_id_get(): %" PRIu32 "\n",
++ dmmp_path_group_id_get(dmmp_pgs[i]));
++ PASS("dmmp_path_group_priority_get(): %" PRIu32 "\n",
++ dmmp_path_group_priority_get(dmmp_pgs[i]));
++ PASS("dmmp_path_group_status_get(): %" PRIu32 " -- %s\n",
++ dmmp_path_group_status_get(dmmp_pgs[i]),
++ dmmp_path_group_status_str
++ (dmmp_path_group_status_get(dmmp_pgs[i])));
++ PASS("dmmp_path_group_selector_get(): %s\n",
++ dmmp_path_group_selector_get(dmmp_pgs[i]));
++ rc = test_paths(dmmp_pgs[i]);
++ if (rc != 0)
++ goto out;
++ }
++out:
++ return rc;
++}
++
++int main(int argc, char *argv[])
++{
++ struct dmmp_context *ctx = NULL;
++ struct dmmp_mpath **dmmp_mps = NULL;
++ uint32_t dmmp_mp_count = 0;
++ const char *name = NULL;
++ const char *wwid = NULL;
++ const char *kdev = NULL;
++ uint32_t i = 0;
++ int rc = EXIT_SUCCESS;
++
++ ctx = dmmp_context_new();
++ dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG);
++ dmmp_context_userdata_set(ctx, ctx);
++ dmmp_context_userdata_set(ctx, NULL);
++ dmmp_context_timeout_set(ctx, TMO);
++ if (dmmp_context_timeout_get(ctx) != TMO)
++ FAIL(rc, out, "dmmp_context_timeout_set(): Failed to set "
++ "timeout to %u", TMO);
++
++ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0)
++ FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n");
++ if (dmmp_mp_count == 0)
++ FAIL(rc, out, "dmmp_mpath_array_get(): "
++ "Got no multipath devices\n");
++ PASS("dmmp_mpath_array_get(): Got %" PRIu32 " mpath\n", dmmp_mp_count);
++ for (i = 0; i < dmmp_mp_count; ++i) {
++ name = dmmp_mpath_name_get(dmmp_mps[i]);
++ wwid = dmmp_mpath_wwid_get(dmmp_mps[i]);
++ kdev = dmmp_mpath_kdev_name_get(dmmp_mps[i]);
++ if ((name == NULL) ||(wwid == NULL) || (kdev == NULL))
++ FAIL(rc, out,
++ "dmmp_mpath_array_get(): Got NULL name or wwid");
++ PASS("dmmp_mpath_array_get(): Got mpath(%s): %s %s\n",
++ kdev, name, wwid);
++ rc = test_path_groups(dmmp_mps[i]);
++ if (rc != 0)
++ goto out;
++ }
++ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count);
++out:
++ dmmp_context_free(ctx);
++ exit(rc);
++}
diff --git a/0210-RH-fix-uninstall.patch b/0210-RH-fix-uninstall.patch
new file mode 100644
index 0000000..7083ded
--- /dev/null
+++ b/0210-RH-fix-uninstall.patch
@@ -0,0 +1,25 @@
+---
+ libmpathpersist/Makefile | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+Index: multipath-tools-130222/libmpathpersist/Makefile
+===================================================================
+--- multipath-tools-130222.orig/libmpathpersist/Makefile
++++ multipath-tools-130222/libmpathpersist/Makefile
+@@ -33,12 +33,14 @@ install: $(LIBS)
+ ln -sf $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
+ install -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir)
+ install -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir)
++ $(INSTALL_PROGRAM) -m 644 mpath_persist.h $(DESTDIR)$(includedir)
+
+ uninstall:
+ rm -f $(DESTDIR)$(syslibdir)/$(LIBS)
+ rm -f $(DESTDIR)$(syslibdir)/$(DEVLIB)
+- rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_in.3.gz
+- rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_out.3.gz
++ rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz
++ rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz
++ rm -f $(DESTDIR)$(includedir)/mpath_persist.h
+
+ clean:
+ rm -f core *.a *.o
diff --git a/0211-RH-strlen-fix.patch b/0211-RH-strlen-fix.patch
new file mode 100644
index 0000000..2c12fe8
--- /dev/null
+++ b/0211-RH-strlen-fix.patch
@@ -0,0 +1,35 @@
+---
+ multipathd/main.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -878,7 +878,8 @@ uxsock_trigger (char * str, char ** repl
+ (strncmp(str, "list", strlen("list")) != 0) &&
+ (strncmp(str, "show", strlen("show")) != 0)) {
+ *reply = STRDUP("permission deny: need to be root");
+- *len = strlen(*reply) + 1;
++ if (*reply)
++ *len = strlen(*reply) + 1;
+ r = 1;
+ goto out;
+ }
+@@ -887,12 +888,14 @@ uxsock_trigger (char * str, char ** repl
+
+ if (r > 0) {
+ *reply = STRDUP("fail\n");
+- *len = strlen(*reply) + 1;
++ if (*reply)
++ *len = strlen(*reply) + 1;
+ r = 1;
+ }
+ else if (!r && *len == 0) {
+ *reply = STRDUP("ok\n");
+- *len = strlen(*reply) + 1;
++ if (*reply)
++ *len = strlen(*reply) + 1;
+ r = 0;
+ }
+ /* else if (r < 0) leave *reply alone */
diff --git a/0212-RHBZ-1431562-for-read-only.patch b/0212-RHBZ-1431562-for-read-only.patch
new file mode 100644
index 0000000..b398100
--- /dev/null
+++ b/0212-RHBZ-1431562-for-read-only.patch
@@ -0,0 +1,56 @@
+---
+ libmultipath/devmapper.c | 10 ++++++----
+ libmultipath/structs.h | 1 +
+ multipathd/main.c | 5 +++--
+ 3 files changed, 10 insertions(+), 6 deletions(-)
+
+Index: multipath-tools-130222/libmultipath/devmapper.c
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/devmapper.c
++++ multipath-tools-130222/libmultipath/devmapper.c
+@@ -358,10 +358,12 @@ dm_addmap_create (struct multipath *mpp,
+ extern int
+ dm_addmap_reload (struct multipath *mpp, char *params) {
+ sysfs_set_max_sectors_kb(mpp, 1);
+- if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW, SKIP_KPARTX_OFF))
+- return 1;
+- if (errno != EROFS)
+- return 0;
++ if (!mpp->force_readonly) {
++ if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW, SKIP_KPARTX_OFF))
++ return 1;
++ if (errno != EROFS)
++ return 0;
++ }
+ return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RO, SKIP_KPARTX_OFF);
+ }
+
+Index: multipath-tools-130222/libmultipath/structs.h
+===================================================================
+--- multipath-tools-130222.orig/libmultipath/structs.h
++++ multipath-tools-130222/libmultipath/structs.h
+@@ -259,6 +259,7 @@ struct multipath {
+ int force_udev_reload;
+ int skip_kpartx;
+ int max_sectors_kb;
++ int force_readonly;
+ unsigned int dev_loss;
+ uid_t uid;
+ gid_t gid;
+Index: multipath-tools-130222/multipathd/main.c
+===================================================================
+--- multipath-tools-130222.orig/multipathd/main.c
++++ multipath-tools-130222/multipathd/main.c
+@@ -831,9 +831,10 @@ uev_update_path (struct uevent *uev, str
+ pp->mpp->wait_for_udev = 2;
+ return 0;
+ }
+-
++ if (ro == 1)
++ pp->mpp->force_readonly = 1;
+ retval = reload_map(vecs, pp->mpp, 0);
+-
++ pp->mpp->force_readonly = 0;
+ condlog(2, "%s: map %s reloaded (retval %d)",
+ uev->kernel, pp->mpp->alias, retval);
+ }
diff --git a/device-mapper-multipath.spec b/device-mapper-multipath.spec
index 2453b12..a0374dd 100644
--- a/device-mapper-multipath.spec
+++ b/device-mapper-multipath.spec
@@ -1,7 +1,7 @@
Summary: Tools to manage multipath devices using device-mapper
Name: device-mapper-multipath
Version: 0.4.9
-Release: 85%{?dist}
+Release: 86%{?dist}
License: GPL+
Group: System Environment/Base
URL: http://christophe.varoqui.free.fr/
@@ -180,21 +180,64 @@ Patch0169: 0169-UPBZ-1353357-json-output.patch
Patch0170: 0170-UPBZ-1352925-fix-typo.patch
Patch0171: 0171-UPBZ-1356651-allow-zero-size.patch
Patch0172: 0172-RHBZ-1350931-no-active-add.patch
+Patch0173: 0173-RH-update-man-page.patch
+Patch0174: 0174-RHBZ-1362396-modprobe.patch
+Patch0175: 0175-RHBZ-1357382-ordering.patch
+Patch0176: 0176-RHBZ-1363830-fix-rename.patch
+Patch0177: 0177-libmultipath-correctly-initialize-pp-sg_id.patch
+Patch0178: 0178-libmultipath-add-rbd-discovery.patch
+Patch0179: 0179-multipath-tools-add-checker-callout-to-repair-path.patch
+Patch0180: 0180-multipath-tools-Add-rbd-checker.patch
+Patch0181: 0181-multipath-tools-Add-rbd-to-the-hwtable.patch
+Patch0182: 0182-multipath-tools-check-for-initialized-checker-before.patch
+Patch0183: 0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch
+Patch0184: 0184-rbd-fix-sync-repair-support.patch
+Patch0185: 0185-rbd-check-for-nonshared-clients.patch
+Patch0186: 0186-rbd-check-for-exclusive-lock-enabled.patch
+Patch0187: 0187-rbd-fixup-log-messages.patch
+Patch0188: 0188-RHBZ-1368501-dont-exit.patch
+Patch0189: 0189-RHBZ-1368211-remove-retries.patch
+Patch0190: 0190-RHBZ-1380602-rbd-lock-on-read.patch
+Patch0191: 0191-RHBZ-1169168-disable-changed-paths.patch
+Patch0192: 0192-RHBZ-1362409-infinibox-config.patch
+Patch0194: 0194-RHBZ-1351964-kpartx-recurse.patch
+Patch0195: 0195-RHBZ-1359510-no-daemon-msg.patch
+Patch0196: 0196-RHBZ-1239173-dont-set-flag.patch
+Patch0197: 0197-RHBZ-1394059-max-sectors-kb.patch
+Patch0198: 0198-RHBZ-1372032-detect-path-checker.patch
+Patch0199: 0199-RHBZ-1279355-3pardata-config.patch
+Patch0200: 0200-RHBZ-1402092-orphan-status.patch
+Patch0201: 0201-RHBZ-1403552-silence-warning.patch
+Patch0202: 0202-RHBZ-1362120-skip-prio.patch
+Patch0203: 0203-RHBZ-1363718-add-msgs.patch
+Patch0204: 0204-RHBZ-1406226-nimble-config.patch
+Patch0205: 0205-RHBZ-1416569-reset-stats.patch
+Patch0206: 0206-RHBZ-1239173-pt2-no-paths.patch
+Patch0207: 0207-UP-add-libmpathcmd.patch
+Patch0208: 0208-UPBZ-1430097-multipathd-IPC-changes.patch
+Patch0209: 0209-UPBZ-1430097-multipath-C-API.patch
+Patch0210: 0210-RH-fix-uninstall.patch
+Patch0211: 0211-RH-strlen-fix.patch
+Patch0212: 0212-RHBZ-1431562-for-read-only.patch
# runtime
Requires: %{name}-libs = %{version}-%{release}
Requires: kpartx = %{version}-%{release}
-Requires: device-mapper >= 1.02.96
+Requires: device-mapper >= 7:1.02.96
Requires: initscripts
Requires(post): systemd-units systemd-sysv chkconfig
Requires(preun): systemd-units
Requires(postun): systemd-units
# build/setup
-BuildRequires: libaio-devel, device-mapper-devel >= 1.02.82-2
+BuildRequires: libaio-devel, device-mapper-devel >= 1.02.89
BuildRequires: libselinux-devel, libsepol-devel
BuildRequires: readline-devel, ncurses-devel
BuildRequires: systemd-units, systemd-devel
+BuildRequires: json-c-devel, perl, pkgconfig
+%ifarch x86_64
+BuildRequires: librados2-devel
+%endif
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
@@ -212,9 +255,20 @@ Group: System Environment/Libraries
%description libs
The %{name}-libs provides the path checker
-and prioritizer modules. It also contains the multipath shared library,
+and prioritizer modules. It also contains the libmpathpersist and
+libmpathcmd shared libraries, as well as multipath's internal library,
libmultipath.
+%package devel
+Summary: Development libraries and headers for %{name}
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: %{name}-libs = %{version}-%{release}
+
+%description devel
+This package contains the files need to develop applications that use
+device-mapper-multipath's lbmpathpersist and libmpathcmd libraries.
+
%if 0%{?fedora} < 23
%package sysvinit
Summary: SysV init script for device-mapper-multipath
@@ -232,6 +286,27 @@ Group: System Environment/Base
%description -n kpartx
kpartx manages partition creation and removal for device-mapper devices.
+%package -n libdmmp
+Summary: device-mapper-multipath C API library
+Group: System Environment/Libraries
+Requires: json-c
+Requires: %{name} = %{version}-%{release}
+Requires: %{name}-libs = %{version}-%{release}
+
+%description -n libdmmp
+This package contains the shared library for the device-mapper-multipath
+C API library.
+
+%package -n libdmmp-devel
+Summary: device-mapper-multipath C API library headers
+Group: Development/Libraries
+Requires: pkgconfig
+Requires: libdmmp = %{version}-%{release}
+
+%description -n libdmmp-devel
+This package contains the files needed to develop applications that use
+device-mapper-multipath's libdmmp C API library
+
%prep
%setup -q -n multipath-tools-130222
%patch0001 -p1
@@ -405,12 +480,52 @@ kpartx manages partition creation and removal for device-mapper devices.
%patch0170 -p1
%patch0171 -p1
%patch0172 -p1
+%patch0173 -p1
+%patch0174 -p1
+%patch0175 -p1
+%patch0176 -p1
+%patch0177 -p1
+%patch0178 -p1
+%patch0179 -p1
+%patch0180 -p1
+%patch0181 -p1
+%patch0182 -p1
+%patch0183 -p1
+%patch0184 -p1
+%patch0185 -p1
+%patch0186 -p1
+%patch0187 -p1
+%patch0188 -p1
+%patch0189 -p1
+%patch0190 -p1
+%patch0191 -p1
+%patch0192 -p1
+%patch0194 -p1
+%patch0195 -p1
+%patch0196 -p1
+%patch0197 -p1
+%patch0198 -p1
+%patch0199 -p1
+%patch0200 -p1
+%patch0201 -p1
+%patch0202 -p1
+%patch0203 -p1
+%patch0204 -p1
+%patch0205 -p1
+%patch0206 -p1
+%patch0207 -p1
+%patch0208 -p1
+%patch0209 -p1
+%patch0210 -p1
+%patch0211 -p1
+%patch0212 -p1
cp %{SOURCE1} .
%build
%define _sbindir /usr/sbin
%define _libdir /usr/%{_lib}
%define _libmpathdir %{_libdir}/multipath
+%define _pkgconfdir %{_libdir}/pkgconfig
make %{?_smp_mflags} LIB=%{_lib}
%install
@@ -422,7 +537,9 @@ make install \
syslibdir=%{_libdir} \
libdir=%{_libmpathdir} \
rcdir=%{_initrddir} \
- unitdir=%{_unitdir}
+ unitdir=%{_unitdir} \
+ includedir=%{_includedir} \
+ pkgconfdir=%{_pkgconfdir}
# tree fix up
install -d %{buildroot}/etc/multipath
@@ -464,8 +581,6 @@ fi
%{_sbindir}/mpathconf
%{_sbindir}/mpathpersist
%{_unitdir}/multipathd.service
-%{_mandir}/man3/mpath_persistent_reserve_in.3.gz
-%{_mandir}/man3/mpath_persistent_reserve_out.3.gz
%{_mandir}/man5/multipath.conf.5.gz
%{_mandir}/man8/multipath.8.gz
%{_mandir}/man8/multipathd.8.gz
@@ -486,8 +601,8 @@ fi
%license COPYING
%{_libdir}/libmultipath.so
%{_libdir}/libmultipath.so.*
-%{_libdir}/libmpathpersist.so
%{_libdir}/libmpathpersist.so.*
+%{_libdir}/libmpathcmd.so.*
%dir %{_libmpathdir}
%{_libmpathdir}/*
@@ -495,6 +610,16 @@ fi
%postun libs -p /sbin/ldconfig
+%files devel
+%defattr(-,root,root,-)
+%doc AUTHOR COPYING
+%{_libdir}/libmpathpersist.so
+%{_libdir}/libmpathcmd.so
+%{_includedir}/mpath_cmd.h
+%{_includedir}/mpath_persist.h
+%{_mandir}/man3/mpath_persistent_reserve_in.3.gz
+%{_mandir}/man3/mpath_persistent_reserve_out.3.gz
+
%if 0%{?fedora} < 23
%files sysvinit
%{_initrddir}/multipathd
@@ -505,7 +630,114 @@ fi
%{_sbindir}/kpartx
%{_mandir}/man8/kpartx.8.gz
+%files -n libdmmp
+%defattr(-,root,root,-)
+%doc AUTHOR COPYING
+%{_libdir}/libdmmp.so.*
+
+%post -n libdmmp -p /sbin/ldconfig
+
+%postun -n libdmmp -p /sbin/ldconfig
+
+%files -n libdmmp-devel
+%defattr(-,root,root,-)
+%doc AUTHOR COPYING
+%{_libdir}/libdmmp.so
+%dir %{_includedir}/libdmmp
+%{_includedir}/libdmmp/*
+%{_mandir}/man3/dmmp_*
+%{_mandir}/man3/libdmmp.h.3.gz
+%{_pkgconfdir}/libdmmp.pc
+
%changelog
+* Fri Apr 7 2017 Benjamin Marzinski <bmarzins@redhat.com> 0.4.9-86
+- Modify 0136-RHBZ-1304687-wait-for-map-add.patch
+ * switch to missing_uev_wait_timeout to stop waiting for uev
+- Refresh 0137-RHBZ-1280524-clear-chkr-msg.patch
+- Refresh 0150-RHBZ-1253913-fix-startup-msg.patch
+- Refresh 0154-UPBZ-1291406-disable-reinstate.patch
+- Refresh 0156-UPBZ-1313324-dont-fail-discovery.patch
+- Refresh 0161-RHBZ-1311659-no-kpartx.patch
+- Refresh 0167-RHBZ-1335176-fix-show-cmds.patch
+- Add 0173-RH-update-man-page.patch
+- Add 0174-RHBZ-1362396-modprobe.patch
+ * make starting the multipathd service modprobe dm-multipath in the
+ sysvinit scripts
+- Add 0175-RHBZ-1357382-ordering.patch
+ * force multipathd.service to start after systemd-udev-trigger.service
+- Add 0176-RHBZ-1363830-fix-rename.patch
+ * initialized a variable to make dm_rename not fail randomly
+- Add 0177-libmultipath-correctly-initialize-pp-sg_id.patch
+ * This and all the following patches add the rbd patch checker
+- Add 0178-libmultipath-add-rbd-discovery.patch
+- Add 0179-multipath-tools-add-checker-callout-to-repair-path.patch
+- Add 0180-multipath-tools-Add-rbd-checker.patch
+- Add 0181-multipath-tools-Add-rbd-to-the-hwtable.patch
+- Add 0182-multipath-tools-check-for-initialized-checker-before.patch
+- Add 0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch
+- Add 0184-rbd-fix-sync-repair-support.patch
+- Add 0185-rbd-check-for-nonshared-clients.patch
+- Add 0186-rbd-check-for-exclusive-lock-enabled.patch
+- Add 0187-rbd-fixup-log-messages.patch
+- Add 0188-RHBZ-1368501-dont-exit.patch
+ * make multipathd not exit if it encounters recoverable errors on startup
+- Add 0189-RHBZ-1368211-remove-retries.patch
+ * add "remove_retries" multipath.conf parameter to make multiple attempts
+ to remove a multipath device if it is busy.
+- Add 0190-RHBZ-1380602-rbd-lock-on-read.patch
+ * pass lock_on_read when remapping image
+- Add 0191-RHBZ-1169168-disable-changed-paths.patch
+ * add "disabled_changed_wwids" multipath.conf parameter to disable
+ paths whose wwid changes
+- Add 0192-RHBZ-1362409-infinibox-config.patch
+- Add 0194-RHBZ-1351964-kpartx-recurse.patch
+ * fix recursion on corrupt dos partitions
+- Add 0195-RHBZ-1359510-no-daemon-msg.patch
+ * print a messages when multipathd isn't running
+- Add 0196-RHBZ-1239173-dont-set-flag.patch
+ * don't set reload flag on reloads when you gain your first
+ valid path
+- Add 0197-RHBZ-1394059-max-sectors-kb.patch
+ * add "max_sectors_kb" multipath.conf parameter to set max_sectors_kb
+ on a multipath device and all its path devices
+- Add 0198-RHBZ-1372032-detect-path-checker.patch
+ * add "detect_checker" multipath.conf parameter to detect ALUA arrays
+ and set the path checker to TUR
+- Add 0199-RHBZ-1279355-3pardata-config.patch
+- Add 0200-RHBZ-1402092-orphan-status.patch
+ * clear status on orphan paths
+- Add 0201-RHBZ-1403552-silence-warning.patch
+- Add 0202-RHBZ-1362120-skip-prio.patch
+ * don't run prio on failed paths
+- Add 0203-RHBZ-1363718-add-msgs.patch
+- Add 0204-RHBZ-1406226-nimble-config.patch
+- Add 0205-RHBZ-1416569-reset-stats.patch
+ * add "reset maps stats" and "reset map <map> stats" multipathd
+ interactive commands to reset the stats tracked by multipathd
+- Add 0206-RHBZ-1239173-pt2-no-paths.patch
+ * make multipath correctly disable scanning and rules running when
+ it gets a uevent and there are not valid paths.
+- Add 0207-UP-add-libmpathcmd.patch
+ * New shared library, libmpathcmd, that sends and receives messages from
+ multipathd. device-mapper-multipath now uses this library internally.
+- Add 0208-UPBZ-1430097-multipathd-IPC-changes.patch
+ * validation that modifying commands are coming from root.
+- Add 0209-UPBZ-1430097-multipath-C-API.patch
+ * New shared library. libdmmp, that presents the information from multipathd
+ in a structured manner to make it easier for callers to use
+- Add 0210-RH-fix-uninstall.patch
+ * Minor compilation fixes
+- Add 0211-RH-strlen-fix.patch
+ * checks that variables are not NULL before passing them to strlen
+- Add 0212-RHBZ-1431562-for-read-only.patch
+- Make 3 new subpackages
+ * device-mapper-multipath-devel, libdmmp, and libdmmp-devel. libmpathcmd
+ and libmpathprio are in device-mapper-multipath-libs and
+ device-mapper-multipath-devel. libdmmp is in its own subpackages
+- Move libmpathprio devel files to device-mapper-multipath-devel
+- Added BuildRequires on librados2-devel
+
+
* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.4.9-85
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild