Blame SOURCES/rh799241-dcb-carrier-fixes.patch

94180d
From a9fe0d3a347c6b8af6d772a758c64ffd9580d9de Mon Sep 17 00:00:00 2001
94180d
From: Dan Winship <danw@gnome.org>
94180d
Date: Wed, 2 Apr 2014 09:24:21 -0400
94180d
Subject: [PATCH] dcb: fix -Wformat-security bugs
94180d
94180d
---
94180d
 src/nm-dcb.c | 4 ++--
94180d
 1 file changed, 2 insertions(+), 2 deletions(-)
94180d
94180d
diff --git a/src/nm-dcb.c b/src/nm-dcb.c
94180d
index 77d7b41..5e25cd8 100644
94180d
--- a/src/nm-dcb.c
94180d
+++ b/src/nm-dcb.c
94180d
@@ -204,15 +204,15 @@ _dcb_setup (const char *iface,
94180d
 		g_string_append (s, " up2tc:");
94180d
 		for (i = 0; i < 8; i++) {
94180d
 			id = nm_setting_dcb_get_priority_traffic_class (s_dcb, i);
94180d
 			g_assert (id < 8);
94180d
 			g_string_append_c (s, '0' + id);
94180d
 		}
94180d
 
94180d
-		success = do_helper (iface, DCBTOOL, run_func, user_data, error, s->str);
94180d
+		success = do_helper (iface, DCBTOOL, run_func, user_data, error, "%s", s->str);
94180d
 		g_string_free (s, TRUE);
94180d
 		if (!success)
94180d
 			return FALSE;
94180d
 	} else {
94180d
 		/* Ignore disable failure since lldpad <= 0.9.46 does not support disabling
94180d
 		 * priority groups without specifying an entire PG config.
94180d
 		 */
94180d
@@ -238,15 +238,15 @@ _dcb_cleanup (const char *iface,
94180d
 		NULL
94180d
 	};
94180d
 	const char **iter = cmds;
94180d
 	gboolean success = TRUE;
94180d
 
94180d
 	/* Turn everything off and return first error we get (if any) */
94180d
 	while (iter && *iter) {
94180d
-		if (!do_helper (iface, DCBTOOL, run_func, user_data, success ? error : NULL, *iter))
94180d
+		if (!do_helper (iface, DCBTOOL, run_func, user_data, success ? error : NULL, "%s", *iter))
94180d
 			success = FALSE;
94180d
 		iter++;
94180d
 	}
94180d
 
94180d
 	return success;
94180d
 }
94180d
 
