Skip to content

Commit

Permalink
net/ncsi: Choose hot channel as active one if necessary
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Gavin Shan authored and davem330 committed Oct 20, 2016
1 parent 008a424 commit bbc7c01
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 3 deletions.
1 change: 1 addition & 0 deletions net/ncsi/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ struct ncsi_dev_priv {
#endif
unsigned int package_num; /* Number of packages */
struct list_head packages; /* List of packages */
struct ncsi_channel *hot_channel; /* Channel was ever active */
struct ncsi_request requests[256]; /* Request table */
unsigned int request_id; /* Last used request ID */
#define NCSI_REQ_START_IDX 1
Expand Down
22 changes: 19 additions & 3 deletions net/ncsi/ncsi-manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
struct net_device *dev = nd->dev;
struct ncsi_package *np = ndp->active_package;
struct ncsi_channel *nc = ndp->active_channel;
struct ncsi_channel *hot_nc = NULL;
struct ncsi_cmd_arg nca;
unsigned char index;
unsigned long flags;
Expand Down Expand Up @@ -750,12 +751,20 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
break;
case ncsi_dev_state_config_done:
spin_lock_irqsave(&nc->lock, flags);
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1)
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
hot_nc = nc;
nc->state = NCSI_CHANNEL_ACTIVE;
else
} else {
hot_nc = NULL;
nc->state = NCSI_CHANNEL_INACTIVE;
}
spin_unlock_irqrestore(&nc->lock, flags);

/* Update the hot channel */
spin_lock_irqsave(&ndp->lock, flags);
ndp->hot_channel = hot_nc;
spin_unlock_irqrestore(&ndp->lock, flags);

ncsi_start_channel_monitor(nc);
ncsi_process_next_channel(ndp);
break;
Expand All @@ -773,10 +782,14 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
{
struct ncsi_package *np;
struct ncsi_channel *nc, *found;
struct ncsi_channel *nc, *found, *hot_nc;
struct ncsi_channel_mode *ncm;
unsigned long flags;

spin_lock_irqsave(&ndp->lock, flags);
hot_nc = ndp->hot_channel;
spin_unlock_irqrestore(&ndp->lock, flags);

/* The search is done once an inactive channel with up
* link is found.
*/
Expand All @@ -794,6 +807,9 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
if (!found)
found = nc;

if (nc == hot_nc)
found = nc;

ncm = &nc->modes[NCSI_MODE_LINK];
if (ncm->data[2] & 0x1) {
spin_unlock_irqrestore(&nc->lock, flags);
Expand Down

0 comments on commit bbc7c01

Please sign in to comment.