c90e5b
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
c90e5b
From: Muneendra Kumar <muneendra.kumar@broadcom.com>
c90e5b
Date: Wed, 9 Feb 2022 19:28:10 -0800
c90e5b
Subject: [PATCH] multipathd: handle fpin events
c90e5b
c90e5b
This patch incorporates the functionality to handle
c90e5b
FPIN ELS events present as part of FCTransport daemon
c90e5b
(available in EPEL8) into the multipathd. This helps us to
c90e5b
reduce the response time to react and take the necessary actions
c90e5b
on receiving the FPIN events.
c90e5b
c90e5b
This patch currently support FPIN-Li Events.
c90e5b
c90e5b
It adds a new thread to listen for ELS frames from driver and on
c90e5b
receiving the frame payload, push the payload to a list and notify the
c90e5b
fpin_els_li_consumer thread to process it.Once consumer thread is
c90e5b
notified, it returns to listen for more ELS frames from driver.
c90e5b
c90e5b
The consumer thread process the ELS frames and moves the devices paths
c90e5b
which are affected due to link integrity to marginal path groups.
c90e5b
This also sets the associated portstate to marginal.
c90e5b
The paths which are set to marginal path group will be unset
c90e5b
on receiving the RSCN events
c90e5b
c90e5b
[ MW: minor fixup for 32bit compilation ]
c90e5b
c90e5b
Signed-off-by: Muneendra Kumar <muneendra.kumar@broadcom.com>
c90e5b
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
c90e5b
Signed-off-by: Martin Wilck <mwilck@suse.com>
c90e5b
Reviewed-by: Martin Wilck <mwilck@suse.com>
c90e5b
---
c90e5b
 Makefile.inc               |  13 +
c90e5b
 libmultipath/Makefile      |   5 +
c90e5b
 libmultipath/dict.c        |  56 +++-
c90e5b
 libmultipath/propsel.c     |  47 +++-
c90e5b
 libmultipath/structs.h     |   7 +
c90e5b
 multipath/multipath.conf.5 |  19 +-
c90e5b
 multipathd/Makefile        |  10 +
c90e5b
 multipathd/fpin.h          |  20 ++
c90e5b
 multipathd/fpin_handlers.c | 541 +++++++++++++++++++++++++++++++++++++
c90e5b
 multipathd/main.c          |  35 ++-
c90e5b
 10 files changed, 738 insertions(+), 15 deletions(-)
c90e5b
 create mode 100644 multipathd/fpin.h
c90e5b
 create mode 100644 multipathd/fpin_handlers.c
c90e5b
c90e5b
diff --git a/Makefile.inc b/Makefile.inc
c90e5b
index 220009e0..25c16f4e 100644
c90e5b
--- a/Makefile.inc
c90e5b
+++ b/Makefile.inc
c90e5b
@@ -146,6 +146,19 @@ check_file = $(shell \
c90e5b
 	echo "$$found" \
c90e5b
 	)
c90e5b
 
c90e5b
+# Check whether a file contains a variable with name $1 in header file $2
c90e5b
+check_var = $(shell \
c90e5b
+	if grep -Eq "(^|[[:blank:]])$1([[:blank:]]|=|$$)" "$2"; then \
c90e5b
+                found=1; \
c90e5b
+                status="yes"; \
c90e5b
+        else \
c90e5b
+                found=0; \
c90e5b
+                status="no"; \
c90e5b
+        fi; \
c90e5b
+        echo 1>&2 "Checking for ..  $1 in $2 ... $$status"; \
c90e5b
+        echo "$$found" \
c90e5b
+        )
c90e5b
+
c90e5b
 %.o:	%.c
c90e5b
 	@echo building $@ because of $?
c90e5b
 	$(CC) $(CFLAGS) -c -o $@ $<
c90e5b
diff --git a/libmultipath/Makefile b/libmultipath/Makefile
c90e5b
index ad690a49..49f71dfa 100644
c90e5b
--- a/libmultipath/Makefile
c90e5b
+++ b/libmultipath/Makefile
c90e5b
@@ -40,6 +40,11 @@ ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0)
c90e5b
 	CFLAGS += -DLIBDM_API_HOLD_CONTROL
c90e5b
 endif
c90e5b
 
c90e5b
+ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,/usr/include/scsi/fc/fc_els.h),0)
c90e5b
+	CFLAGS += -DFPIN_EVENT_HANDLER
c90e5b
+endif
c90e5b
+
c90e5b
+
c90e5b
 OBJS = memory.o parser.o vector.o devmapper.o callout.o \
c90e5b
 	hwtable.o blacklist.o util.o dmparser.o config.o \
c90e5b
 	structs.o discovery.o propsel.o dict.o \
c90e5b
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
c90e5b
index 8321ec1e..d7cd94a5 100644
c90e5b
--- a/libmultipath/dict.c
c90e5b
+++ b/libmultipath/dict.c
c90e5b
@@ -579,6 +579,59 @@ snprint_def_find_multipaths(struct config *conf, char *buff, int len,
c90e5b
 			 find_multipaths_optvals[conf->find_multipaths]);
c90e5b
 }
c90e5b
 
c90e5b
+static const char * const marginal_pathgroups_optvals[] = {
c90e5b
+	[MARGINAL_PATHGROUP_OFF] = "off",
c90e5b
+	[MARGINAL_PATHGROUP_ON] = "on",
c90e5b
+#ifdef FPIN_EVENT_HANDLER
c90e5b
+	[MARGINAL_PATHGROUP_FPIN] = "fpin",
c90e5b
+#endif
c90e5b
+};
c90e5b
+
c90e5b
+static int
c90e5b
+def_marginal_pathgroups_handler(struct config *conf, vector strvec,
c90e5b
+			    const char *file, int line_nr)
c90e5b
+{
c90e5b
+	char *buff;
c90e5b
+	unsigned int i;
c90e5b
+
c90e5b
+	buff = set_value(strvec);
c90e5b
+	if (!buff)
c90e5b
+		return 1;
c90e5b
+	for (i = MARGINAL_PATHGROUP_OFF;
c90e5b
+	     i < ARRAY_SIZE(marginal_pathgroups_optvals); i++) {
c90e5b
+		if (marginal_pathgroups_optvals[i] != NULL &&
c90e5b
+		    !strcmp(buff, marginal_pathgroups_optvals[i])) {
c90e5b
+			conf->marginal_pathgroups = i;
c90e5b
+			break;
c90e5b
+		}
c90e5b
+	}
c90e5b
+
c90e5b
+	if (i >= ARRAY_SIZE(marginal_pathgroups_optvals)) {
c90e5b
+		if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0)
c90e5b
+			conf->marginal_pathgroups = MARGINAL_PATHGROUP_OFF;
c90e5b
+		else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0)
c90e5b
+			conf->marginal_pathgroups = MARGINAL_PATHGROUP_ON;
c90e5b
+		/* This can only be true if FPIN_EVENT_HANDLER isn't defined,
c90e5b
+		 * otherwise this check will have already happened above */
c90e5b
+		else if (strcmp(buff, "fpin") == 0)
c90e5b
+			condlog(1, "%s line %d, support for \"fpin\" is not compiled in for marginal_pathgroups", file, line_nr);
c90e5b
+		else
c90e5b
+			condlog(1, "%s line %d, invalid value for marginal_pathgroups: \"%s\"",
c90e5b
+				file, line_nr, buff);
c90e5b
+	}
c90e5b
+	free(buff);
c90e5b
+	return 0;
c90e5b
+}
c90e5b
+
c90e5b
+static int
c90e5b
+snprint_def_marginal_pathgroups(struct config *conf, char *buff, int len,
c90e5b
+			    const void *data)
c90e5b
+{
c90e5b
+	return snprintf(buff, len, "\"%s\"",
c90e5b
+			marginal_pathgroups_optvals[conf->marginal_pathgroups]);
c90e5b
+}
c90e5b
+
c90e5b
+
c90e5b
 declare_def_handler(selector, set_str)
