diff options
author | David S. Miller <davem@davemloft.net> | 2016-10-20 11:23:08 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-10-20 11:23:08 -0400 |
commit | ae3877bce505e5591832bcb7d8a7aefb74a04ffb (patch) | |
tree | e27ab3bc1a06c24831946d1319a64d292f55f7d5 | |
parent | 5712bf9c5c30ade3204016147d7b04bece6952d9 (diff) | |
parent | 22d8aa93d7ea169a349fb2f9dee5babc68f6a683 (diff) |
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: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ncsi/internal.h | 2 | ||||
-rw-r--r-- | net/ncsi/ncsi-aen.c | 18 | ||||
-rw-r--r-- | net/ncsi/ncsi-manage.c | 126 |
3 files changed, 112 insertions, 34 deletions
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h index 13290a70fa71..1308a56f2591 100644 --- a/net/ncsi/internal.h +++ b/net/ncsi/internal.h | |||
@@ -246,6 +246,7 @@ enum { | |||
246 | ncsi_dev_state_config_gls, | 246 | ncsi_dev_state_config_gls, |
247 | ncsi_dev_state_config_done, | 247 | ncsi_dev_state_config_done, |
248 | ncsi_dev_state_suspend_select = 0x0401, | 248 | ncsi_dev_state_suspend_select = 0x0401, |
249 | ncsi_dev_state_suspend_gls, | ||
249 | ncsi_dev_state_suspend_dcnt, | 250 | ncsi_dev_state_suspend_dcnt, |
250 | ncsi_dev_state_suspend_dc, | 251 | ncsi_dev_state_suspend_dc, |
251 | ncsi_dev_state_suspend_deselect, | 252 | ncsi_dev_state_suspend_deselect, |
@@ -264,6 +265,7 @@ struct ncsi_dev_priv { | |||
264 | #endif | 265 | #endif |
265 | unsigned int package_num; /* Number of packages */ | 266 | unsigned int package_num; /* Number of packages */ |
266 | struct list_head packages; /* List of packages */ | 267 | struct list_head packages; /* List of packages */ |
268 | struct ncsi_channel *hot_channel; /* Channel was ever active */ | ||
267 | struct ncsi_request requests[256]; /* Request table */ | 269 | struct ncsi_request requests[256]; /* Request table */ |
268 | unsigned int request_id; /* Last used request ID */ | 270 | unsigned int request_id; /* Last used request ID */ |
269 | #define NCSI_REQ_START_IDX 1 | 271 | #define NCSI_REQ_START_IDX 1 |
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c index b41a6617d498..6898e7229285 100644 --- a/net/ncsi/ncsi-aen.c +++ b/net/ncsi/ncsi-aen.c | |||
@@ -141,23 +141,35 @@ static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp, | |||
141 | return -ENODEV; | 141 | return -ENODEV; |
142 | 142 | ||
143 | /* If the channel is active one, we need reconfigure it */ | 143 | /* If the channel is active one, we need reconfigure it */ |
144 | spin_lock_irqsave(&nc->lock, flags); | ||
144 | ncm = &nc->modes[NCSI_MODE_LINK]; | 145 | ncm = &nc->modes[NCSI_MODE_LINK]; |
145 | hncdsc = (struct ncsi_aen_hncdsc_pkt *)h; | 146 | hncdsc = (struct ncsi_aen_hncdsc_pkt *)h; |
146 | ncm->data[3] = ntohl(hncdsc->status); | 147 | ncm->data[3] = ntohl(hncdsc->status); |
147 | if (!list_empty(&nc->link) || | 148 | if (!list_empty(&nc->link) || |
148 | nc->state != NCSI_CHANNEL_ACTIVE || | 149 | nc->state != NCSI_CHANNEL_ACTIVE) { |
149 | (ncm->data[3] & 0x1)) | 150 | spin_unlock_irqrestore(&nc->lock, flags); |
150 | return 0; | 151 | return 0; |
152 | } | ||
151 | 153 | ||
152 | if (ndp->flags & NCSI_DEV_HWA) | 154 | spin_unlock_irqrestore(&nc->lock, flags); |
155 | if (!(ndp->flags & NCSI_DEV_HWA) && !(ncm->data[3] & 0x1)) | ||
153 | ndp->flags |= NCSI_DEV_RESHUFFLE; | 156 | ndp->flags |= NCSI_DEV_RESHUFFLE; |
154 | 157 | ||
155 | /* If this channel is the active one and the link doesn't | 158 | /* If this channel is the active one and the link doesn't |
156 | * work, we have to choose another channel to be active one. | 159 | * work, we have to choose another channel to be active one. |
157 | * The logic here is exactly similar to what we do when link | 160 | * The logic here is exactly similar to what we do when link |
158 | * is down on the active channel. | 161 | * is down on the active channel. |
162 | * | ||
163 | * On the other hand, we need configure it when host driver | ||
164 | * state on the active channel becomes ready. | ||
159 | */ | 165 | */ |
160 | ncsi_stop_channel_monitor(nc); | 166 | ncsi_stop_channel_monitor(nc); |
167 | |||
168 | spin_lock_irqsave(&nc->lock, flags); | ||
169 | nc->state = (ncm->data[3] & 0x1) ? NCSI_CHANNEL_INACTIVE : | ||
170 | NCSI_CHANNEL_ACTIVE; | ||
171 | spin_unlock_irqrestore(&nc->lock, flags); | ||
172 | |||
161 | spin_lock_irqsave(&ndp->lock, flags); | 173 | spin_lock_irqsave(&ndp->lock, flags); |
162 | list_add_tail_rcu(&nc->link, &ndp->channel_queue); | 174 | list_add_tail_rcu(&nc->link, &ndp->channel_queue); |
163 | spin_unlock_irqrestore(&ndp->lock, flags); | 175 | spin_unlock_irqrestore(&ndp->lock, flags); |
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index 5e509e547c2d..a3bd5fa8ad09 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c | |||
@@ -540,42 +540,86 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) | |||
540 | nd->state = ncsi_dev_state_suspend_select; | 540 | nd->state = ncsi_dev_state_suspend_select; |
541 | /* Fall through */ | 541 | /* Fall through */ |
542 | case ncsi_dev_state_suspend_select: | 542 | case ncsi_dev_state_suspend_select: |
543 | case ncsi_dev_state_suspend_dcnt: | ||
544 | case ncsi_dev_state_suspend_dc: | ||
545 | case ncsi_dev_state_suspend_deselect: | ||
546 | ndp->pending_req_num = 1; | 543 | ndp->pending_req_num = 1; |
547 | 544 | ||
548 | np = ndp->active_package; | 545 | nca.type = NCSI_PKT_CMD_SP; |
549 | nc = ndp->active_channel; | ||
550 | nca.package = np->id; | 546 | nca.package = np->id; |
551 | if (nd->state == ncsi_dev_state_suspend_select) { | 547 | nca.channel = NCSI_RESERVED_CHANNEL; |
552 | nca.type = NCSI_PKT_CMD_SP; | 548 | if (ndp->flags & NCSI_DEV_HWA) |
553 | nca.channel = NCSI_RESERVED_CHANNEL; | 549 | nca.bytes[0] = 0; |
554 | if (ndp->flags & NCSI_DEV_HWA) | 550 | else |
555 | nca.bytes[0] = 0; | 551 | nca.bytes[0] = 1; |
556 | else | 552 | |
557 | nca.bytes[0] = 1; | 553 | /* To retrieve the last link states of channels in current |
554 | * package when current active channel needs fail over to | ||
555 | * another one. It means we will possibly select another | ||
556 | * channel as next active one. The link states of channels | ||
557 | * are most important factor of the selection. So we need | ||
558 | * accurate link states. Unfortunately, the link states on | ||
559 | * inactive channels can't be updated with LSC AEN in time. | ||
560 | */ | ||
561 | if (ndp->flags & NCSI_DEV_RESHUFFLE) | ||
562 | nd->state = ncsi_dev_state_suspend_gls; | ||
563 | else | ||
558 | nd->state = ncsi_dev_state_suspend_dcnt; | 564 | nd->state = ncsi_dev_state_suspend_dcnt; |
559 | } else if (nd->state == ncsi_dev_state_suspend_dcnt) { | 565 | ret = ncsi_xmit_cmd(&nca); |
560 | nca.type = NCSI_PKT_CMD_DCNT; | 566 | if (ret) |
561 | nca.channel = nc->id; | 567 | goto error; |
562 | nd->state = ncsi_dev_state_suspend_dc; | 568 | |
563 | } else if (nd->state == ncsi_dev_state_suspend_dc) { | 569 | break; |
564 | nca.type = NCSI_PKT_CMD_DC; | 570 | case ncsi_dev_state_suspend_gls: |
571 | ndp->pending_req_num = np->channel_num; | ||
572 | |||
573 | nca.type = NCSI_PKT_CMD_GLS; | ||
574 | nca.package = np->id; | ||
575 | |||
576 | nd->state = ncsi_dev_state_suspend_dcnt; | ||
577 | NCSI_FOR_EACH_CHANNEL(np, nc) { | ||
565 | nca.channel = nc->id; | 578 | nca.channel = nc->id; |
566 | nca.bytes[0] = 1; | 579 | ret = ncsi_xmit_cmd(&nca); |
567 | nd->state = ncsi_dev_state_suspend_deselect; | 580 | if (ret) |
568 | } else if (nd->state == ncsi_dev_state_suspend_deselect) { | 581 | goto error; |
569 | nca.type = NCSI_PKT_CMD_DP; | ||
570 | nca.channel = NCSI_RESERVED_CHANNEL; | ||
571 | nd->state = ncsi_dev_state_suspend_done; | ||
572 | } | 582 | } |
573 | 583 | ||
584 | break; | ||
585 | case ncsi_dev_state_suspend_dcnt: | ||
586 | ndp->pending_req_num = 1; | ||
587 | |||
588 | nca.type = NCSI_PKT_CMD_DCNT; | ||
589 | nca.package = np->id; | ||
590 | nca.channel = nc->id; | ||
591 | |||
592 | nd->state = ncsi_dev_state_suspend_dc; | ||
574 | ret = ncsi_xmit_cmd(&nca); | 593 | ret = ncsi_xmit_cmd(&nca); |
575 | if (ret) { | 594 | if (ret) |
576 | nd->state = ncsi_dev_state_functional; | 595 | goto error; |
577 | return; | 596 | |
578 | } | 597 | break; |
598 | case ncsi_dev_state_suspend_dc: | ||
599 | ndp->pending_req_num = 1; | ||
600 | |||
601 | nca.type = NCSI_PKT_CMD_DC; | ||
602 | nca.package = np->id; | ||
603 | nca.channel = nc->id; | ||
604 | nca.bytes[0] = 1; | ||
605 | |||
606 | nd->state = ncsi_dev_state_suspend_deselect; | ||
607 | ret = ncsi_xmit_cmd(&nca); | ||
608 | if (ret) | ||
609 | goto error; | ||
610 | |||
611 | break; | ||
612 | case ncsi_dev_state_suspend_deselect: | ||
613 | ndp->pending_req_num = 1; | ||
614 | |||
615 | nca.type = NCSI_PKT_CMD_DP; | ||
616 | nca.package = np->id; | ||
617 | nca.channel = NCSI_RESERVED_CHANNEL; | ||
618 | |||
619 | nd->state = ncsi_dev_state_suspend_done; | ||
620 | ret = ncsi_xmit_cmd(&nca); | ||
621 | if (ret) | ||
622 | goto error; | ||
579 | 623 | ||
580 | break; | 624 | break; |
581 | case ncsi_dev_state_suspend_done: | 625 | case ncsi_dev_state_suspend_done: |
@@ -589,6 +633,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) | |||
589 | netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n", | 633 | netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n", |
590 | nd->state); | 634 | nd->state); |
591 | } | 635 | } |
636 | |||
637 | return; | ||
638 | error: | ||
639 | nd->state = ncsi_dev_state_functional; | ||
592 | } | 640 | } |
593 | 641 | ||
594 | static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) | 642 | 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 | struct net_device *dev = nd->dev; | 645 | struct net_device *dev = nd->dev; |
598 | struct ncsi_package *np = ndp->active_package; | 646 | struct ncsi_package *np = ndp->active_package; |
599 | struct ncsi_channel *nc = ndp->active_channel; | 647 | struct ncsi_channel *nc = ndp->active_channel; |
648 | struct ncsi_channel *hot_nc = NULL; | ||
600 | struct ncsi_cmd_arg nca; | 649 | struct ncsi_cmd_arg nca; |
601 | unsigned char index; | 650 | unsigned char index; |
602 | unsigned long flags; | 651 | unsigned long flags; |
@@ -702,12 +751,20 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) | |||
702 | break; | 751 | break; |
703 | case ncsi_dev_state_config_done: | 752 | case ncsi_dev_state_config_done: |
704 | spin_lock_irqsave(&nc->lock, flags); | 753 | spin_lock_irqsave(&nc->lock, flags); |
705 | if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) | 754 | if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { |
755 | hot_nc = nc; | ||
706 | nc->state = NCSI_CHANNEL_ACTIVE; | 756 | nc->state = NCSI_CHANNEL_ACTIVE; |
707 | else | 757 | } else { |
758 | hot_nc = NULL; | ||
708 | nc->state = NCSI_CHANNEL_INACTIVE; | 759 | nc->state = NCSI_CHANNEL_INACTIVE; |
760 | } | ||
709 | spin_unlock_irqrestore(&nc->lock, flags); | 761 | spin_unlock_irqrestore(&nc->lock, flags); |
710 | 762 | ||
763 | /* Update the hot channel */ | ||
764 | spin_lock_irqsave(&ndp->lock, flags); | ||
765 | ndp->hot_channel = hot_nc; | ||
766 | spin_unlock_irqrestore(&ndp->lock, flags); | ||
767 | |||
711 | ncsi_start_channel_monitor(nc); | 768 | ncsi_start_channel_monitor(nc); |
712 | ncsi_process_next_channel(ndp); | 769 | ncsi_process_next_channel(ndp); |
713 | break; | 770 | break; |
@@ -725,10 +782,14 @@ error: | |||
725 | static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) | 782 | static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) |
726 | { | 783 | { |
727 | struct ncsi_package *np; | 784 | struct ncsi_package *np; |
728 | struct ncsi_channel *nc, *found; | 785 | struct ncsi_channel *nc, *found, *hot_nc; |
729 | struct ncsi_channel_mode *ncm; | 786 | struct ncsi_channel_mode *ncm; |
730 | unsigned long flags; | 787 | unsigned long flags; |
731 | 788 | ||
789 | spin_lock_irqsave(&ndp->lock, flags); | ||
790 | hot_nc = ndp->hot_channel; | ||
791 | spin_unlock_irqrestore(&ndp->lock, flags); | ||
792 | |||
732 | /* The search is done once an inactive channel with up | 793 | /* The search is done once an inactive channel with up |
733 | * link is found. | 794 | * link is found. |
734 | */ | 795 | */ |
@@ -746,6 +807,9 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) | |||
746 | if (!found) | 807 | if (!found) |
747 | found = nc; | 808 | found = nc; |
748 | 809 | ||
810 | if (nc == hot_nc) | ||
811 | found = nc; | ||
812 | |||
749 | ncm = &nc->modes[NCSI_MODE_LINK]; | 813 | ncm = &nc->modes[NCSI_MODE_LINK]; |
750 | if (ncm->data[2] & 0x1) { | 814 | if (ncm->data[2] & 0x1) { |
751 | spin_unlock_irqrestore(&nc->lock, flags); | 815 | spin_unlock_irqrestore(&nc->lock, flags); |