94180d
-- 
94180d
1.9.0
94180d
94180d
From 18fd3e45d8db3428d4840d7cb9ea1980a22ef7de Mon Sep 17 00:00:00 2001
94180d
From: Dan Williams <dcbw@redhat.com>
94180d
Date: Thu, 27 Mar 2014 13:49:50 -0500
94180d
Subject: [PATCH] dcb: separate DCB enable/disable and wait for carrier changes
94180d
 (rh #799241) (rh #1081991)
94180d
94180d
Non-git-master versions of lldpad refuse to touch a device that doesn't
94180d
have a carrier.  And when enabling/disabling DCB, the kernel driver will
94180d
reconfigure itself and may turn carrier off for a few seconds.  So we
94180d
must ensure that before enabling/disabling DCB, the carrier is already
94180d
on.  Next we must ensure that *after* enabling/disabling DCB, the
94180d
carrier is back on before doing further DCB setup.
94180d
94180d
There's a race condition between enabling/disabling DCB and receiving
94180d
the carrier event in NetworkManager that has to be handled carefully.
94180d
Because the carrier may not yet be down after the dcbtool call to
94180d
enable/disable DCB returns, we need to wait for a couple seconds for
94180d
the carrier to go down, and then again for it to come back up.
94180d
Otherwise we might see the still-on carrier, proceed with DCB setup,
94180d
and the carrier finally goes down halfway through the setup, which
94180d
will fail the operations with "DCB not enabled, link down, or DCB
94180d
not supported" errors from lldpad.
94180d
---
94180d
 src/devices/nm-device-ethernet.c | 270 ++++++++++++++++++++++++++++++++++++---
94180d
 src/nm-dcb.c                     |  38 ++++--
94180d
 src/nm-dcb.h                     |   7 +
94180d
 src/tests/test-dcb.c             |  27 ++--
94180d
 4 files changed, 300 insertions(+), 42 deletions(-)
94180d
94180d
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
94180d
index a6279a4..e6f091d 100644
94180d
--- a/src/devices/nm-device-ethernet.c
94180d
+++ b/src/devices/nm-device-ethernet.c
94180d
@@ -82,14 +82,28 @@ typedef struct Supplicant {
94180d
 	guint iface_state_id;
94180d
 
94180d
 	/* Timeouts and idles */
94180d
 	guint iface_con_error_cb_id;
94180d
 	guint con_timeout_id;
94180d
 } Supplicant;
94180d
 
94180d
+typedef enum {
94180d
+	DCB_WAIT_UNKNOWN = 0,
94180d
+	/* Ensure carrier is up before enabling DCB */
94180d
+	DCB_WAIT_CARRIER_PREENABLE_UP,
94180d
+	/* Wait for carrier down when device starts enabling */
94180d
+	DCB_WAIT_CARRIER_PRECONFIG_DOWN,
94180d
+	/* Wait for carrier up when device has finished enabling */
94180d
+	DCB_WAIT_CARRIER_PRECONFIG_UP,
94180d
+	/* Wait carrier down when device starts configuring */
94180d
+	DCB_WAIT_CARRIER_POSTCONFIG_DOWN,
94180d
+	/* Wait carrier up when device has finished configuring */
94180d
+	DCB_WAIT_CARRIER_POSTCONFIG_UP,
94180d
+} DcbWait;
94180d
+
94180d
 typedef struct {
94180d
 	guint8              perm_hw_addr[ETH_ALEN];    /* Permanent MAC address */
94180d
 	guint8              initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
94180d
 
94180d
 	guint32             speed;
94180d
 
94180d
 	Supplicant          supplicant;
94180d
@@ -102,14 +116,19 @@ typedef struct {
94180d
 	char *              subchannels; /* Composite used for checking unmanaged specs */
94180d
 
94180d
 	/* PPPoE */
94180d
 	NMPPPManager *ppp_manager;
94180d
 	NMIP4Config  *pending_ip4_config;
94180d
 	gint32        last_pppoe_time;
94180d
 	guint         pppoe_wait_id;
94180d
+
94180d
+	/* DCB */
94180d
+	DcbWait       dcb_wait;
94180d
+	guint         dcb_timeout_id;
94180d
+	guint         dcb_carrier_id;
94180d
 } NMDeviceEthernetPrivate;
94180d
 
94180d
 enum {
94180d
 	PROP_0,
94180d
 	PROP_PERM_HW_ADDRESS,
94180d
 	PROP_SPEED,
94180d
 
94180d
@@ -1090,51 +1109,263 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reas
94180d
 
94180d
 		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
94180d
 	}
94180d
 
94180d
 	return ret;
94180d
 }
94180d
 
94180d
+/****************************************************************/
94180d
+
94180d
+static void
94180d
+dcb_timeout_cleanup (NMDevice *device)
94180d
+{
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
+
94180d
+	if (priv->dcb_timeout_id) {
94180d
+		g_source_remove (priv->dcb_timeout_id);
94180d
+		priv->dcb_timeout_id = 0;
94180d
+	}
94180d
+}
94180d
+
94180d
+static void
94180d
+dcb_carrier_cleanup (NMDevice *device)
94180d
+{
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
+
94180d
+	if (priv->dcb_carrier_id) {
94180d
+		g_signal_handler_disconnect (device, priv->dcb_carrier_id);
94180d
+		priv->dcb_carrier_id = 0;
94180d
+	}
94180d
+}
94180d
+
94180d
+static void dcb_state (NMDevice *device, gboolean timeout);
94180d
+
94180d
+static gboolean
94180d
+dcb_carrier_timeout (gpointer user_data)
94180d
+{
94180d
+	NMDevice *device = NM_DEVICE (user_data);
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
+
94180d
+	g_return_val_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG, G_SOURCE_REMOVE);
94180d
+
94180d
+	priv->dcb_timeout_id = 0;
94180d
+	if (priv->dcb_wait != DCB_WAIT_CARRIER_POSTCONFIG_DOWN) {
94180d
+		nm_log_warn (LOGD_DCB,
94180d
+		             "(%s): DCB: timed out waiting for carrier (step %d)",
94180d
+		             nm_device_get_iface (device),
94180d
+		             priv->dcb_wait);
94180d
+	}
94180d
+	dcb_state (device, TRUE);
94180d
+	return G_SOURCE_REMOVE;
94180d
+}
94180d
+
94180d
+static gboolean
94180d
+dcb_configure (NMDevice *device)
94180d
+{
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
+	NMSettingDcb *s_dcb;
94180d
+	const char *iface = nm_device_get_iface (device);
94180d
+	GError *error = NULL;
94180d
+
94180d
+	dcb_timeout_cleanup (device);
94180d
+
94180d
+	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
94180d
+	g_assert (s_dcb);
94180d
+	if (!nm_dcb_setup (iface, s_dcb, &error)) {
94180d
+		nm_log_warn (LOGD_DCB,
94180d
+		             "Activation (%s/wired) failed to enable DCB/FCoE: %s",
94180d
+		             iface, error->message);
94180d
+		g_clear_error (&error);
94180d
+		return FALSE;
94180d
+	}
94180d
+
94180d
+	/* Pause again just in case the device takes the carrier down when
94180d
+	 * setting specific DCB attributes.
94180d
+	 */
94180d
+	nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (postconfig down)", iface);
94180d
+	priv->dcb_wait = DCB_WAIT_CARRIER_POSTCONFIG_DOWN;
94180d
+	priv->dcb_timeout_id = g_timeout_add_seconds (3, dcb_carrier_timeout, device);
94180d
+	return TRUE;
94180d
+}
94180d
+
94180d
+static gboolean
94180d
+dcb_enable (NMDevice *device)
94180d
+{
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
+	const char *iface = nm_device_get_iface (device);
94180d
+	GError *error = NULL;
94180d
+
94180d
+	dcb_timeout_cleanup (device);
94180d
+	if (!nm_dcb_enable (iface, TRUE, &error)) {
94180d
+		nm_log_warn (LOGD_DCB,
94180d
+		             "Activation (%s/wired) failed to enable DCB/FCoE: %s",
94180d
+		             iface, error->message);
94180d
+		g_clear_error (&error);
94180d
+		return FALSE;
94180d
+	}
94180d
+
94180d
+	/* Pause for 3 seconds after enabling DCB to let the card reconfigure
94180d
+	 * itself.  Drivers will often re-initialize internal settings which
94180d
+	 * takes the carrier down for 2 or more seconds.  During this time,
94180d
+	 * lldpad will refuse to do anything else with the card since the carrier
94180d
+	 * is down.  But NM might get the carrier-down signal long after calling
94180d
+	 * "dcbtool dcb on", so we have to first wait for the carrier to go down.
94180d
+	 */
94180d
+	nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (preconfig down)", iface);
94180d
+	priv->dcb_wait = DCB_WAIT_CARRIER_PRECONFIG_DOWN;
94180d
+	priv->dcb_timeout_id = g_timeout_add_seconds (3, dcb_carrier_timeout, device);
94180d
+	return TRUE;
94180d
+}
94180d
+
94180d
+static void
94180d
+dcb_state (NMDevice *device, gboolean timeout)
94180d
+{
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
+	const char *iface = nm_device_get_iface (device);
94180d
+	gboolean carrier;
94180d
+
94180d
+	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
94180d
+
94180d
+
94180d
+	carrier = nm_platform_link_is_connected (nm_device_get_ifindex (device));
94180d
+	nm_log_dbg (LOGD_DCB, "(%s): dcb_state() wait %d carrier %d timeout %d", iface, priv->dcb_wait, carrier, timeout);
94180d
+
94180d
+	switch (priv->dcb_wait) {
94180d
+	case DCB_WAIT_CARRIER_PREENABLE_UP:
94180d
+		if (timeout || carrier) {
94180d
+			nm_log_dbg (LOGD_DCB, "(%s): dcb_state() enabling DCB", iface);
94180d
+			dcb_timeout_cleanup (device);
94180d
+			if (!dcb_enable (device)) {
94180d
+				dcb_carrier_cleanup (device);
94180d
+				nm_device_state_changed (device,
94180d
+				                         NM_ACT_STAGE_RETURN_FAILURE,
94180d
+				                         NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED);
94180d
+			}
94180d
+		}
94180d
+		break;
94180d
+	case DCB_WAIT_CARRIER_PRECONFIG_DOWN:
94180d
+		dcb_timeout_cleanup (device);
94180d
+		priv->dcb_wait = DCB_WAIT_CARRIER_PRECONFIG_UP;
94180d
+
94180d
+		if (!carrier) {
94180d
+			/* Wait for the carrier to come back up */
94180d
+			nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (preconfig up)", iface);
94180d
+			priv->dcb_timeout_id = g_timeout_add_seconds (5, dcb_carrier_timeout, device);
94180d
+			break;
94180d
+		}
94180d
+		nm_log_dbg (LOGD_DCB, "(%s): dcb_state() preconfig down falling through", iface);
94180d
+		/* carrier never went down? fall through */
94180d
+	case DCB_WAIT_CARRIER_PRECONFIG_UP:
94180d
+		if (timeout || carrier) {
94180d
+			nm_log_dbg (LOGD_DCB, "(%s): dcb_state() preconfig up configuring DCB", iface);
94180d
+			dcb_timeout_cleanup (device);
94180d
+			if (!dcb_configure (device)) {
94180d
+				dcb_carrier_cleanup (device);
94180d
+				nm_device_state_changed (device,
94180d
+				                         NM_ACT_STAGE_RETURN_FAILURE,
94180d
+				                         NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED);
94180d
+			}
94180d
+		}
94180d
+		break;
94180d
+	case DCB_WAIT_CARRIER_POSTCONFIG_DOWN:
94180d
+		dcb_timeout_cleanup (device);
94180d
+		priv->dcb_wait = DCB_WAIT_CARRIER_POSTCONFIG_UP;
94180d
+
94180d
+		if (!carrier) {
94180d
+			/* Wait for the carrier to come back up */
94180d
+			nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (postconfig up)", iface);
94180d
+			priv->dcb_timeout_id = g_timeout_add_seconds (5, dcb_carrier_timeout, device);
94180d
+			break;
94180d
+		}
94180d
+		nm_log_dbg (LOGD_DCB, "(%s): dcb_state() postconfig down falling through", iface);
94180d
+		/* carrier never went down? fall through */
94180d
+	case DCB_WAIT_CARRIER_POSTCONFIG_UP:
94180d
+		if (timeout || carrier) {
94180d
+			nm_log_dbg (LOGD_DCB, "(%s): dcb_state() postconfig up starting IP", iface);
94180d
+			dcb_timeout_cleanup (device);
94180d
+			dcb_carrier_cleanup (device);
94180d
+			priv->dcb_wait = DCB_WAIT_UNKNOWN;
94180d
+			nm_device_activate_schedule_stage3_ip_config_start (device);
94180d
+		}
94180d
+		break;
94180d
+	default:
94180d
+		g_assert_not_reached ();
94180d
+	}
94180d
+}
94180d
+
94180d
+static void
94180d
+dcb_carrier_changed (NMDevice *device, GParamSpec *pspec, gpointer unused)
94180d
+{
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
+
94180d
+	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
94180d
+
94180d
+	if (priv->dcb_timeout_id) {
94180d
+		nm_log_dbg (LOGD_DCB, "(%s): carrier_changed() calling dcb_state()", nm_device_get_iface (device));
94180d
+		dcb_state (device, FALSE);
94180d
+	}
94180d
+}
94180d
+
94180d
+/****************************************************************/
94180d
+
94180d
 static NMActStageReturn