c90e5b
 declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR)
c90e5b
 declare_hw_handler(selector, set_str)
c90e5b
@@ -1596,9 +1649,6 @@ declare_ovr_snprint(all_tg_pt, print_yes_no_undef)
c90e5b
 declare_hw_handler(all_tg_pt, set_yes_no_undef)
c90e5b
 declare_hw_snprint(all_tg_pt, print_yes_no_undef)
c90e5b
 
c90e5b
-declare_def_handler(marginal_pathgroups, set_yes_no)
c90e5b
-declare_def_snprint(marginal_pathgroups, print_yes_no)
c90e5b
-
c90e5b
 declare_def_handler(recheck_wwid, set_yes_no_undef)
c90e5b
 declare_def_snprint_defint(recheck_wwid, print_yes_no_undef, DEFAULT_RECHECK_WWID)
c90e5b
 declare_ovr_handler(recheck_wwid, set_yes_no_undef)
c90e5b
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
c90e5b
index 209c1d67..be79902f 100644
c90e5b
--- a/libmultipath/propsel.c
c90e5b
+++ b/libmultipath/propsel.c
c90e5b
@@ -85,6 +85,8 @@ static const char cmdline_origin[] =
c90e5b
 	"(setting: multipath command line [-p] flag)";
c90e5b
 static const char autodetect_origin[] =
c90e5b
 	"(setting: storage device autodetected)";
c90e5b
+static const char fpin_marginal_path_origin[] =
c90e5b
+	"(setting: overridden by marginal_path_fpin)";
c90e5b
 static const char marginal_path_origin[] =
c90e5b
 	"(setting: implied by marginal_path check)";
c90e5b
 static const char delay_watch_origin[] =
c90e5b
@@ -1052,9 +1054,12 @@ int select_san_path_err_threshold(struct config *conf, struct multipath *mp)
c90e5b
 	const char *origin;
c90e5b
 	char buff[12];
c90e5b
 
c90e5b
-	if (marginal_path_check_enabled(mp)) {
c90e5b
+	if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
c90e5b
 		mp->san_path_err_threshold = NU_NO;
c90e5b
-		origin = marginal_path_origin;
c90e5b
+		if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
c90e5b
+			origin = fpin_marginal_path_origin;
c90e5b
+		else
c90e5b
+			origin = marginal_path_origin;
c90e5b
 		goto out;
c90e5b
 	}
c90e5b
 	mp_set_mpe(san_path_err_threshold);
c90e5b
@@ -1075,9 +1080,12 @@ int select_san_path_err_forget_rate(struct config *conf, struct multipath *mp)
c90e5b
 	const char *origin;
c90e5b
 	char buff[12];
c90e5b
 
c90e5b
-	if (marginal_path_check_enabled(mp)) {
c90e5b
+	if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
c90e5b
 		mp->san_path_err_forget_rate = NU_NO;
c90e5b
-		origin = marginal_path_origin;
c90e5b
+		if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
c90e5b
+			origin = fpin_marginal_path_origin;
c90e5b
+		else
c90e5b
+			origin = marginal_path_origin;
c90e5b
 		goto out;
c90e5b
 	}
c90e5b
 	mp_set_mpe(san_path_err_forget_rate);
c90e5b
@@ -1099,9 +1107,12 @@ int select_san_path_err_recovery_time(struct config *conf, struct multipath *mp)
c90e5b
 	const char *origin;
c90e5b
 	char buff[12];
c90e5b
 
c90e5b
-	if (marginal_path_check_enabled(mp)) {
c90e5b
+	if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
c90e5b
 		mp->san_path_err_recovery_time = NU_NO;
c90e5b
-		origin = marginal_path_origin;
c90e5b
+		if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
c90e5b
+			origin = fpin_marginal_path_origin;
c90e5b
+		else
c90e5b
+			origin = marginal_path_origin;
c90e5b
 		goto out;
c90e5b
 	}
c90e5b
 	mp_set_mpe(san_path_err_recovery_time);
c90e5b
@@ -1123,6 +1134,12 @@ int select_marginal_path_err_sample_time(struct config *conf, struct multipath *
c90e5b
 	const char *origin;
c90e5b
 	char buff[12];
c90e5b
 
c90e5b
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
c90e5b
+		mp->marginal_path_err_sample_time = NU_NO;
c90e5b
+		origin = fpin_marginal_path_origin;
c90e5b
+		goto out;
c90e5b
+	}
c90e5b
+
c90e5b
 	mp_set_mpe(marginal_path_err_sample_time);
c90e5b
 	mp_set_ovr(marginal_path_err_sample_time);
c90e5b
 	mp_set_hwe(marginal_path_err_sample_time);
c90e5b
@@ -1141,6 +1158,12 @@ int select_marginal_path_err_rate_threshold(struct config *conf, struct multipat
c90e5b
 	const char *origin;
c90e5b
 	char buff[12];
c90e5b
 
c90e5b
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
c90e5b
+		mp->marginal_path_err_rate_threshold = NU_NO;
c90e5b
+		origin = fpin_marginal_path_origin;
c90e5b
+		goto out;
c90e5b
+	}
c90e5b
+
c90e5b
 	mp_set_mpe(marginal_path_err_rate_threshold);
c90e5b
 	mp_set_ovr(marginal_path_err_rate_threshold);
c90e5b
 	mp_set_hwe(marginal_path_err_rate_threshold);
c90e5b
@@ -1159,6 +1182,12 @@ int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multip
c90e5b
 	const char *origin;
c90e5b
 	char buff[12];
c90e5b
 
