Commit 4061f3a4 authored by Michael J. Ruhl's avatar Michael J. Ruhl Committed by Doug Ledford

IB/hfi1: Race condition between user notification and driver state

The handler for link init state (HLS_UP_INIT) notifies userspace
(update_statusp()) before enabling the device
(RCV_CTRL_RCV_PORT_ENABLE_SMASK) or setting the device state
(ppd->host_link_state).  This causes a race condition where the
userspace thinks the interface is in the INIT state before the driver
has set that state.

Rework the code path to eliminate the race.

Delay setting the init state until after a HW settling period.
Reviewed-by: default avatarSebastian Sanchez <sebastian.sanchez@intel.com>
Signed-off-by: default avatarMichael J. Ruhl <michael.j.ruhl@intel.com>
Signed-off-by: default avatarDennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 5455e73a
......@@ -10316,9 +10316,6 @@ static void force_logical_link_state_down(struct hfi1_pportdata *ppd)
write_csr(dd, DC_LCB_CFG_ALLOW_LINK_UP, 0);
write_csr(dd, DC_LCB_CFG_IGNORE_LOST_RCLK, 0);
/* adjust ppd->statusp, if needed */
update_statusp(ppd, IB_PORT_DOWN);
dd_dev_info(ppd->dd, "logical state forced to LINK_DOWN\n");
}
......@@ -10400,6 +10397,7 @@ static int goto_offline(struct hfi1_pportdata *ppd, u8 rem_reason)
force_logical_link_state_down(ppd);
ppd->host_link_state = HLS_LINK_COOLDOWN; /* LCB access allowed */
update_statusp(ppd, IB_PORT_DOWN);
/*
* The LNI has a mandatory wait time after the physical state
......@@ -10661,6 +10659,7 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state)
handle_linkup_change(dd, 1);
ppd->host_link_state = HLS_UP_INIT;
update_statusp(ppd, IB_PORT_INIT);
break;
case HLS_UP_ARMED:
if (ppd->host_link_state != HLS_UP_INIT)
......@@ -10682,6 +10681,7 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state)
break;
}
ppd->host_link_state = HLS_UP_ARMED;
update_statusp(ppd, IB_PORT_ARMED);
/*
* The simulator does not currently implement SMA messages,
* so neighbor_normal is not set. Set it here when we first
......@@ -10704,6 +10704,7 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state)
/* tell all engines to go running */
sdma_all_running(dd);
ppd->host_link_state = HLS_UP_ACTIVE;
update_statusp(ppd, IB_PORT_ACTIVE);
/* Signal the IB layer that the port has went active */
event.device = &dd->verbs_dev.rdi.ibdev;
......@@ -12717,6 +12718,17 @@ const char *opa_pstate_name(u32 pstate)
return "unknown";
}
/**
* update_statusp - Update userspace status flag
* @ppd: Port data structure
* @state: port state information
*
* Actual port status is determined by the host_link_state value
* in the ppd.
*
* host_link_state MUST be updated before updating the user space
* statusp.
*/
static void update_statusp(struct hfi1_pportdata *ppd, u32 state)
{
/*
......@@ -12742,9 +12754,11 @@ static void update_statusp(struct hfi1_pportdata *ppd, u32 state)
break;
}
}
dd_dev_info(ppd->dd, "logical state changed to %s (0x%x)\n",
opa_lstate_name(state), state);
}
/*
/**
* wait_logical_linkstate - wait for an IB link state change to occur
* @ppd: port device
* @state: the state to wait for
......@@ -12775,11 +12789,6 @@ static int wait_logical_linkstate(struct hfi1_pportdata *ppd, u32 state,
msleep(20);
}
update_statusp(ppd, state);
dd_dev_info(ppd->dd,
"logical state changed to %s (0x%x)\n",
opa_lstate_name(state),
state);
return 0;
}
......
......@@ -53,6 +53,8 @@
#include "common.h"
#include "sdma.h"
#define LINK_UP_DELAY 500 /* in microseconds */
/**
* format_hwmsg - format a single hwerror message
* @msg message buffer
......@@ -102,9 +104,16 @@ static void signal_ib_event(struct hfi1_pportdata *ppd, enum ib_event_type ev)
ib_dispatch_event(&event);
}
/*
/**
* handle_linkup_change - finish linkup/down state changes
* @dd: valid device
* @linkup: link state information
*
* Handle a linkup or link down notification.
* The HW needs time to finish its link up state change. Give it that chance.
*
* This is called outside an interrupt.
*
*/
void handle_linkup_change(struct hfi1_devdata *dd, u32 linkup)
{
......@@ -151,6 +160,9 @@ void handle_linkup_change(struct hfi1_devdata *dd, u32 linkup)
ppd->neighbor_guid, ppd->neighbor_type,
ppd->neighbor_port_number);
/* HW needs LINK_UP_DELAY to settle, give it that chance */
udelay(LINK_UP_DELAY);
/* physical link went up */
ppd->linkup = 1;
ppd->offline_disabled_reason =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment