Commit ae3877bc authored by David S. Miller's avatar David S. Miller

Merge branch 'ncsi-fixes'

Gavin Shan says:

====================
net/ncsi: More bug fixes

This series fixes 2 issues that were found during NCSI's availability
testing on BCM5718 and improves HNCDSC AEN handler:

   * PATCH[1] refactors the code so that minimal code change is put
     to PATCH[2].
   * PATCH[2] fixes the NCSI channel's stale link state before doing
     failover.
   * PATCH[3] chooses the hot channel, which was ever chosen as active
     channel, when the available channels are all in link-down state.
   * PATCH[4] improves Host Network Controller Driver Status Change
     (HNCDSC) AEN handler

Changelog
=========
v2:
   * Merged PATCH[v1 1/2] to PATCH[v2 1].
   * Avoid if/else statements in ncsi_suspend_channel() as Joel suggested.
   * Added comments to explain why we need retrieve last link states in
     ncsi_suspend_channel().
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5712bf9c 22d8aa93
...@@ -246,6 +246,7 @@ enum { ...@@ -246,6 +246,7 @@ enum {
ncsi_dev_state_config_gls, ncsi_dev_state_config_gls,
ncsi_dev_state_config_done, ncsi_dev_state_config_done,
ncsi_dev_state_suspend_select = 0x0401, ncsi_dev_state_suspend_select = 0x0401,
ncsi_dev_state_suspend_gls,
ncsi_dev_state_suspend_dcnt, ncsi_dev_state_suspend_dcnt,
ncsi_dev_state_suspend_dc, ncsi_dev_state_suspend_dc,
ncsi_dev_state_suspend_deselect, ncsi_dev_state_suspend_deselect,
...@@ -264,6 +265,7 @@ struct ncsi_dev_priv { ...@@ -264,6 +265,7 @@ struct ncsi_dev_priv {
#endif #endif
unsigned int package_num; /* Number of packages */ unsigned int package_num; /* Number of packages */
struct list_head packages; /* List 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 */ struct ncsi_request requests[256]; /* Request table */
unsigned int request_id; /* Last used request ID */ unsigned int request_id; /* Last used request ID */
#define NCSI_REQ_START_IDX 1 #define NCSI_REQ_START_IDX 1
......
...@@ -141,23 +141,35 @@ static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp, ...@@ -141,23 +141,35 @@ static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
return -ENODEV; return -ENODEV;
/* If the channel is active one, we need reconfigure it */ /* If the channel is active one, we need reconfigure it */
spin_lock_irqsave(&nc->lock, flags);
ncm = &nc->modes[NCSI_MODE_LINK]; ncm = &nc->modes[NCSI_MODE_LINK];
hncdsc = (struct ncsi_aen_hncdsc_pkt *)h; hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
ncm->data[3] = ntohl(hncdsc->status); ncm->data[3] = ntohl(hncdsc->status);
if (!list_empty(&nc->link) || if (!list_empty(&nc->link) ||
nc->state != NCSI_CHANNEL_ACTIVE || nc->state != NCSI_CHANNEL_ACTIVE) {
(ncm->data[3] & 0x1)) spin_unlock_irqrestore(&nc->lock, flags);
return 0; return 0;
}
if (ndp->flags & NCSI_DEV_HWA) spin_unlock_irqrestore(&nc->lock, flags);
if (!(ndp->flags & NCSI_DEV_HWA) && !(ncm->data[3] & 0x1))
ndp->flags |= NCSI_DEV_RESHUFFLE; ndp->flags |= NCSI_DEV_RESHUFFLE;
/* If this channel is the active one and the link doesn't /* If this channel is the active one and the link doesn't
* work, we have to choose another channel to be active one. * work, we have to choose another channel to be active one.
* The logic here is exactly similar to what we do when link * The logic here is exactly similar to what we do when link
* is down on the active channel. * is down on the active channel.
*
* On the other hand, we need configure it when host driver
* state on the active channel becomes ready.
*/ */
ncsi_stop_channel_monitor(nc); ncsi_stop_channel_monitor(nc);
spin_lock_irqsave(&nc->lock, flags);
nc->state = (ncm->data[3] & 0x1) ? NCSI_CHANNEL_INACTIVE :
NCSI_CHANNEL_ACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
spin_lock_irqsave(&ndp->lock, flags); spin_lock_irqsave(&ndp->lock, flags);
list_add_tail_rcu(&nc->link, &ndp->channel_queue); list_add_tail_rcu(&nc->link, &ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags); spin_unlock_irqrestore(&ndp->lock, flags);
......
...@@ -540,42 +540,86 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) ...@@ -540,42 +540,86 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
nd->state = ncsi_dev_state_suspend_select; nd->state = ncsi_dev_state_suspend_select;
/* Fall through */ /* Fall through */
case ncsi_dev_state_suspend_select: case ncsi_dev_state_suspend_select:
case ncsi_dev_state_suspend_dcnt:
case ncsi_dev_state_suspend_dc:
case ncsi_dev_state_suspend_deselect:
ndp->pending_req_num = 1; ndp->pending_req_num = 1;
np = ndp->active_package; nca.type = NCSI_PKT_CMD_SP;
nc = ndp->active_channel;
nca.package = np->id; nca.package = np->id;
if (nd->state == ncsi_dev_state_suspend_select) { nca.channel = NCSI_RESERVED_CHANNEL;
nca.type = NCSI_PKT_CMD_SP; if (ndp->flags & NCSI_DEV_HWA)
nca.channel = NCSI_RESERVED_CHANNEL; nca.bytes[0] = 0;
if (ndp->flags & NCSI_DEV_HWA) else
nca.bytes[0] = 0; nca.bytes[0] = 1;
else
nca.bytes[0] = 1; /* To retrieve the last link states of channels in current
* package when current active channel needs fail over to
* another one. It means we will possibly select another
* channel as next active one. The link states of channels
* are most important factor of the selection. So we need
* accurate link states. Unfortunately, the link states on
* inactive channels can't be updated with LSC AEN in time.
*/
if (ndp->flags & NCSI_DEV_RESHUFFLE)
nd->state = ncsi_dev_state_suspend_gls;
else
nd->state = ncsi_dev_state_suspend_dcnt; nd->state = ncsi_dev_state_suspend_dcnt;
} else if (nd->state == ncsi_dev_state_suspend_dcnt) { ret = ncsi_xmit_cmd(&nca);
nca.type = NCSI_PKT_CMD_DCNT; if (ret)
nca.channel = nc->id; goto error;
nd->state = ncsi_dev_state_suspend_dc;
} else if (nd->state == ncsi_dev_state_suspend_dc) { break;
nca.type = NCSI_PKT_CMD_DC; case ncsi_dev_state_suspend_gls:
ndp->pending_req_num = np->channel_num;
nca.type = NCSI_PKT_CMD_GLS;
nca.package = np->id;
nd->state = ncsi_dev_state_suspend_dcnt;
NCSI_FOR_EACH_CHANNEL(np, nc) {
nca.channel = nc->id; nca.channel = nc->id;
nca.bytes[0] = 1; ret = ncsi_xmit_cmd(&nca);
nd->state = ncsi_dev_state_suspend_deselect; if (ret)
} else if (nd->state == ncsi_dev_state_suspend_deselect) { goto error;
nca.type = NCSI_PKT_CMD_DP;
nca.channel = NCSI_RESERVED_CHANNEL;
nd->state = ncsi_dev_state_suspend_done;
} }
break;
case ncsi_dev_state_suspend_dcnt:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_DCNT;
nca.package = np->id;
nca.channel = nc->id;
nd->state = ncsi_dev_state_suspend_dc;
ret = ncsi_xmit_cmd(&nca); ret = ncsi_xmit_cmd(&nca);
if (ret) { if (ret)
nd->state = ncsi_dev_state_functional; goto error;
return;
} break;
case ncsi_dev_state_suspend_dc:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_DC;
nca.package = np->id;
nca.channel = nc->id;
nca.bytes[0] = 1;
nd->state = ncsi_dev_state_suspend_deselect;
ret = ncsi_xmit_cmd(&nca);
if (ret)
goto error;
break;
case ncsi_dev_state_suspend_deselect:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_DP;
nca.package = np->id;
nca.channel = NCSI_RESERVED_CHANNEL;
nd->state = ncsi_dev_state_suspend_done;
ret = ncsi_xmit_cmd(&nca);
if (ret)
goto error;
break; break;
case ncsi_dev_state_suspend_done: case ncsi_dev_state_suspend_done:
...@@ -589,6 +633,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) ...@@ -589,6 +633,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n", netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
nd->state); nd->state);
} }
return;
error:
nd->state = ncsi_dev_state_functional;
} }
static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
...@@ -597,6 +645,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) ...@@ -597,6 +645,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
struct net_device *dev = nd->dev; struct net_device *dev = nd->dev;
struct ncsi_package *np = ndp->active_package; struct ncsi_package *np = ndp->active_package;
struct ncsi_channel *nc = ndp->active_channel; struct ncsi_channel *nc = ndp->active_channel;
struct ncsi_channel *hot_nc = NULL;
struct ncsi_cmd_arg nca; struct ncsi_cmd_arg nca;
unsigned char index; unsigned char index;
unsigned long flags; unsigned long flags;
...@@ -702,12 +751,20 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) ...@@ -702,12 +751,20 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
break; break;
case ncsi_dev_state_config_done: case ncsi_dev_state_config_done:
spin_lock_irqsave(&nc->lock, flags); 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; nc->state = NCSI_CHANNEL_ACTIVE;
else } else {
hot_nc = NULL;
nc->state = NCSI_CHANNEL_INACTIVE; nc->state = NCSI_CHANNEL_INACTIVE;
}
spin_unlock_irqrestore(&nc->lock, flags); 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_start_channel_monitor(nc);
ncsi_process_next_channel(ndp); ncsi_process_next_channel(ndp);
break; break;
...@@ -725,10 +782,14 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) ...@@ -725,10 +782,14 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
{ {
struct ncsi_package *np; struct ncsi_package *np;
struct ncsi_channel *nc, *found; struct ncsi_channel *nc, *found, *hot_nc;
struct ncsi_channel_mode *ncm; struct ncsi_channel_mode *ncm;
unsigned long flags; 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 /* The search is done once an inactive channel with up
* link is found. * link is found.
*/ */
...@@ -746,6 +807,9 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) ...@@ -746,6 +807,9 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
if (!found) if (!found)
found = nc; found = nc;
if (nc == hot_nc)
found = nc;
ncm = &nc->modes[NCSI_MODE_LINK]; ncm = &nc->modes[NCSI_MODE_LINK];
if (ncm->data[2] & 0x1) { if (ncm->data[2] & 0x1) {
spin_unlock_irqrestore(&nc->lock, flags); spin_unlock_irqrestore(&nc->lock, flags);
......
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