c90e5b
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
c90e5b
+		mp->marginal_path_err_recheck_gap_time = NU_NO;
c90e5b
+		origin = fpin_marginal_path_origin;
c90e5b
+		goto out;
c90e5b
+	}
c90e5b
+
c90e5b
 	mp_set_mpe(marginal_path_err_recheck_gap_time);
c90e5b
 	mp_set_ovr(marginal_path_err_recheck_gap_time);
c90e5b
 	mp_set_hwe(marginal_path_err_recheck_gap_time);
c90e5b
@@ -1177,6 +1206,12 @@ int select_marginal_path_double_failed_time(struct config *conf, struct multipat
c90e5b
 	const char *origin;
c90e5b
 	char buff[12];
c90e5b
 
c90e5b
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
c90e5b
+		mp->marginal_path_double_failed_time = NU_NO;
c90e5b
+		origin = fpin_marginal_path_origin;
c90e5b
+		goto out;
c90e5b
+	}
c90e5b
+
c90e5b
 	mp_set_mpe(marginal_path_double_failed_time);
c90e5b
 	mp_set_ovr(marginal_path_double_failed_time);
c90e5b
 	mp_set_hwe(marginal_path_double_failed_time);
c90e5b
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
c90e5b
index 875e726e..3ed5cfc1 100644
c90e5b
--- a/libmultipath/structs.h
c90e5b
+++ b/libmultipath/structs.h
c90e5b
@@ -128,6 +128,12 @@ enum find_multipaths_states {
c90e5b
 	__FIND_MULTIPATHS_LAST,
c90e5b
 };
c90e5b
 