94180d
 act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
94180d
 {
94180d
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
94180d
 	NMSettingConnection *s_con;
94180d
 	const char *connection_type;
94180d
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
94180d
 	NMSettingDcb *s_dcb;
94180d
-	GError *error = NULL;
94180d
 
94180d
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
94180d
 
94180d
-	/* DCB and FCoE setup */
94180d
-	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
94180d
-	if (s_dcb) {
94180d
-		if (!nm_dcb_setup (nm_device_get_iface (device), s_dcb, &error)) {
94180d
-			nm_log_warn (LOGD_DEVICE | LOGD_HW,
94180d
-			             "Activation (%s/wired) failed to enable DCB/FCoE: %s",
94180d
-			             nm_device_get_iface (device), error->message);
94180d
-			g_clear_error (&error);
94180d
-			*reason = NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED;
94180d
-			return NM_ACT_STAGE_RETURN_FAILURE;
94180d
-		}
94180d
-	}
94180d
-
94180d
 	s_con = NM_SETTING_CONNECTION (device_get_setting (device, NM_TYPE_SETTING_CONNECTION));
94180d
 	g_assert (s_con);
94180d
 
94180d
+	dcb_timeout_cleanup (device);
94180d
+	dcb_carrier_cleanup (device);
94180d
+
94180d
 	/* 802.1x has to run before any IP configuration since the 802.1x auth
94180d
 	 * process opens the port up for normal traffic.
94180d
 	 */
94180d
 	connection_type = nm_setting_connection_get_connection_type (s_con);
94180d
 	if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) {
94180d
 		NMSetting8021x *security;
94180d
 
94180d
 		security = (NMSetting8021x *) device_get_setting (device, NM_TYPE_SETTING_802_1X);
94180d
-		if (security)
94180d
-			ret = nm_8021x_stage2_config (NM_DEVICE_ETHERNET (device), reason);
94180d
+		if (security) {
94180d
+			/* FIXME: for now 802.1x is mutually exclusive with DCB */
94180d
+			return nm_8021x_stage2_config (NM_DEVICE_ETHERNET (device), reason);
94180d
+		}
94180d
+	}
94180d
+
94180d
+	/* DCB and FCoE setup */
94180d
+	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
94180d
+	if (s_dcb) {
94180d
+		/* lldpad really really wants the carrier to be up */
94180d
+		if (nm_platform_link_is_connected (nm_device_get_ifindex (device))) {
94180d
+			if (!dcb_enable (device)) {
94180d
+				*reason = NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED;
94180d
+				return NM_ACT_STAGE_RETURN_FAILURE;
94180d
+			}
94180d
+		} else {
94180d
+			nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (preenable up)",
94180d
+			            nm_device_get_iface (device));
94180d
+			priv->dcb_wait = DCB_WAIT_CARRIER_PREENABLE_UP;
94180d
+			priv->dcb_timeout_id = g_timeout_add_seconds (4, dcb_carrier_timeout, device);
94180d
+		}
94180d
+
94180d
+		/* Watch carrier independently of NMDeviceClass::carrier_changed so
94180d
+		 * we get instant notifications of disconnection that aren't deferred.
94180d
+		 */
94180d
+		priv->dcb_carrier_id = g_signal_connect (device,
94180d
+		                                         "notify::" NM_DEVICE_CARRIER,
94180d
+		                                         G_CALLBACK (dcb_carrier_changed),
94180d
+		                                         NULL);
94180d
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
94180d
 	}
