summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Shan <gwshan@linux.vnet.ibm.com>2016-10-21 03:47:45 +0300
committerJoel Stanley <joel@jms.id.au>2016-10-21 04:38:12 +0300
commit572e07fe62177e183e9ca68987adb372fe229ba2 (patch)
treed8e90eefa9245bf2981004b58639e829928dc6c9
parent5cae1c00c02700f3785762cc550d9eccd676d6fb (diff)
downloadlinux-572e07fe62177e183e9ca68987adb372fe229ba2.tar.xz
net/ncsi: Choose hot channel as active one if necessary
The issue was found on BCM5718 which has two NCSI channels in one package: C0 and C1. C0 is in link-up state while C1 is in link-down state. C0 is chosen as active channel until unplugging and plugging C0's cable: On unplugging C0's cable, LSC (Link State Change) AEN packet received on C0 to report link-down event. After that, C1 is chosen as active channel. LSC AEN for link-up event is lost on C0 when plugging C0's cable back. We lose the network even C0 is usable. This resolves the issue by recording the (hot) channel that was ever chosen as active one. The hot channel is chosen to be active one if none of available channels in link-up state. With this, C0 is still the active one after unplugging C0's cable. LSC AEN packet received on C0 when plugging its cable back. Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Signed-off-by: Joel Stanley <joel@jms.id.au>
-rw-r--r--net/ncsi/internal.h1
-rw-r--r--net/ncsi/ncsi-manage.c19
2 files changed, 18 insertions, 2 deletions
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 1696b1b3c957..9476652f98e2 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -88,6 +88,7 @@ struct ncsi_dev_priv {
atomic_t ndp_package_num;
spinlock_t ndp_package_lock;
struct list_head ndp_packages;
+ struct ncsi_channel *ndp_hot_channel;
atomic_t ndp_pending_reqs;
atomic_t ndp_last_req_idx;
#define NCSI_REQ_START_IDX 1
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index a3bae7bceb65..be767a81ccc5 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -382,6 +382,7 @@ static void ncsi_dev_config(struct ncsi_dev_priv *ndp)
struct net_device *dev = nd->nd_dev;
struct ncsi_package *np = ndp->ndp_active_package;
struct ncsi_channel *nc = ndp->ndp_active_channel;
+ struct ncsi_channel *hot_nc = NULL;
struct ncsi_cmd_arg nca;
unsigned char index;
int ret;
@@ -472,8 +473,15 @@ static void ncsi_dev_config(struct ncsi_dev_priv *ndp)
case ncsi_dev_state_config_done:
nd->nd_state = ncsi_dev_state_functional;
nd->nd_link_up = 0;
- if (nc->nc_modes[NCSI_MODE_LINK].ncm_data[2] & 0x1)
+ if (nc->nc_modes[NCSI_MODE_LINK].ncm_data[2] & 0x1) {
nd->nd_link_up = 1;
+ hot_nc = nc;
+ } else {
+ hot_nc = NULL;
+ }
+
+ /* Update the hot channel */
+ ndp->ndp_hot_channel = hot_nc;
nd->nd_handler(nd);
ndp->ndp_flags &= ~NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE;
@@ -497,11 +505,13 @@ error:
static void ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
{
struct ncsi_package *np;
- struct ncsi_channel *nc;
+ struct ncsi_channel *nc, *hot_nc;
struct ncsi_channel_mode *ncm;
ndp->ndp_active_package = NULL;
ndp->ndp_active_channel = NULL;
+
+ hot_nc = ndp->ndp_hot_channel;
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
if (!ndp->ndp_active_channel) {
@@ -509,6 +519,11 @@ static void ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
ndp->ndp_active_channel = nc;
}
+ if (nc == hot_nc) {
+ ndp->ndp_active_package = np;
+ ndp->ndp_active_channel = nc;
+ }
+
ncm = &nc->nc_modes[NCSI_MODE_LINK];
if (ncm->ncm_data[2] & 0x1) {
ndp->ndp_active_package = np;