c90e5b
+enum marginal_pathgroups_mode {
c90e5b
+	MARGINAL_PATHGROUP_OFF = YN_NO,
c90e5b
+	MARGINAL_PATHGROUP_ON = YN_YES,
c90e5b
+	MARGINAL_PATHGROUP_FPIN,
c90e5b
+};
c90e5b
+
c90e5b
 enum flush_states {
c90e5b
 	FLUSH_UNDEF = YNU_UNDEF,
c90e5b
 	FLUSH_DISABLED = YNU_NO,
c90e5b
@@ -429,6 +435,7 @@ struct multipath {
c90e5b
 	unsigned char prflag;
c90e5b
 	int all_tg_pt;
c90e5b
 	struct gen_multipath generic_mp;
c90e5b
+	bool fpin_must_reload;
c90e5b
 };
c90e5b
 
c90e5b
 static inline int marginal_path_check_enabled(const struct multipath *mpp)
c90e5b
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
c90e5b
index abbc89af..805b7a5e 100644
c90e5b
--- a/multipath/multipath.conf.5
c90e5b
+++ b/multipath/multipath.conf.5
c90e5b
@@ -1063,20 +1063,26 @@ The default is: \fBno\fR
c90e5b
 .
c90e5b
 .TP
c90e5b
 .B marginal_pathgroups
c90e5b
-If set to \fIno\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and
c90e5b
+If set to \fIoff\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and
c90e5b
 \fIsan_path_err_*\fR options will keep marginal, or \(dqshaky\(dq, paths from
c90e5b
 being reinstated until they have been monitored for some time. This can cause
c90e5b
 situations where all non-marginal paths are down, and no paths are usable
c90e5b
 until multipathd detects this and reinstates a marginal path. If the multipath
c90e5b
 device is not configured to queue IO in this case, it can cause IO errors to
c90e5b
 occur, even though there are marginal paths available.  However, if this
c90e5b
-option is set to \fIyes\fR, when one of the marginal path detecting methods
c90e5b
+option is set to \fIon\fR, when one of the marginal path detecting methods
c90e5b
 determines that a path is marginal, it will be reinstated and placed in a
c90e5b
 seperate pathgroup that will only be used after all the non-marginal pathgroups
c90e5b
 have been tried first. This prevents the possibility of IO errors occuring
c90e5b
 while marginal paths are still usable. After the path has been monitored
c90e5b
 for the configured time, and is declared healthy, it will be returned to its
c90e5b
-normal pathgroup. See "Shaky paths detection" below for more information.
c90e5b
+normal pathgroup.
c90e5b
+However if this option is set to \fIfpin\fR multipathd will receive fpin
c90e5b
+notifications, set path states to "marginal" accordingly, and regroup paths
c90e5b
+as described for "marginal_pathgroups yes". This option can't be used in combination
c90e5b
+with other options for "Shaky path detection" (see below).If it is set to fpin,
c90e5b
+marginal_path_xyz and san_path_err_xyz parameters are implicitly set to 0.
c90e5b
+See "Shaky paths detection" below for more information.
c90e5b
 .RS
c90e5b
 .TP
c90e5b
 The default  is: \fBno\fR
c90e5b
@@ -1852,6 +1858,13 @@ increase and the threshold is never reached. Ticks are the time between
c90e5b
 path checks by multipathd, which is variable and controlled by the
c90e5b
 \fIpolling_interval\fR and \fImax_polling_interval\fR parameters.
c90e5b
 .
c90e5b
+.TP
c90e5b
+.B \(dqFPIN \(dq failure tracking
c90e5b
+Fibre channel fabrics can notify hosts about fabric-level issues such
c90e5b
+as integrity failures or congestion with so-called Fabric Performance
c90e5b
+Impact Notifications (FPINs).On receiving the fpin notifications through ELS
c90e5b
+multipathd will move the affected path and port states to marginal.
c90e5b
+.
c90e5b
 .RS 8
c90e5b
 .LP
c90e5b
 This method is \fBdeprecated\fR in favor of the \(dqmarginal_path\(dq failure
c90e5b
diff --git a/multipathd/Makefile b/multipathd/Makefile
c90e5b
index 8d901178..835edd93 100644
c90e5b
--- a/multipathd/Makefile
c90e5b
+++ b/multipathd/Makefile
c90e5b
@@ -1,5 +1,9 @@
c90e5b
 include ../Makefile.inc
c90e5b
 
c90e5b
+ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,/usr/include/scsi/fc/fc_els.h),0)
c90e5b
+	CFLAGS += -DFPIN_EVENT_HANDLER
c90e5b
+	FPIN_SUPPORT = 1
c90e5b
+endif
c90e5b
 #
c90e5b
 # debugging stuff
c90e5b
 #
c90e5b
@@ -28,6 +32,12 @@ endif
c90e5b
 OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \
c90e5b
        dmevents.o
c90e5b
 
c90e5b
+ifeq ($(FPIN_SUPPORT),1)
c90e5b
+OBJS += fpin_handlers.o
c90e5b
+endif
c90e5b
+
c90e5b
+
c90e5b
+
c90e5b
 EXEC = multipathd
c90e5b
 
c90e5b
 all : $(EXEC)
c90e5b
diff --git a/multipathd/fpin.h b/multipathd/fpin.h
c90e5b
new file mode 100644
c90e5b
index 00000000..bfcc1ce2
c90e5b
--- /dev/null
c90e5b
+++ b/multipathd/fpin.h
c90e5b
@@ -0,0 +1,20 @@
c90e5b
+#ifndef __FPIN_H__
c90e5b
+#define __FPIN_H__
c90e5b
+
c90e5b
+#ifdef FPIN_EVENT_HANDLER
c90e5b
+void *fpin_fabric_notification_receiver(void *unused);
c90e5b
+void *fpin_els_li_consumer(void *data);
c90e5b
+void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg);
c90e5b
+#else
c90e5b
+static void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
c90e5b
+{
c90e5b
+	return NULL;
c90e5b
+}
c90e5b
+static void *fpin_els_li_consumer(__attribute__((unused))void *data)
c90e5b
+{
c90e5b
+	return NULL;
c90e5b
+}
c90e5b
+/* fpin_clean_marginal_dev_list() is never called */
c90e5b
+#endif
c90e5b
+
c90e5b
+#endif
c90e5b
diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c
c90e5b
new file mode 100644
c90e5b
index 00000000..b14366d7
c90e5b
--- /dev/null
c90e5b
+++ b/multipathd/fpin_handlers.c
c90e5b
@@ -0,0 +1,541 @@
c90e5b
+#include <errno.h>
c90e5b
+#include <unistd.h>
c90e5b
+#include <sys/types.h>
c90e5b
+#include <sys/socket.h>
c90e5b
+#include <libudev.h>
c90e5b
+#include <stdint.h>
c90e5b
+#include <scsi/scsi_netlink_fc.h>
c90e5b
+#include <scsi/fc/fc_els.h>
c90e5b
+
c90e5b
+#include "parser.h"
c90e5b
+#include "vector.h"
c90e5b
+#include "structs.h"
c90e5b
+#include "structs_vec.h"
c90e5b
+#include "main.h"
c90e5b
+#include "debug.h"
c90e5b
+#include "util.h"
c90e5b
+#include "sysfs.h"
c90e5b
+
c90e5b
+#include "fpin.h"
c90e5b
+#include "devmapper.h"
c90e5b
+
c90e5b
+static pthread_cond_t fpin_li_cond = PTHREAD_COND_INITIALIZER;
c90e5b
+static pthread_mutex_t fpin_li_mutex = PTHREAD_MUTEX_INITIALIZER;
c90e5b
+static pthread_mutex_t fpin_li_marginal_dev_mutex = PTHREAD_MUTEX_INITIALIZER;
c90e5b
+
c90e5b
+static LIST_HEAD(els_marginal_list_head);
c90e5b
+static LIST_HEAD(fpin_li_marginal_dev_list_head);
c90e5b
+
c90e5b
+
c90e5b
+#define DEF_RX_BUF_SIZE	4096
c90e5b
+#define DEV_NAME_LEN	128
c90e5b
+#define FCH_EVT_LINKUP 0x2
c90e5b
+#define FCH_EVT_LINK_FPIN 0x501
c90e5b
+#define FCH_EVT_RSCN 0x5
c90e5b
+
c90e5b
+#define list_first_entry(ptr, type, member) \
c90e5b
+	list_entry((ptr)->next, type, member)
c90e5b
+
c90e5b
+/* max ELS frame Size */
c90e5b
+#define FC_PAYLOAD_MAXLEN   2048
c90e5b
+
c90e5b
+struct els_marginal_list {
c90e5b
+	uint32_t event_code;
c90e5b
+	uint16_t host_num;
c90e5b
+	uint16_t length;
c90e5b
+	char payload[FC_PAYLOAD_MAXLEN];
c90e5b
+	struct list_head node;
c90e5b
+};
c90e5b
+/* Structure to store the marginal devices info */
c90e5b
+struct marginal_dev_list {
c90e5b
+	char dev_t[BLK_DEV_SIZE];
c90e5b
+	uint32_t host_num;
c90e5b
+	struct list_head node;
c90e5b
+};
c90e5b
+
c90e5b
+static void _udev_device_unref(void *p)
c90e5b
+{
c90e5b
+	udev_device_unref(p);
c90e5b
+}
c90e5b
+
c90e5b
+
c90e5b
+/*set/unset the path state to marginal*/
c90e5b
+static int fpin_set_pathstate(struct path *pp, bool set)
c90e5b
+{
c90e5b
+	const char *action = set ? "set" : "unset";
c90e5b
+
c90e5b
+	if (!pp || !pp->mpp || !pp->mpp->alias)
c90e5b
+		return -1;
c90e5b
+
c90e5b
+	condlog(3, "\n%s: %s  marginal path %s (fpin)",
c90e5b
+		action, pp->mpp->alias, pp->dev_t);
c90e5b
+	pp->marginal = set;
c90e5b
+	pp->mpp->fpin_must_reload = true;
c90e5b
+	return 0;
c90e5b
+}
c90e5b
+
c90e5b
+/* This will unset marginal state of a device*/
c90e5b
+static void fpin_path_unsetmarginal(char *devname, struct vectors *vecs)
c90e5b
+{
c90e5b
+	struct path *pp;
c90e5b
+
c90e5b
+	pp = find_path_by_dev(vecs->pathvec, devname);
c90e5b
+	if (!pp)
c90e5b
+		pp = find_path_by_devt(vecs->pathvec, devname);
c90e5b
+
c90e5b
+	fpin_set_pathstate(pp, false);
c90e5b
+}
c90e5b
+
c90e5b
+/*This will set the marginal state of a device*/
c90e5b
+static int fpin_path_setmarginal(struct path *pp)
c90e5b
+{
c90e5b
+	return fpin_set_pathstate(pp, true);
c90e5b
+}
c90e5b
+
c90e5b
+/* Unsets all the devices in the list from marginal state */
c90e5b
+static void
c90e5b
+fpin_unset_marginal_dev(uint32_t host_num, struct vectors *vecs)
c90e5b
+{
c90e5b
+	struct marginal_dev_list *tmp_marg = NULL;
c90e5b
+	struct marginal_dev_list *marg = NULL;
c90e5b
+	struct multipath *mpp;
c90e5b
+	int ret = 0;
c90e5b
+	int i;
c90e5b
+
c90e5b
+	pthread_cleanup_push(cleanup_lock, &vecs->lock);
c90e5b
+	lock(&vecs->lock);
c90e5b
+	pthread_testcancel();
c90e5b
+
c90e5b
+	pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
c90e5b
+	pthread_cleanup_push(cleanup_mutex, &fpin_li_marginal_dev_mutex);
c90e5b
+	pthread_testcancel();
c90e5b
+	if (list_empty(&fpin_li_marginal_dev_list_head)) {
c90e5b
+		condlog(4, "Marginal List is empty\n");
c90e5b
+		goto empty;
c90e5b
+	}
c90e5b
+	list_for_each_entry_safe(marg, tmp_marg, &fpin_li_marginal_dev_list_head, node) {
c90e5b
+		if (marg->host_num != host_num)
c90e5b
+			continue;
c90e5b
+		condlog(4, " unsetting marginal dev: is %s %d\n",
c90e5b
+				tmp_marg->dev_t, tmp_marg->host_num);
c90e5b
+		fpin_path_unsetmarginal(marg->dev_t, vecs);
c90e5b
+		list_del(&marg->node);
c90e5b
+		free(marg);
c90e5b
+	}
c90e5b
+empty:
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	/* walk backwards because update_path_groups() can remove mpp */
c90e5b
+	vector_foreach_slot_backwards(vecs->mpvec, mpp, i) {
c90e5b
+		if (mpp->fpin_must_reload) {
c90e5b
+			ret = update_path_groups(mpp, vecs, 0);
c90e5b
+			if (ret == 2)
c90e5b
+				condlog(2, "map removed during reload");
c90e5b
+			else
c90e5b
+				mpp->fpin_must_reload = false;
c90e5b
+		}
c90e5b
+	}
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+}
c90e5b
+
c90e5b
+/*
c90e5b
+ * On Receiving the frame from HBA driver, insert the frame into link
c90e5b
+ * integrity frame list which will be picked up later by consumer thread for
c90e5b
+ * processing.
c90e5b
+ */
c90e5b
+static int
c90e5b
+fpin_els_add_li_frame(struct fc_nl_event *fc_event)
c90e5b
+{
c90e5b
+	struct els_marginal_list *els_mrg = NULL;
c90e5b
+	int ret = 0;
c90e5b
+
c90e5b
+	if (fc_event->event_datalen > FC_PAYLOAD_MAXLEN)
c90e5b
+		return -EINVAL;
c90e5b
+
c90e5b
+	pthread_mutex_lock(&fpin_li_mutex);
c90e5b
+	pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex);
c90e5b
+	pthread_testcancel();
c90e5b
+	els_mrg = calloc(1, sizeof(struct els_marginal_list));
c90e5b
+	if (els_mrg != NULL) {
c90e5b
+		els_mrg->host_num = fc_event->host_no;
c90e5b
+		els_mrg->event_code = fc_event->event_code;
c90e5b
+		els_mrg->length = fc_event->event_datalen;
c90e5b
+		memcpy(els_mrg->payload, &(fc_event->event_data), fc_event->event_datalen);
c90e5b
+		list_add_tail(&els_mrg->node, &els_marginal_list_head);
c90e5b
+		pthread_cond_signal(&fpin_li_cond);
c90e5b
+	} else
c90e5b
+		ret = -ENOMEM;
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	return ret;
c90e5b
+
c90e5b
+}
c90e5b
+
c90e5b
+/*Sets the rport port_state to marginal*/
c90e5b
+static void fpin_set_rport_marginal(struct udev_device *rport_dev)
c90e5b
+{
c90e5b
+	sysfs_attr_set_value(rport_dev, "port_state",
c90e5b
+				"Marginal", strlen("Marginal"));
c90e5b
+}
c90e5b
+
c90e5b
+/*Add the marginal devices info into the list*/
c90e5b
+static void
c90e5b
+fpin_add_marginal_dev_info(uint32_t host_num, char *devname)
c90e5b
+{
c90e5b
+	struct marginal_dev_list *newdev = NULL;
c90e5b
+
c90e5b
+	newdev = calloc(1, sizeof(struct marginal_dev_list));
c90e5b
+	if (newdev != NULL) {
c90e5b
+		newdev->host_num = host_num;
c90e5b
+		strlcpy(newdev->dev_t, devname, BLK_DEV_SIZE);
c90e5b
+		condlog(4, "\n%s hostno %d devname %s\n", __func__,
c90e5b
+				host_num, newdev->dev_t);
c90e5b
+		pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
c90e5b
+		list_add_tail(&(newdev->node),
c90e5b
+				&fpin_li_marginal_dev_list_head);
c90e5b
+		pthread_mutex_unlock(&fpin_li_marginal_dev_mutex);
c90e5b
+	}
c90e5b
+}
c90e5b
+
c90e5b
+/*
c90e5b
+ * This function goes through the vecs->pathvec, and for
c90e5b
+ * each path, check that the host  number,
c90e5b
+ * the target WWPN associated with the path matches
c90e5b
+ * with the els wwpn and sets the path and port state to
c90e5b
+ * Marginal
c90e5b
+ */
c90e5b
+static int  fpin_chk_wwn_setpath_marginal(uint16_t host_num,  struct vectors *vecs,
c90e5b
+		uint64_t els_wwpn)
c90e5b
+{
c90e5b
+	struct path *pp;
c90e5b
+	struct multipath *mpp;
c90e5b
+	int i, k;
c90e5b
+	char rport_id[42];
c90e5b
+	const char *value = NULL;
c90e5b
+	struct udev_device *rport_dev = NULL;
c90e5b
+	uint64_t wwpn;
c90e5b
+	int ret = 0;
c90e5b
+
c90e5b
+	pthread_cleanup_push(cleanup_lock, &vecs->lock);
c90e5b
+	lock(&vecs->lock);
c90e5b
+	pthread_testcancel();
c90e5b
+
c90e5b
+	vector_foreach_slot(vecs->pathvec, pp, k) {
c90e5b
+		/* Checks the host number and also for the SCSI FCP */
c90e5b
+		if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num !=  pp->sg_id.host_no)
c90e5b
+			continue;
c90e5b
+		sprintf(rport_id, "rport-%d:%d-%d",
c90e5b
+				pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
c90e5b
+		rport_dev = udev_device_new_from_subsystem_sysname(udev,
c90e5b
+				"fc_remote_ports", rport_id);
c90e5b
+		if (!rport_dev) {
c90e5b
+			condlog(2, "%s: No fc_remote_port device for '%s'", pp->dev,
c90e5b
+					rport_id);
c90e5b
+			continue;
c90e5b
+		}
c90e5b
+		pthread_cleanup_push(_udev_device_unref, rport_dev);
c90e5b
+		value = udev_device_get_sysattr_value(rport_dev, "port_name");
c90e5b
+		if (!value)
c90e5b
+			goto unref;
c90e5b
+
c90e5b
+		if (value)
c90e5b
+			wwpn =  strtol(value, NULL, 16);
c90e5b
+		/*
c90e5b
+		 * If the port wwpn matches sets the path and port state
c90e5b
+		 * to marginal
c90e5b
+		 */
c90e5b
+		if (wwpn == els_wwpn) {
c90e5b
+			ret = fpin_path_setmarginal(pp);
c90e5b
+			if (ret < 0)
c90e5b
+				goto unref;
c90e5b
+			fpin_set_rport_marginal(rport_dev);
c90e5b
+			fpin_add_marginal_dev_info(host_num, pp->dev);
c90e5b
+		}
c90e5b
+unref:
c90e5b
+		pthread_cleanup_pop(1);
c90e5b
+	}
c90e5b
+	/* walk backwards because update_path_groups() can remove mpp */
c90e5b
+	vector_foreach_slot_backwards(vecs->mpvec, mpp, i) {
c90e5b
+		if (mpp->fpin_must_reload) {
c90e5b
+			ret = update_path_groups(mpp, vecs, 0);
c90e5b
+			if (ret == 2)
c90e5b
+				condlog(2, "map removed during reload");
c90e5b
+			else
c90e5b
+				mpp->fpin_must_reload = false;
c90e5b
+		}
c90e5b
+	}
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	return ret;
c90e5b
+}
c90e5b
+
c90e5b
+/*
c90e5b
+ * This function loops around all the impacted wwns received as part of els
c90e5b
+ * frame and sets the associated path and port states to marginal.
c90e5b
+ */
c90e5b
+static int
c90e5b
+fpin_parse_li_els_setpath_marginal(uint16_t host_num, struct fc_tlv_desc *tlv,
c90e5b
+		struct vectors *vecs)
c90e5b
+{
c90e5b
+	uint32_t wwn_count = 0, iter = 0;
c90e5b
+	uint64_t wwpn;
c90e5b
+	struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
c90e5b
+	int count = 0;
c90e5b
+	int ret = 0;
c90e5b
+
c90e5b
+	/* Update the wwn to list */
c90e5b
+	wwn_count = be32_to_cpu(li_desc->pname_count);
c90e5b
+	condlog(4, "Got wwn count as %d\n", wwn_count);
c90e5b
+
c90e5b
+	for (iter = 0; iter < wwn_count; iter++) {
c90e5b
+		wwpn = be64_to_cpu(li_desc->pname_list[iter]);
c90e5b
+		ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn);
c90e5b
+		if (ret < 0)
c90e5b
+			condlog(2, "failed to set the path marginal associated with wwpn: 0x%" PRIx64 "\n", wwpn);
c90e5b
+
c90e5b
+		count++;
c90e5b
+	}
c90e5b
+	return count;
c90e5b
+}
c90e5b
+
c90e5b
+/*
c90e5b
+ * This function process the ELS frame received from HBA driver,
c90e5b
+ * and sets the path associated with the port wwn to marginal
c90e5b
+ * and also set the port state to marginal.
c90e5b
+ */
c90e5b
+static int
c90e5b
+fpin_process_els_frame(uint16_t host_num, char *fc_payload, struct vectors *vecs)
c90e5b
+{
c90e5b
+
c90e5b
+	int count = -1;
c90e5b
+	struct fc_els_fpin *fpin = (struct fc_els_fpin *)fc_payload;
c90e5b
+	struct fc_tlv_desc *tlv;
c90e5b
+
c90e5b
+	tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
c90e5b
+
c90e5b
+	/*
c90e5b
+	 * Parse the els frame and set the affected paths and port
c90e5b
+	 * state to marginal
c90e5b
+	 */
c90e5b
+	count = fpin_parse_li_els_setpath_marginal(host_num, tlv, vecs);
c90e5b
+	if (count <= 0)
c90e5b
+		condlog(4, "Could not find any WWNs, ret = %d\n",
c90e5b
+					count);
c90e5b
+	return count;
c90e5b
+}
c90e5b
+
c90e5b
+/*
c90e5b
+ * This function process the FPIN ELS frame received from HBA driver,
c90e5b
+ * and push the frame to appropriate frame list. Currently we have only FPIN
c90e5b
+ * LI frame list.
c90e5b
+ */
c90e5b
+static int
c90e5b
+fpin_handle_els_frame(struct fc_nl_event *fc_event)
c90e5b
+{
c90e5b
+	int ret = -1;
c90e5b
+	uint32_t els_cmd;
c90e5b
+	struct fc_els_fpin *fpin = (struct fc_els_fpin *)&fc_event->event_data;
c90e5b
+	struct fc_tlv_desc *tlv;
c90e5b
+	uint32_t dtag;
c90e5b
+
c90e5b
+	els_cmd = (uint32_t)fc_event->event_data;
c90e5b
+	tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
c90e5b
+	dtag = be32_to_cpu(tlv->desc_tag);
c90e5b
+	condlog(4, "Got CMD in add as 0x%x fpin_cmd 0x%x dtag 0x%x\n",
c90e5b
+			els_cmd, fpin->fpin_cmd, dtag);
c90e5b
+
c90e5b
+	if ((fc_event->event_code == FCH_EVT_LINK_FPIN) ||
c90e5b
+			(fc_event->event_code == FCH_EVT_LINKUP) ||
c90e5b
+			(fc_event->event_code == FCH_EVT_RSCN)) {
c90e5b
+
c90e5b
+		if (els_cmd == ELS_FPIN) {
c90e5b
+			/*
c90e5b
+			 * Check the type of fpin by checking the tag info
c90e5b
+			 * At present we are supporting only LI events
c90e5b
+			 */
c90e5b
+			if (dtag == ELS_DTAG_LNK_INTEGRITY) {
c90e5b
+				/*Push the Payload to FPIN frame queue. */
c90e5b
+				ret = fpin_els_add_li_frame(fc_event);
c90e5b
+				if (ret != 0)
c90e5b
+					condlog(0, "Failed to process LI frame with error %d\n",
c90e5b
+							ret);
c90e5b
+			} else {
c90e5b
+				condlog(4, "Unsupported FPIN received 0x%x\n", dtag);
c90e5b
+				return ret;
c90e5b
+			}
c90e5b
+		} else {
c90e5b
+			/*Push the Payload to FPIN frame queue. */
c90e5b
+			ret = fpin_els_add_li_frame(fc_event);
c90e5b
+			if (ret != 0)
c90e5b
+				condlog(0, "Failed to process Linkup/RSCN event with error %d evnt %d\n",
c90e5b
+						ret, fc_event->event_code);
c90e5b
+		}
c90e5b
+	} else
c90e5b
+		condlog(4, "Invalid command received: 0x%x\n", els_cmd);
c90e5b
+	return ret;
c90e5b
+}
c90e5b
+
c90e5b
+/*cleans the global marginal dev list*/
c90e5b
+void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg)
c90e5b
+{
c90e5b
+	struct marginal_dev_list *tmp_marg = NULL;
c90e5b
+
c90e5b
+	pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
c90e5b
+	while (!list_empty(&fpin_li_marginal_dev_list_head)) {
c90e5b
+		tmp_marg  = list_first_entry(&fpin_li_marginal_dev_list_head,
c90e5b
+				struct marginal_dev_list, node);
c90e5b
+		list_del(&tmp_marg->node);
c90e5b
+		free(tmp_marg);
c90e5b
+	}
c90e5b
+	pthread_mutex_unlock(&fpin_li_marginal_dev_mutex);
c90e5b
+}
c90e5b
+
c90e5b
+/* Cleans the global els  marginal list */
c90e5b
+static void fpin_clean_els_marginal_list(void *arg)
c90e5b
+{
c90e5b
+	struct list_head *head = (struct list_head *)arg;
c90e5b
+	struct els_marginal_list *els_marg;
c90e5b
+
c90e5b
+	while (!list_empty(head)) {
c90e5b
+		els_marg  = list_first_entry(head, struct els_marginal_list,
c90e5b
+					     node);
c90e5b
+		list_del(&els_marg->node);
c90e5b
+		free(els_marg);
c90e5b
+	}
c90e5b
+}
c90e5b
+
c90e5b
+static void rcu_unregister(__attribute__((unused)) void *param)
c90e5b
+{
c90e5b
+	rcu_unregister_thread();
c90e5b
+}
c90e5b
+/*
c90e5b
+ * This is the FPIN ELS consumer thread. The thread sleeps on pthread cond
c90e5b
+ * variable unless notified by fpin_fabric_notification_receiver thread.
c90e5b
+ * This thread is only to process FPIN-LI ELS frames. A new thread and frame
c90e5b
+ * list will be added if any more ELS frames types are to be supported.
c90e5b
+ */
c90e5b
+void *fpin_els_li_consumer(void *data)
c90e5b
+{
c90e5b
+	struct list_head marginal_list_head;
c90e5b
+	int ret = 0;
c90e5b
+	uint16_t host_num;
c90e5b
+	struct els_marginal_list *els_marg;
c90e5b
+	uint32_t event_code;
c90e5b
+	struct vectors *vecs = (struct vectors *)data;
c90e5b
+
c90e5b
+	pthread_cleanup_push(rcu_unregister, NULL);
c90e5b
+	rcu_register_thread();
c90e5b
+	pthread_cleanup_push(fpin_clean_marginal_dev_list, NULL);
c90e5b
+	INIT_LIST_HEAD(&marginal_list_head);
c90e5b
+	pthread_cleanup_push(fpin_clean_els_marginal_list,
c90e5b
+				(void *)&marginal_list_head);
c90e5b
+	for ( ; ; ) {
c90e5b
+		pthread_mutex_lock(&fpin_li_mutex);
c90e5b
+		pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex);
c90e5b
+		pthread_testcancel();
c90e5b
+		while (list_empty(&els_marginal_list_head))
c90e5b
+			pthread_cond_wait(&fpin_li_cond, &fpin_li_mutex);
c90e5b
+
c90e5b
+		if (!list_empty(&els_marginal_list_head)) {
c90e5b
+			condlog(4, "Invoke List splice tail\n");
c90e5b
+			list_splice_tail_init(&els_marginal_list_head, &marginal_list_head);
c90e5b
+		}
c90e5b
+		pthread_cleanup_pop(1);
c90e5b
+
c90e5b
+		while (!list_empty(&marginal_list_head)) {
c90e5b
+			els_marg  = list_first_entry(&marginal_list_head,
c90e5b
+							struct els_marginal_list, node);
c90e5b
+			host_num = els_marg->host_num;
c90e5b
+			event_code = els_marg->event_code;
c90e5b
+			/* Now finally process FPIN LI ELS Frame */
c90e5b
+			condlog(4, "Got a new Payload buffer, processing it\n");
c90e5b
+			if ((event_code ==  FCH_EVT_LINKUP) || (event_code == FCH_EVT_RSCN))
c90e5b
+				 fpin_unset_marginal_dev(host_num, vecs);
c90e5b
+			else {
c90e5b
+				ret = fpin_process_els_frame(host_num, els_marg->payload, vecs);
c90e5b
+				if (ret <= 0)
c90e5b
+					condlog(0, "ELS frame processing failed with ret %d\n", ret);
c90e5b
+			}
c90e5b
+			list_del(&els_marg->node);
c90e5b
+			free(els_marg);
c90e5b
+
c90e5b
+		}
c90e5b
+	}
c90e5b
+
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	return NULL;
c90e5b
+}
c90e5b
+
c90e5b
+static void receiver_cleanup_list(__attribute__((unused)) void *arg)
c90e5b
+{
c90e5b
+	pthread_mutex_lock(&fpin_li_mutex);
c90e5b
+	fpin_clean_els_marginal_list(&els_marginal_list_head);
c90e5b
+	pthread_mutex_unlock(&fpin_li_mutex);
c90e5b
+}
c90e5b
+
c90e5b
+/*
c90e5b
+ * Listen for ELS frames from driver. on receiving the frame payload,
c90e5b
+ * push the payload to a list, and notify the fpin_els_li_consumer thread to
c90e5b
+ * process it. Once consumer thread is notified, return to listen for more ELS
c90e5b
+ * frames from driver.
c90e5b
+ */
c90e5b
+void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
c90e5b
+{
c90e5b
+	int ret;
c90e5b
+	long fd;
c90e5b
+	uint32_t els_cmd;
c90e5b
+	struct fc_nl_event *fc_event = NULL;
c90e5b
+	struct sockaddr_nl fc_local;
c90e5b
+	unsigned char buf[DEF_RX_BUF_SIZE] __attribute__((aligned(sizeof(uint64_t))));
c90e5b
+	size_t plen = 0;
c90e5b
+
c90e5b
+	pthread_cleanup_push(rcu_unregister, NULL);
c90e5b
+	rcu_register_thread();
c90e5b
+
c90e5b
+	pthread_cleanup_push(receiver_cleanup_list, NULL);
c90e5b
+	fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_SCSITRANSPORT);
c90e5b
+	if (fd < 0) {
c90e5b
+		condlog(0, "fc socket error %ld", fd);
c90e5b
+		return NULL;
c90e5b
+	}
c90e5b
+
c90e5b
+	pthread_cleanup_push(close_fd, (void *)fd);
c90e5b
+	memset(&fc_local, 0, sizeof(fc_local));
c90e5b
+	fc_local.nl_family = AF_NETLINK;
c90e5b
+	fc_local.nl_groups = ~0;
c90e5b
+	fc_local.nl_pid = getpid();
c90e5b
+	ret = bind(fd, (struct sockaddr *)&fc_local, sizeof(fc_local));
c90e5b
+	if (ret == -1) {
c90e5b
+		condlog(0, "fc socket bind error %d\n", ret);
c90e5b
+		goto out;
c90e5b
+	}
c90e5b
+	for ( ; ; ) {
c90e5b
+		condlog(4, "Waiting for ELS...\n");
c90e5b
+		ret = read(fd, buf, DEF_RX_BUF_SIZE);
c90e5b
+		if (ret < 0) {
c90e5b
+			condlog(0, "failed to read the els frame (%d)", ret);
c90e5b
+			continue;
c90e5b
+		}
c90e5b
+		condlog(4, "Got a new request %d\n", ret);
c90e5b
+		if (!NLMSG_OK((struct nlmsghdr *)buf, (unsigned int)ret)) {
c90e5b
+			condlog(0, "bad els frame read (%d)", ret);
c90e5b
+			continue;
c90e5b
+		}
c90e5b
+		/* Push the frame to appropriate frame list */
c90e5b
+		plen = NLMSG_PAYLOAD((struct nlmsghdr *)buf, 0);
c90e5b
+		fc_event = (struct fc_nl_event *)NLMSG_DATA(buf);
c90e5b
+		if (plen < sizeof(*fc_event)) {
c90e5b
+			condlog(0, "too short (%d) to be an FC event", ret);
c90e5b
+			continue;
c90e5b
+		}
c90e5b
+		els_cmd = (uint32_t)fc_event->event_data;
c90e5b
+		condlog(4, "Got host no as %d, event 0x%x, len %d evntnum %d evntcode %d\n",
c90e5b
+				fc_event->host_no, els_cmd, fc_event->event_datalen,
c90e5b
+				fc_event->event_num, fc_event->event_code);
c90e5b
+		fpin_handle_els_frame(fc_event);
c90e5b
+	}
c90e5b
+out:
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	pthread_cleanup_pop(1);
c90e5b
+	return NULL;
c90e5b
+}
c90e5b
diff --git a/multipathd/main.c b/multipathd/main.c
c90e5b
index eeded52b..4cf5bc41 100644
c90e5b
--- a/multipathd/main.c
c90e5b
+++ b/multipathd/main.c
c90e5b
@@ -16,6 +16,7 @@
c90e5b
 #include <linux/oom.h>
c90e5b
 #include <libudev.h>
c90e5b
 #include <urcu.h>
c90e5b
+#include "fpin.h"
c90e5b
 #ifdef USE_SYSTEMD
c90e5b
 #include <systemd/sd-daemon.h>
c90e5b
 #endif
c90e5b
@@ -2704,7 +2705,9 @@ reconfigure (struct vectors * vecs)
c90e5b
 	conf->sequence_nr = old->sequence_nr + 1;
c90e5b
 	rcu_assign_pointer(multipath_conf, conf);
c90e5b
 	call_rcu(&old->rcu, rcu_free_config);
c90e5b
-
c90e5b
+#ifdef FPIN_EVENT_HANDLER
c90e5b
+	fpin_clean_marginal_dev_list(NULL);
c90e5b
+#endif
c90e5b
 	configure(vecs);
c90e5b
 
c90e5b
 
c90e5b
@@ -2878,7 +2881,8 @@ set_oom_adj (void)
c90e5b
 static int
c90e5b
 child (__attribute__((unused)) void *param)
c90e5b
 {
c90e5b
-	pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr;
c90e5b
+	pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr,
c90e5b
+		  fpin_thr, fpin_consumer_thr;
c90e5b
 	pthread_attr_t log_attr, misc_attr, uevent_attr;
c90e5b
 	struct vectors * vecs;
c90e5b
 	struct multipath * mpp;
c90e5b
@@ -2892,6 +2896,7 @@ child (__attribute__((unused)) void *param)
c90e5b
 	char *envp;
c90e5b
 	int queue_without_daemon;
c90e5b
 	enum daemon_status state;
c90e5b
+	int fpin_marginal_paths = 0;
c90e5b
 
c90e5b
 	mlockall(MCL_CURRENT | MCL_FUTURE);
c90e5b
 	signal_init();
c90e5b
@@ -2959,7 +2964,10 @@ child (__attribute__((unused)) void *param)
c90e5b
 
c90e5b
 	setscheduler();
c90e5b
 	set_oom_adj();
c90e5b
-
c90e5b
+#ifdef FPIN_EVENT_HANDLER
c90e5b
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
c90e5b
+		fpin_marginal_paths = 1;
c90e5b
+#endif
c90e5b
 	/*
c90e5b
 	 * Startup done, invalidate configuration
c90e5b
 	 */
c90e5b
@@ -3020,6 +3028,19 @@ child (__attribute__((unused)) void *param)
c90e5b
 		condlog(0, "failed to create uevent dispatcher: %d", rc);
c90e5b
 		goto failed;
c90e5b
 	}