94180d
 
94180d
 	return ret;
94180d
 }
94180d
 
94180d
 static NMActStageReturn
94180d
 act_stage3_ip4_config_start (NMDevice *device,
94180d
@@ -1202,14 +1433,18 @@ deactivate (NMDevice *device)
94180d
 	if (priv->ppp_manager) {
94180d
 		g_object_unref (priv->ppp_manager);
94180d
 		priv->ppp_manager = NULL;
94180d
 	}
94180d
 
94180d
 	supplicant_interface_release (self);
94180d
 
94180d
+	priv->dcb_wait = DCB_WAIT_UNKNOWN;
94180d
+	dcb_timeout_cleanup (device);
94180d
+	dcb_carrier_cleanup (device);
94180d
+
94180d
 	/* Tear down DCB/FCoE if it was enabled */
94180d
 	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
94180d
 	if (s_dcb) {
94180d
 		if (!nm_dcb_cleanup (nm_device_get_iface (device), &error)) {
94180d
 			nm_log_warn (LOGD_DEVICE | LOGD_HW,
94180d
 			             "(%s) failed to disable DCB/FCoE: %s",
94180d
 			             nm_device_get_iface (device), error->message);
94180d
@@ -1405,14 +1640,17 @@ dispose (GObject *object)
94180d
 	g_free (priv->subchannels);
94180d
 
94180d
 	if (priv->pppoe_wait_id) {
94180d
 		g_source_remove (priv->pppoe_wait_id);
94180d
 		priv->pppoe_wait_id = 0;
94180d
 	}
94180d
 
94180d
+	dcb_timeout_cleanup (NM_DEVICE (self));
94180d
+	dcb_carrier_cleanup (NM_DEVICE (self));
94180d
+
94180d
 	G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
94180d
 }
94180d
 
94180d
 static void
94180d
 get_property (GObject *object, guint prop_id,
94180d
               GValue *value, GParamSpec *pspec)
94180d
 {
94180d
diff --git a/src/nm-dcb.c b/src/nm-dcb.c
94180d
index 5e25cd8..428c61e 100644
94180d
--- a/src/nm-dcb.c
94180d
+++ b/src/nm-dcb.c
94180d
@@ -88,14 +88,27 @@ out:
94180d
 		g_strfreev (split);
94180d
 	g_free (argv);
94180d
 	g_free (cmdline);
94180d
 	g_free (errmsg);
94180d
 	return success;
94180d
 }
94180d
 
94180d
+gboolean
94180d
+_dcb_enable (const char *iface,
94180d
+             gboolean enable,
94180d
+             DcbFunc run_func,
94180d
+             gpointer user_data,
94180d
+             GError **error)
94180d
+{
94180d
+	if (enable)
94180d
+		return do_helper (iface, DCBTOOL, run_func, user_data, error, "dcb on");
94180d
+	else
94180d
+		return do_helper (iface, DCBTOOL, run_func, user_data, error, "dcb off");
94180d
+}
94180d
+
94180d
 #define SET_FLAGS(f, tag) \
94180d
 G_STMT_START { \
94180d
 	if (!do_helper (iface, DCBTOOL, run_func, user_data, error, tag " e:%c a:%c w:%c", \
94180d
 	                 f & NM_SETTING_DCB_FLAG_ENABLE ? '1' : '0', \
94180d
 	                 f & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0', \
94180d
 	                 f & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0')) \
94180d
 		return FALSE; \
94180d
@@ -120,17 +133,14 @@ _dcb_setup (const char *iface,
94180d
             GError **error)
94180d
 {
94180d
 	NMSettingDcbFlags flags;
94180d
 	guint i;
94180d
 
94180d
 	g_assert (s_dcb);
94180d
 
94180d
-	if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "dcb on"))
94180d
-		return FALSE;
94180d
-
94180d
 	/* FCoE */
94180d
 	flags = nm_setting_dcb_get_app_fcoe_flags (s_dcb);
94180d
 	SET_APP (flags, s_dcb, fcoe);
94180d
 
94180d
 	/* iSCSI */
94180d
 	flags = nm_setting_dcb_get_app_iscsi_flags (s_dcb);
94180d
 	SET_APP (flags, s_dcb, iscsi);
94180d
@@ -225,15 +235,14 @@ _dcb_setup (const char *iface,
94180d
 gboolean
94180d
 _dcb_cleanup (const char *iface,
94180d
               DcbFunc run_func,
94180d
               gpointer user_data,
94180d
               GError **error)
94180d
 {
94180d
 	const char *cmds[] = {
94180d
-		"dcb off",
94180d
 		"app:fcoe e:0",
94180d
 		"app:iscsi e:0",
94180d
 		"app:fip e:0",
94180d
 		"pfc e:0",
94180d
 		"pg e:0",
94180d
 		NULL
94180d
 	};
94180d
@@ -243,14 +252,17 @@ _dcb_cleanup (const char *iface,
94180d
 	/* Turn everything off and return first error we get (if any) */
94180d
 	while (iter && *iter) {
94180d
 		if (!do_helper (iface, DCBTOOL, run_func, user_data, success ? error : NULL, "%s", *iter))
94180d
 			success = FALSE;
94180d
 		iter++;
94180d
 	}
94180d
 
94180d
+	if (!_dcb_enable (iface, FALSE, run_func, user_data, success ? error : NULL))
94180d
+		success = FALSE;
94180d
+
94180d
 	return success;
94180d
 }
94180d
 
94180d
 gboolean
94180d
 _fcoe_setup (const char *iface,
94180d
              NMSettingDcb *s_dcb,
94180d
              DcbFunc run_func,
94180d
@@ -351,32 +363,32 @@ run_helper (char **argv, guint which, gpointer user_data, GError **error)
94180d
 	g_free (errmsg);
94180d
 
94180d
 	g_free (cmdline);
94180d
 	return success;
94180d
 }
94180d
 
94180d
 gboolean
94180d
+nm_dcb_enable (const char *iface, gboolean enable, GError **error)
94180d
+{
94180d
+	return _dcb_enable (iface, enable, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
94180d
+}
94180d
+
94180d
+gboolean
94180d
 nm_dcb_setup (const char *iface, NMSettingDcb *s_dcb, GError **error)
94180d
 {
94180d
 	gboolean success;
94180d
 
94180d
 	success = _dcb_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
94180d
 	if (success)
94180d
 		success = _fcoe_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (FCOEADM), error);
94180d
 
94180d
 	return success;
94180d
 }
94180d
 
94180d
 gboolean
94180d
 nm_dcb_cleanup (const char *iface, GError **error)
94180d
 {
94180d
-	gboolean success;
94180d
-
94180d
-	success = _dcb_cleanup (iface, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
94180d
-	if (success) {
94180d
-		/* Only report FCoE errors if DCB cleanup was successful */
94180d
-		success = _fcoe_cleanup (iface, run_helper, GUINT_TO_POINTER (FCOEADM), success ? error : NULL);
94180d
-	}
94180d
-
94180d
-	return success;
94180d
+	/* Ignore FCoE cleanup errors */
94180d
+	_fcoe_cleanup (iface, run_helper, GUINT_TO_POINTER (FCOEADM), NULL);
94180d
+	return _dcb_cleanup (iface, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
94180d
 }
94180d
 
94180d
diff --git a/src/nm-dcb.h b/src/nm-dcb.h
94180d
index 3dbd5e6..bfe5ced 100644
94180d
--- a/src/nm-dcb.h
94180d
+++ b/src/nm-dcb.h
94180d
@@ -44,14 +44,15 @@ typedef enum {
94180d
 
94180d
 #define NM_DCB_ERROR (nm_dcb_error_quark ())
94180d
 GQuark nm_dcb_error_quark (void);
94180d
 #define NM_TYPE_DCB_ERROR (nm_dcb_error_get_type ())
94180d
 GType  nm_dcb_error_get_type (void);
94180d
 
94180d
 
94180d
+gboolean nm_dcb_enable (const char *iface, gboolean enable, GError **error);
94180d
 gboolean nm_dcb_setup (const char *iface, NMSettingDcb *s_dcb, GError **error);
94180d
 gboolean nm_dcb_cleanup (const char *iface, GError **error);
94180d
 
94180d
 /* For testcases only! */
94180d
 typedef gboolean (*DcbFunc) (char **argv,
94180d
                              guint which,
94180d
                              gpointer user_data,
94180d
@@ -64,14 +65,20 @@ gboolean do_helper (const char *iface,
94180d
                     guint which,
94180d
                     DcbFunc run_func,
94180d
                     gpointer user_data,
94180d
                     GError **error,
94180d
                     const char *fmt,
94180d
                     ...) G_GNUC_PRINTF(6, 7);
94180d
 
94180d
+gboolean _dcb_enable (const char *iface,
94180d
+                      gboolean enable,
94180d
+                      DcbFunc run_func,
94180d
+                      gpointer user_data,
94180d
+                      GError **error);
94180d
+
94180d
 gboolean _dcb_setup (const char *iface,
94180d
                      NMSettingDcb *s_dcb,
94180d
                      DcbFunc run_func,
94180d
                      gpointer user_data,
94180d
                      GError **error);
94180d
 
94180d
 gboolean _dcb_cleanup (const char *iface,
94180d
diff --git a/src/tests/test-dcb.c b/src/tests/test-dcb.c
94180d
index 74dcca5..aead8f1 100644
94180d
--- a/src/tests/test-dcb.c
94180d
+++ b/src/tests/test-dcb.c
94180d
@@ -50,16 +50,15 @@ test_dcb_func (char **argv, guint which, gpointer user_data, GError **error)
94180d
                        NM_SETTING_DCB_FLAG_ADVERTISE | \
94180d
                        NM_SETTING_DCB_FLAG_WILLING)
94180d
 
94180d
 static void
94180d
 test_dcb_fcoe (void)
94180d
 {
94180d
 	static DcbExpected expected = { 0,
94180d
-		{ "dcbtool sc eth0 dcb on",
94180d
-		  "dcbtool sc eth0 app:fcoe e:1 a:1 w:1",
94180d
+		{ "dcbtool sc eth0 app:fcoe e:1 a:1 w:1",
94180d
 		  "dcbtool sc eth0 app:fcoe appcfg:40",
94180d
 		  "dcbtool sc eth0 app:iscsi e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:fip e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pfc e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pg e:0",
94180d
 		  NULL },
94180d
 	};
94180d
@@ -81,16 +80,15 @@ test_dcb_fcoe (void)
94180d
 	g_object_unref (s_dcb);
94180d
 }
94180d
 
94180d
 static void
94180d
 test_dcb_iscsi (void)
94180d
 {
94180d
 	static DcbExpected expected = { 0,
94180d
-		{ "dcbtool sc eth0 dcb on",
94180d
-		  "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
+		{ "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:iscsi e:1 a:0 w:1",
94180d
 		  "dcbtool sc eth0 app:iscsi appcfg:08",
94180d
 		  "dcbtool sc eth0 app:fip e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pfc e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pg e:0",
94180d
 		  NULL },
94180d
 	};
94180d
@@ -112,16 +110,15 @@ test_dcb_iscsi (void)
94180d
 	g_object_unref (s_dcb);
94180d
 }
94180d
 
94180d
 static void
94180d
 test_dcb_fip (void)
94180d
 {
94180d
 	static DcbExpected expected = { 0,
94180d
-		{ "dcbtool sc eth0 dcb on",
94180d
-		  "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
+		{ "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:iscsi e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:fip e:1 a:1 w:0",
94180d
 		  "dcbtool sc eth0 app:fip appcfg:01",
94180d
 		  "dcbtool sc eth0 pfc e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pg e:0",
94180d
 		  NULL },
94180d
 	};
94180d
@@ -143,16 +140,15 @@ test_dcb_fip (void)
94180d
 	g_object_unref (s_dcb);
94180d
 }
94180d
 
94180d
 static void
94180d
 test_dcb_fip_default_prio (void)
94180d
 {
94180d
 	static DcbExpected expected = { 0,
94180d
-		{ "dcbtool sc eth0 dcb on",
94180d
-		  "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
+		{ "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:iscsi e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:fip e:1 a:1 w:0",
94180d
 		  "dcbtool sc eth0 pfc e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pg e:0",
94180d
 		  NULL },
94180d
 	};
94180d
 	NMSettingDcb *s_dcb;
94180d
@@ -173,16 +169,15 @@ test_dcb_fip_default_prio (void)
94180d
 	g_object_unref (s_dcb);
94180d
 }
94180d
 
94180d
 static void
94180d
 test_dcb_pfc (void)
94180d
 {
94180d
 	static DcbExpected expected = { 0,
94180d
-		{ "dcbtool sc eth0 dcb on",
94180d
-		  "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
+		{ "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:iscsi e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:fip e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pfc e:1 a:1 w:1",
94180d
 		  "dcbtool sc eth0 pfc pfcup:01101100",
94180d
 		  "dcbtool sc eth0 pg e:0",
94180d
 		  NULL },
94180d
 	};
94180d
@@ -212,16 +207,15 @@ test_dcb_pfc (void)
94180d
 	g_object_unref (s_dcb);
94180d
 }
94180d
 
94180d
 static void
94180d
 test_dcb_priority_groups (void)
94180d
 {
94180d
 	static DcbExpected expected = { 0,
94180d
-		{ "dcbtool sc eth0 dcb on",
94180d
-		  "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
+		{ "dcbtool sc eth0 app:fcoe e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:iscsi e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 app:fip e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pfc e:0 a:0 w:0",
94180d
 		  "dcbtool sc eth0 pg e:1 a:1 w:1" \
94180d
 		      " pgid:765f3210" \
94180d
 		      " pgpct:10,40,5,10,5,20,7,3" \
94180d
 		      " uppct:100,50,33,25,20,16,14,12" \
94180d
@@ -264,28 +258,35 @@ test_dcb_priority_groups (void)
94180d
 	g_object_unref (s_dcb);
94180d
 }
94180d
 
94180d
 static void
94180d
 test_dcb_cleanup (void)
94180d
 {
94180d
 	static DcbExpected expected = { 0,
94180d
-		{ "dcbtool sc eth0 dcb off",
94180d
+		{ "fcoeadm -d eth0",
94180d
 		  "dcbtool sc eth0 app:fcoe e:0",
94180d
 		  "dcbtool sc eth0 app:iscsi e:0",
94180d
 		  "dcbtool sc eth0 app:fip e:0",
94180d
 		  "dcbtool sc eth0 pfc e:0",
94180d
 		  "dcbtool sc eth0 pg e:0",
94180d
+		  "dcbtool sc eth0 dcb off",
94180d
 		  NULL },
94180d
 	};
94180d
 	GError *error = NULL;
94180d
 	gboolean success;
94180d
 
94180d
+	success = _fcoe_cleanup ("eth0", test_dcb_func, &expected, &error);
94180d
+	g_assert_no_error (error);
94180d
+	g_assert (success);
94180d
+
94180d
 	success = _dcb_cleanup ("eth0", test_dcb_func, &expected, &error);
94180d
 	g_assert_no_error (error);
94180d
 	g_assert (success);
94180d
+
94180d
+	g_assert_cmpstr (expected.cmds[expected.num], ==, NULL);
94180d
 }
94180d
 
94180d
 static void
94180d
 test_fcoe_create (void)
94180d
 {
94180d
 	static DcbExpected expected1 = { 0,
94180d
 		{ "fcoeadm -m fabric -c eth0", NULL },
94180d
-- 
94180d
1.9.0
94180d
94180d
From 55704170caed97eb26b3853af04cbbf332845966 Mon Sep 17 00:00:00 2001
94180d
From: Dan Williams <dcbw@redhat.com>
94180d
Date: Mon, 31 Mar 2014 15:06:22 -0500
94180d
Subject: [PATCH] dcb: wait for carrier down/up after disabling FCoE
94180d
94180d
---
94180d
 src/nm-dcb.c | 32 ++++++++++++++++++++++++++++++++
94180d
 1 file changed, 32 insertions(+)
94180d
94180d
diff --git a/src/nm-dcb.c b/src/nm-dcb.c
94180d
index 428c61e..3bdbf7d 100644
94180d
--- a/src/nm-dcb.c
94180d
+++ b/src/nm-dcb.c
94180d
@@ -380,15 +380,47 @@ nm_dcb_setup (const char *iface, NMSettingDcb *s_dcb, GError **error)
94180d
 	success = _dcb_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
94180d
 	if (success)
94180d
 		success = _fcoe_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (FCOEADM), error);
94180d
 
94180d
 	return success;
94180d
 }
94180d
 
94180d
+static void
94180d
+carrier_wait (const char *iface, guint secs, gboolean up)
94180d
+{
94180d
+	int ifindex, count = secs * 10;
94180d
+
94180d
+	g_return_if_fail (iface != NULL);
94180d
+
94180d
+	ifindex = nm_platform_link_get_ifindex (iface);
94180d
+	if (ifindex > 0) {
94180d
+		/* To work around driver quirks and lldpad handling of carrier status,
94180d
+		 * we must wait a short period of time to see if the carrier goes
94180d
+		 * down, and then wait for the carrier to come back up again.  Otherwise
94180d
+		 * subsequent lldpad calls may fail with "Device not found, link down
94180d
+		 * or DCB not enabled" errors.
94180d
+		 */
94180d
+		nm_log_dbg (LOGD_DCB, "(%s): cleanup waiting for carrier %s",
94180d
+		            iface, up ? "up" : "down");
94180d
+		g_usleep (G_USEC_PER_SEC / 4);
94180d
+		while (nm_platform_link_is_connected (ifindex) != up && count-- > 0) {
94180d
+			g_usleep (G_USEC_PER_SEC / 10);
94180d
+			nm_platform_link_refresh (ifindex);
94180d
+		}
94180d
+	}
94180d
+}
94180d
+
94180d
 gboolean
94180d
 nm_dcb_cleanup (const char *iface, GError **error)
94180d
 {
94180d
 	/* Ignore FCoE cleanup errors */
94180d
 	_fcoe_cleanup (iface, run_helper, GUINT_TO_POINTER (FCOEADM), NULL);
94180d
+
94180d
+	/* Must pause a bit to wait for carrier-up since disabling FCoE may
94180d
+	 * cause the device to take the link down, making lldpad return errors.
94180d
+	 */
94180d
+	carrier_wait (iface, 2, FALSE);
94180d
+	carrier_wait (iface, 4, TRUE);
94180d
+
94180d
 	return _dcb_cleanup (iface, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
94180d
 }
94180d
 
94180d
-- 
94180d
1.9.0
94180d