c90e5b
+	if (fpin_marginal_paths) {
c90e5b
+		if ((rc = pthread_create(&fpin_thr, &misc_attr,
c90e5b
+			fpin_fabric_notification_receiver, NULL))) {
c90e5b
+			condlog(0, "failed to create the fpin receiver thread: %d", rc);
c90e5b
+			goto failed;
c90e5b
+		}
c90e5b
+
c90e5b
+		if ((rc = pthread_create(&fpin_consumer_thr,
c90e5b
+			&misc_attr, fpin_els_li_consumer, vecs))) {
c90e5b
+			condlog(0, "failed to create the fpin consumer thread thread: %d", rc);
c90e5b
+			goto failed;
c90e5b
+		}
c90e5b
+	}
c90e5b
 	pthread_attr_destroy(&misc_attr);
c90e5b
 
c90e5b
 	while (1) {
c90e5b
@@ -3070,6 +3091,10 @@ child (__attribute__((unused)) void *param)
c90e5b
 	pthread_cancel(uevq_thr);
c90e5b
 	if (poll_dmevents)
c90e5b
 		pthread_cancel(dmevent_thr);
c90e5b
+	if (fpin_marginal_paths) {
c90e5b
+		pthread_cancel(fpin_thr);
c90e5b
+		pthread_cancel(fpin_consumer_thr);
c90e5b
+	}
c90e5b
 
c90e5b
 	pthread_join(check_thr, NULL);
c90e5b
 	pthread_join(uevent_thr, NULL);
c90e5b
@@ -3077,6 +3102,10 @@ child (__attribute__((unused)) void *param)
c90e5b
 	pthread_join(uevq_thr, NULL);
c90e5b
 	if (poll_dmevents)
c90e5b
 		pthread_join(dmevent_thr, NULL);
c90e5b
+	if (fpin_marginal_paths) {
c90e5b
+		pthread_join(fpin_thr, NULL);
c90e5b
+		pthread_join(fpin_consumer_thr, NULL);
c90e5b
+	}
c90e5b
 
c90e5b
 	stop_io_err_stat_thread();
c90e5b