summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Mendoza-Jonas <sam@mendozajonas.com>2018-11-15 23:51:59 -0500
committerDavid S. Miller <davem@davemloft.net>2018-11-18 00:09:49 -0500
commit8d951a75d022d94a05f5fa74217670a981e8302d (patch)
treed360d9898e404a423779ba2b3ced1a5801515997
parent2878a2cfe57a5db21844801cf502fe535a3134b2 (diff)
net/ncsi: Configure multi-package, multi-channel modes with failover
This patch extends the ncsi-netlink interface with two new commands and three new attributes to configure multiple packages and/or channels at once, and configure specific failover modes. NCSI_CMD_SET_PACKAGE mask and NCSI_CMD_SET_CHANNEL_MASK set a whitelist of packages or channels allowed to be configured with the NCSI_ATTR_PACKAGE_MASK and NCSI_ATTR_CHANNEL_MASK attributes respectively. If one of these whitelists is set only packages or channels matching the whitelist are considered for the channel queue in ncsi_choose_active_channel(). These commands may also use the NCSI_ATTR_MULTI_FLAG to signal that multiple packages or channels may be configured simultaneously. NCSI hardware arbitration (HWA) must be available in order to enable multi-package mode. Multi-channel mode is always available. If the NCSI_ATTR_CHANNEL_ID attribute is present in the NCSI_CMD_SET_CHANNEL_MASK command the it sets the preferred channel as with the NCSI_CMD_SET_INTERFACE command. The combination of preferred channel and channel whitelist defines a primary channel and the allowed failover channels. If the NCSI_ATTR_MULTI_FLAG attribute is also present then the preferred channel is configured for Tx/Rx and the other channels are enabled only for Rx. Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/uapi/linux/ncsi.h15
-rw-r--r--net/ncsi/internal.h16
-rw-r--r--net/ncsi/ncsi-aen.c63
-rw-r--r--net/ncsi/ncsi-manage.c264
-rw-r--r--net/ncsi/ncsi-netlink.c221
-rw-r--r--net/ncsi/ncsi-rsp.c2
6 files changed, 493 insertions, 88 deletions
diff --git a/include/uapi/linux/ncsi.h b/include/uapi/linux/ncsi.h
index 0a26a5576645..a3f87c54fdb3 100644
--- a/include/uapi/linux/ncsi.h
+++ b/include/uapi/linux/ncsi.h
@@ -26,6 +26,12 @@
26 * @NCSI_CMD_SEND_CMD: send NC-SI command to network card. 26 * @NCSI_CMD_SEND_CMD: send NC-SI command to network card.
27 * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID 27 * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID
28 * and NCSI_ATTR_CHANNEL_ID. 28 * and NCSI_ATTR_CHANNEL_ID.
29 * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages.
30 * Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK.
31 * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels.
32 * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and
33 * NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets
34 * the primary channel.
29 * @NCSI_CMD_MAX: highest command number 35 * @NCSI_CMD_MAX: highest command number
30 */ 36 */
31enum ncsi_nl_commands { 37enum ncsi_nl_commands {
@@ -34,6 +40,8 @@ enum ncsi_nl_commands {
34 NCSI_CMD_SET_INTERFACE, 40 NCSI_CMD_SET_INTERFACE,
35 NCSI_CMD_CLEAR_INTERFACE, 41 NCSI_CMD_CLEAR_INTERFACE,
36 NCSI_CMD_SEND_CMD, 42 NCSI_CMD_SEND_CMD,
43 NCSI_CMD_SET_PACKAGE_MASK,
44 NCSI_CMD_SET_CHANNEL_MASK,
37 45
38 __NCSI_CMD_AFTER_LAST, 46 __NCSI_CMD_AFTER_LAST,
39 NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 47 NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
@@ -48,6 +56,10 @@ enum ncsi_nl_commands {
48 * @NCSI_ATTR_PACKAGE_ID: package ID 56 * @NCSI_ATTR_PACKAGE_ID: package ID
49 * @NCSI_ATTR_CHANNEL_ID: channel ID 57 * @NCSI_ATTR_CHANNEL_ID: channel ID
50 * @NCSI_ATTR_DATA: command payload 58 * @NCSI_ATTR_DATA: command payload
59 * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with
60 * NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK.
61 * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages.
62 * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels.
51 * @NCSI_ATTR_MAX: highest attribute number 63 * @NCSI_ATTR_MAX: highest attribute number
52 */ 64 */
53enum ncsi_nl_attrs { 65enum ncsi_nl_attrs {
@@ -57,6 +69,9 @@ enum ncsi_nl_attrs {
57 NCSI_ATTR_PACKAGE_ID, 69 NCSI_ATTR_PACKAGE_ID,
58 NCSI_ATTR_CHANNEL_ID, 70 NCSI_ATTR_CHANNEL_ID,
59 NCSI_ATTR_DATA, 71 NCSI_ATTR_DATA,
72 NCSI_ATTR_MULTI_FLAG,
73 NCSI_ATTR_PACKAGE_MASK,
74 NCSI_ATTR_CHANNEL_MASK,
60 75
61 __NCSI_ATTR_AFTER_LAST, 76 __NCSI_ATTR_AFTER_LAST,
62 NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1 77 NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index bda51cb179fe..9e3642b802c4 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -222,6 +222,10 @@ struct ncsi_package {
222 unsigned int channel_num; /* Number of channels */ 222 unsigned int channel_num; /* Number of channels */
223 struct list_head channels; /* List of chanels */ 223 struct list_head channels; /* List of chanels */
224 struct list_head node; /* Form list of packages */ 224 struct list_head node; /* Form list of packages */
225
226 bool multi_channel; /* Enable multiple channels */
227 u32 channel_whitelist; /* Channels to configure */
228 struct ncsi_channel *preferred_channel; /* Primary channel */
225}; 229};
226 230
227struct ncsi_request { 231struct ncsi_request {
@@ -297,8 +301,6 @@ struct ncsi_dev_priv {
297 unsigned int package_num; /* Number of packages */ 301 unsigned int package_num; /* Number of packages */
298 struct list_head packages; /* List of packages */ 302 struct list_head packages; /* List of packages */
299 struct ncsi_channel *hot_channel; /* Channel was ever active */ 303 struct ncsi_channel *hot_channel; /* Channel was ever active */
300 struct ncsi_package *force_package; /* Force a specific package */
301 struct ncsi_channel *force_channel; /* Force a specific channel */
302 struct ncsi_request requests[256]; /* Request table */ 304 struct ncsi_request requests[256]; /* Request table */
303 unsigned int request_id; /* Last used request ID */ 305 unsigned int request_id; /* Last used request ID */
304#define NCSI_REQ_START_IDX 1 306#define NCSI_REQ_START_IDX 1
@@ -311,6 +313,9 @@ struct ncsi_dev_priv {
311 struct list_head node; /* Form NCSI device list */ 313 struct list_head node; /* Form NCSI device list */
312#define NCSI_MAX_VLAN_VIDS 15 314#define NCSI_MAX_VLAN_VIDS 15
313 struct list_head vlan_vids; /* List of active VLAN IDs */ 315 struct list_head vlan_vids; /* List of active VLAN IDs */
316
317 bool multi_package; /* Enable multiple packages */
318 u32 package_whitelist; /* Packages to configure */
314}; 319};
315 320
316struct ncsi_cmd_arg { 321struct ncsi_cmd_arg {
@@ -364,6 +369,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
364void ncsi_free_request(struct ncsi_request *nr); 369void ncsi_free_request(struct ncsi_request *nr);
365struct ncsi_dev *ncsi_find_dev(struct net_device *dev); 370struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
366int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); 371int ncsi_process_next_channel(struct ncsi_dev_priv *ndp);
372bool ncsi_channel_has_link(struct ncsi_channel *channel);
373bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
374 struct ncsi_channel *channel);
375int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
376 struct ncsi_package *np,
377 struct ncsi_channel *disable,
378 struct ncsi_channel *enable);
367 379
368/* Packet handlers */ 380/* Packet handlers */
369u32 ncsi_calculate_checksum(unsigned char *data, int len); 381u32 ncsi_calculate_checksum(unsigned char *data, int len);
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
index 57f77e5d381a..26d67e27551f 100644
--- a/net/ncsi/ncsi-aen.c
+++ b/net/ncsi/ncsi-aen.c
@@ -50,14 +50,15 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
50static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, 50static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
51 struct ncsi_aen_pkt_hdr *h) 51 struct ncsi_aen_pkt_hdr *h)
52{ 52{
53 struct ncsi_aen_lsc_pkt *lsc; 53 struct ncsi_channel *nc, *tmp;
54 struct ncsi_channel *nc;
55 struct ncsi_channel_mode *ncm; 54 struct ncsi_channel_mode *ncm;
56 bool chained;
57 int state;
58 unsigned long old_data, data; 55 unsigned long old_data, data;
59 unsigned long flags; 56 struct ncsi_aen_lsc_pkt *lsc;
57 struct ncsi_package *np;
60 bool had_link, has_link; 58 bool had_link, has_link;
59 unsigned long flags;
60 bool chained;
61 int state;
61 62
62 /* Find the NCSI channel */ 63 /* Find the NCSI channel */
63 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); 64 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
@@ -92,14 +93,52 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
92 if ((had_link == has_link) || chained) 93 if ((had_link == has_link) || chained)
93 return 0; 94 return 0;
94 95
95 if (had_link) 96 if (!ndp->multi_package && !nc->package->multi_channel) {
96 ndp->flags |= NCSI_DEV_RESHUFFLE; 97 if (had_link) {
97 ncsi_stop_channel_monitor(nc); 98 ndp->flags |= NCSI_DEV_RESHUFFLE;
98 spin_lock_irqsave(&ndp->lock, flags); 99 ncsi_stop_channel_monitor(nc);
99 list_add_tail_rcu(&nc->link, &ndp->channel_queue); 100 spin_lock_irqsave(&ndp->lock, flags);
100 spin_unlock_irqrestore(&ndp->lock, flags); 101 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
102 spin_unlock_irqrestore(&ndp->lock, flags);
103 return ncsi_process_next_channel(ndp);
104 }
105 /* Configured channel came up */
106 return 0;
107 }
101 108
102 return ncsi_process_next_channel(ndp); 109 if (had_link) {
110 ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
111 if (ncsi_channel_is_last(ndp, nc)) {
112 /* No channels left, reconfigure */
113 return ncsi_reset_dev(&ndp->ndev);
114 } else if (ncm->enable) {
115 /* Need to failover Tx channel */
116 ncsi_update_tx_channel(ndp, nc->package, nc, NULL);
117 }
118 } else if (has_link && nc->package->preferred_channel == nc) {
119 /* Return Tx to preferred channel */
120 ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
121 } else if (has_link) {
122 NCSI_FOR_EACH_PACKAGE(ndp, np) {
123 NCSI_FOR_EACH_CHANNEL(np, tmp) {
124 /* Enable Tx on this channel if the current Tx
125 * channel is down.
126 */
127 ncm = &tmp->modes[NCSI_MODE_TX_ENABLE];
128 if (ncm->enable &&
129 !ncsi_channel_has_link(tmp)) {
130 ncsi_update_tx_channel(ndp, nc->package,
131 tmp, nc);
132 break;
133 }
134 }
135 }
136 }
137
138 /* Leave configured channels active in a multi-channel scenario so
139 * AEN events are still received.
140 */
141 return 0;
103} 142}
104 143
105static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, 144static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index c814ce9bb3de..92e59f07f9a7 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -28,6 +28,29 @@
28LIST_HEAD(ncsi_dev_list); 28LIST_HEAD(ncsi_dev_list);
29DEFINE_SPINLOCK(ncsi_dev_lock); 29DEFINE_SPINLOCK(ncsi_dev_lock);
30 30
31bool ncsi_channel_has_link(struct ncsi_channel *channel)
32{
33 return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
34}
35
36bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
37 struct ncsi_channel *channel)
38{
39 struct ncsi_package *np;
40 struct ncsi_channel *nc;
41
42 NCSI_FOR_EACH_PACKAGE(ndp, np)
43 NCSI_FOR_EACH_CHANNEL(np, nc) {
44 if (nc == channel)
45 continue;
46 if (nc->state == NCSI_CHANNEL_ACTIVE &&
47 ncsi_channel_has_link(nc))
48 return false;
49 }
50
51 return true;
52}
53
31static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) 54static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
32{ 55{
33 struct ncsi_dev *nd = &ndp->ndev; 56 struct ncsi_dev *nd = &ndp->ndev;
@@ -52,7 +75,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
52 continue; 75 continue;
53 } 76 }
54 77
55 if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { 78 if (ncsi_channel_has_link(nc)) {
56 spin_unlock_irqrestore(&nc->lock, flags); 79 spin_unlock_irqrestore(&nc->lock, flags);
57 nd->link_up = 1; 80 nd->link_up = 1;
58 goto report; 81 goto report;
@@ -267,6 +290,7 @@ struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
267 np->ndp = ndp; 290 np->ndp = ndp;
268 spin_lock_init(&np->lock); 291 spin_lock_init(&np->lock);
269 INIT_LIST_HEAD(&np->channels); 292 INIT_LIST_HEAD(&np->channels);
293 np->channel_whitelist = UINT_MAX;
270 294
271 spin_lock_irqsave(&ndp->lock, flags); 295 spin_lock_irqsave(&ndp->lock, flags);
272 tmp = ncsi_find_package(ndp, id); 296 tmp = ncsi_find_package(ndp, id);
@@ -728,13 +752,144 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
728 752
729#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */ 753#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
730 754
755/* Determine if a given channel from the channel_queue should be used for Tx */
756static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
757 struct ncsi_channel *nc)
758{
759 struct ncsi_channel_mode *ncm;
760 struct ncsi_channel *channel;
761 struct ncsi_package *np;
762
763 /* Check if any other channel has Tx enabled; a channel may have already
764 * been configured and removed from the channel queue.
765 */
766 NCSI_FOR_EACH_PACKAGE(ndp, np) {
767 if (!ndp->multi_package && np != nc->package)
768 continue;
769 NCSI_FOR_EACH_CHANNEL(np, channel) {
770 ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
771 if (ncm->enable)
772 return false;
773 }
774 }
775
776 /* This channel is the preferred channel and has link */
777 list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
778 np = channel->package;
779 if (np->preferred_channel &&
780 ncsi_channel_has_link(np->preferred_channel)) {
781 return np->preferred_channel == nc;
782 }
783 }
784
785 /* This channel has link */
786 if (ncsi_channel_has_link(nc))
787 return true;
788
789 list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
790 if (ncsi_channel_has_link(channel))
791 return false;
792
793 /* No other channel has link; default to this one */
794 return true;
795}
796
797/* Change the active Tx channel in a multi-channel setup */
798int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
799 struct ncsi_package *package,
800 struct ncsi_channel *disable,
801 struct ncsi_channel *enable)
802{
803 struct ncsi_cmd_arg nca;
804 struct ncsi_channel *nc;
805 struct ncsi_package *np;
806 int ret = 0;
807
808 if (!package->multi_channel && !ndp->multi_package)
809 netdev_warn(ndp->ndev.dev,
810 "NCSI: Trying to update Tx channel in single-channel mode\n");
811 nca.ndp = ndp;
812 nca.req_flags = 0;
813
814 /* Find current channel with Tx enabled */
815 NCSI_FOR_EACH_PACKAGE(ndp, np) {
816 if (disable)
817 break;
818 if (!ndp->multi_package && np != package)
819 continue;
820
821 NCSI_FOR_EACH_CHANNEL(np, nc)
822 if (nc->modes[NCSI_MODE_TX_ENABLE].enable) {
823 disable = nc;
824 break;
825 }
826 }
827
828 /* Find a suitable channel for Tx */
829 NCSI_FOR_EACH_PACKAGE(ndp, np) {
830 if (enable)
831 break;
832 if (!ndp->multi_package && np != package)
833 continue;
834 if (!(ndp->package_whitelist & (0x1 << np->id)))
835 continue;
836
837 if (np->preferred_channel &&
838 ncsi_channel_has_link(np->preferred_channel)) {
839 enable = np->preferred_channel;
840 break;
841 }
842
843 NCSI_FOR_EACH_CHANNEL(np, nc) {
844 if (!(np->channel_whitelist & 0x1 << nc->id))
845 continue;
846 if (nc->state != NCSI_CHANNEL_ACTIVE)
847 continue;
848 if (ncsi_channel_has_link(nc)) {
849 enable = nc;
850 break;
851 }
852 }
853 }
854
855 if (disable == enable)
856 return -1;
857
858 if (!enable)
859 return -1;
860
861 if (disable) {
862 nca.channel = disable->id;
863 nca.package = disable->package->id;
864 nca.type = NCSI_PKT_CMD_DCNT;
865 ret = ncsi_xmit_cmd(&nca);
866 if (ret)
867 netdev_err(ndp->ndev.dev,
868 "Error %d sending DCNT\n",
869 ret);
870 }
871
872 netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
873
874 nca.channel = enable->id;
875 nca.package = enable->package->id;
876 nca.type = NCSI_PKT_CMD_ECNT;
877 ret = ncsi_xmit_cmd(&nca);
878 if (ret)
879 netdev_err(ndp->ndev.dev,
880 "Error %d sending ECNT\n",
881 ret);
882
883 return ret;
884}
885
731static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) 886static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
732{ 887{
733 struct ncsi_dev *nd = &ndp->ndev;
734 struct net_device *dev = nd->dev;
735 struct ncsi_package *np = ndp->active_package; 888 struct ncsi_package *np = ndp->active_package;
736 struct ncsi_channel *nc = ndp->active_channel; 889 struct ncsi_channel *nc = ndp->active_channel;
737 struct ncsi_channel *hot_nc = NULL; 890 struct ncsi_channel *hot_nc = NULL;
891 struct ncsi_dev *nd = &ndp->ndev;
892 struct net_device *dev = nd->dev;
738 struct ncsi_cmd_arg nca; 893 struct ncsi_cmd_arg nca;
739 unsigned char index; 894 unsigned char index;
740 unsigned long flags; 895 unsigned long flags;
@@ -856,20 +1011,29 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
856 } else if (nd->state == ncsi_dev_state_config_ebf) { 1011 } else if (nd->state == ncsi_dev_state_config_ebf) {
857 nca.type = NCSI_PKT_CMD_EBF; 1012 nca.type = NCSI_PKT_CMD_EBF;
858 nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap; 1013 nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
859 nd->state = ncsi_dev_state_config_ecnt; 1014 if (ncsi_channel_is_tx(ndp, nc))
1015 nd->state = ncsi_dev_state_config_ecnt;
1016 else
1017 nd->state = ncsi_dev_state_config_ec;
860#if IS_ENABLED(CONFIG_IPV6) 1018#if IS_ENABLED(CONFIG_IPV6)
861 if (ndp->inet6_addr_num > 0 && 1019 if (ndp->inet6_addr_num > 0 &&
862 (nc->caps[NCSI_CAP_GENERIC].cap & 1020 (nc->caps[NCSI_CAP_GENERIC].cap &
863 NCSI_CAP_GENERIC_MC)) 1021 NCSI_CAP_GENERIC_MC))
864 nd->state = ncsi_dev_state_config_egmf; 1022 nd->state = ncsi_dev_state_config_egmf;
865 else
866 nd->state = ncsi_dev_state_config_ecnt;
867 } else if (nd->state == ncsi_dev_state_config_egmf) { 1023 } else if (nd->state == ncsi_dev_state_config_egmf) {
868 nca.type = NCSI_PKT_CMD_EGMF; 1024 nca.type = NCSI_PKT_CMD_EGMF;
869 nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap; 1025 nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
870 nd->state = ncsi_dev_state_config_ecnt; 1026 if (ncsi_channel_is_tx(ndp, nc))
1027 nd->state = ncsi_dev_state_config_ecnt;
1028 else
1029 nd->state = ncsi_dev_state_config_ec;
871#endif /* CONFIG_IPV6 */ 1030#endif /* CONFIG_IPV6 */
872 } else if (nd->state == ncsi_dev_state_config_ecnt) { 1031 } else if (nd->state == ncsi_dev_state_config_ecnt) {
1032 if (np->preferred_channel &&
1033 nc != np->preferred_channel)
1034 netdev_info(ndp->ndev.dev,
1035 "NCSI: Tx failed over to channel %u\n",
1036 nc->id);
873 nca.type = NCSI_PKT_CMD_ECNT; 1037 nca.type = NCSI_PKT_CMD_ECNT;
874 nd->state = ncsi_dev_state_config_ec; 1038 nd->state = ncsi_dev_state_config_ec;
875 } else if (nd->state == ncsi_dev_state_config_ec) { 1039 } else if (nd->state == ncsi_dev_state_config_ec) {
@@ -959,43 +1123,35 @@ error:
959 1123
960static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) 1124static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
961{ 1125{
962 struct ncsi_package *np, *force_package; 1126 struct ncsi_channel *nc, *found, *hot_nc;
963 struct ncsi_channel *nc, *found, *hot_nc, *force_channel;
964 struct ncsi_channel_mode *ncm; 1127 struct ncsi_channel_mode *ncm;
965 unsigned long flags; 1128 unsigned long flags, cflags;
1129 struct ncsi_package *np;
1130 bool with_link;
966 1131
967 spin_lock_irqsave(&ndp->lock, flags); 1132 spin_lock_irqsave(&ndp->lock, flags);
968 hot_nc = ndp->hot_channel; 1133 hot_nc = ndp->hot_channel;
969 force_channel = ndp->force_channel;
970 force_package = ndp->force_package;
971 spin_unlock_irqrestore(&ndp->lock, flags); 1134 spin_unlock_irqrestore(&ndp->lock, flags);
972 1135
973 /* Force a specific channel whether or not it has link if we have been 1136 /* By default the search is done once an inactive channel with up
974 * configured to do so 1137 * link is found, unless a preferred channel is set.
975 */ 1138 * If multi_package or multi_channel are configured all channels in the
976 if (force_package && force_channel) { 1139 * whitelist are added to the channel queue.
977 found = force_channel;
978 ncm = &found->modes[NCSI_MODE_LINK];
979 if (!(ncm->data[2] & 0x1))
980 netdev_info(ndp->ndev.dev,
981 "NCSI: Channel %u forced, but it is link down\n",
982 found->id);
983 goto out;
984 }
985
986 /* The search is done once an inactive channel with up
987 * link is found.
988 */ 1140 */
989 found = NULL; 1141 found = NULL;
1142 with_link = false;
990 NCSI_FOR_EACH_PACKAGE(ndp, np) { 1143 NCSI_FOR_EACH_PACKAGE(ndp, np) {
991 if (ndp->force_package && np != ndp->force_package) 1144 if (!(ndp->package_whitelist & (0x1 << np->id)))
992 continue; 1145 continue;
993 NCSI_FOR_EACH_CHANNEL(np, nc) { 1146 NCSI_FOR_EACH_CHANNEL(np, nc) {
994 spin_lock_irqsave(&nc->lock, flags); 1147 if (!(np->channel_whitelist & (0x1 << nc->id)))
1148 continue;
1149
1150 spin_lock_irqsave(&nc->lock, cflags);
995 1151
996 if (!list_empty(&nc->link) || 1152 if (!list_empty(&nc->link) ||
997 nc->state != NCSI_CHANNEL_INACTIVE) { 1153 nc->state != NCSI_CHANNEL_INACTIVE) {
998 spin_unlock_irqrestore(&nc->lock, flags); 1154 spin_unlock_irqrestore(&nc->lock, cflags);
999 continue; 1155 continue;
1000 } 1156 }
1001 1157
@@ -1007,32 +1163,49 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
1007 1163
1008 ncm = &nc->modes[NCSI_MODE_LINK]; 1164 ncm = &nc->modes[NCSI_MODE_LINK];
1009 if (ncm->data[2] & 0x1) { 1165 if (ncm->data[2] & 0x1) {
1010 spin_unlock_irqrestore(&nc->lock, flags);
1011 found = nc; 1166 found = nc;
1012 goto out; 1167 with_link = true;
1013 } 1168 }
1014 1169
1015 spin_unlock_irqrestore(&nc->lock, flags); 1170 /* If multi_channel is enabled configure all valid
1171 * channels whether or not they currently have link
1172 * so they will have AENs enabled.
1173 */
1174 if (with_link || np->multi_channel) {
1175 spin_lock_irqsave(&ndp->lock, flags);
1176 list_add_tail_rcu(&nc->link,
1177 &ndp->channel_queue);
1178 spin_unlock_irqrestore(&ndp->lock, flags);
1179
1180 netdev_dbg(ndp->ndev.dev,
1181 "NCSI: Channel %u added to queue (link %s)\n",
1182 nc->id,
1183 ncm->data[2] & 0x1 ? "up" : "down");
1184 }
1185
1186 spin_unlock_irqrestore(&nc->lock, cflags);
1187
1188 if (with_link && !np->multi_channel)
1189 break;
1016 } 1190 }
1191 if (with_link && !ndp->multi_package)
1192 break;
1017 } 1193 }
1018 1194
1019 if (!found) { 1195 if (list_empty(&ndp->channel_queue) && found) {
1196 netdev_info(ndp->ndev.dev,
1197 "NCSI: No channel with link found, configuring channel %u\n",
1198 found->id);
1199 spin_lock_irqsave(&ndp->lock, flags);
1200 list_add_tail_rcu(&found->link, &ndp->channel_queue);
1201 spin_unlock_irqrestore(&ndp->lock, flags);
1202 } else if (!found) {
1020 netdev_warn(ndp->ndev.dev, 1203 netdev_warn(ndp->ndev.dev,
1021 "NCSI: No channel found with link\n"); 1204 "NCSI: No channel found to configure!\n");
1022 ncsi_report_link(ndp, true); 1205 ncsi_report_link(ndp, true);
1023 return -ENODEV; 1206 return -ENODEV;
1024 } 1207 }
1025 1208
1026 ncm = &found->modes[NCSI_MODE_LINK];
1027 netdev_dbg(ndp->ndev.dev,
1028 "NCSI: Channel %u added to queue (link %s)\n",
1029 found->id, ncm->data[2] & 0x1 ? "up" : "down");
1030
1031out:
1032 spin_lock_irqsave(&ndp->lock, flags);
1033 list_add_tail_rcu(&found->link, &ndp->channel_queue);
1034 spin_unlock_irqrestore(&ndp->lock, flags);
1035
1036 return ncsi_process_next_channel(ndp); 1209 return ncsi_process_next_channel(ndp);
1037} 1210}
1038 1211
@@ -1517,6 +1690,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
1517 INIT_LIST_HEAD(&ndp->channel_queue); 1690 INIT_LIST_HEAD(&ndp->channel_queue);
1518 INIT_LIST_HEAD(&ndp->vlan_vids); 1691 INIT_LIST_HEAD(&ndp->vlan_vids);
1519 INIT_WORK(&ndp->work, ncsi_dev_work); 1692 INIT_WORK(&ndp->work, ncsi_dev_work);
1693 ndp->package_whitelist = UINT_MAX;
1520 1694
1521 /* Initialize private NCSI device */ 1695 /* Initialize private NCSI device */
1522 spin_lock_init(&ndp->lock); 1696 spin_lock_init(&ndp->lock);
diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c
index cde48fe43dba..5d782445d2fc 100644
--- a/net/ncsi/ncsi-netlink.c
+++ b/net/ncsi/ncsi-netlink.c
@@ -30,6 +30,9 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
30 [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, 30 [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 },
31 [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, 31 [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 },
32 [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, 32 [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 },
33 [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG },
34 [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 },
35 [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 },
33}; 36};
34 37
35static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) 38static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
@@ -69,7 +72,7 @@ static int ncsi_write_channel_info(struct sk_buff *skb,
69 nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); 72 nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
70 if (nc->state == NCSI_CHANNEL_ACTIVE) 73 if (nc->state == NCSI_CHANNEL_ACTIVE)
71 nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); 74 nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
72 if (ndp->force_channel == nc) 75 if (nc == nc->package->preferred_channel)
73 nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); 76 nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
74 77
75 nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); 78 nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
@@ -114,7 +117,7 @@ static int ncsi_write_package_info(struct sk_buff *skb,
114 if (!pnest) 117 if (!pnest)
115 return -ENOMEM; 118 return -ENOMEM;
116 nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); 119 nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
117 if (ndp->force_package == np) 120 if ((0x1 << np->id) == ndp->package_whitelist)
118 nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 121 nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
119 cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); 122 cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
120 if (!cnest) { 123 if (!cnest) {
@@ -290,45 +293,54 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
290 package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 293 package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
291 package = NULL; 294 package = NULL;
292 295
293 spin_lock_irqsave(&ndp->lock, flags);
294
295 NCSI_FOR_EACH_PACKAGE(ndp, np) 296 NCSI_FOR_EACH_PACKAGE(ndp, np)
296 if (np->id == package_id) 297 if (np->id == package_id)
297 package = np; 298 package = np;
298 if (!package) { 299 if (!package) {
299 /* The user has set a package that does not exist */ 300 /* The user has set a package that does not exist */
300 spin_unlock_irqrestore(&ndp->lock, flags);
301 return -ERANGE; 301 return -ERANGE;
302 } 302 }
303 303
304 channel = NULL; 304 channel = NULL;
305 if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 305 if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
306 /* Allow any channel */
307 channel_id = NCSI_RESERVED_CHANNEL;
308 } else {
309 channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 306 channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
310 NCSI_FOR_EACH_CHANNEL(package, nc) 307 NCSI_FOR_EACH_CHANNEL(package, nc)
311 if (nc->id == channel_id) 308 if (nc->id == channel_id) {
312 channel = nc; 309 channel = nc;
310 break;
311 }
312 if (!channel) {
313 netdev_info(ndp->ndev.dev,
314 "NCSI: Channel %u does not exist!\n",
315 channel_id);
316 return -ERANGE;
317 }
313 } 318 }
314 319
315 if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { 320 spin_lock_irqsave(&ndp->lock, flags);
316 /* The user has set a channel that does not exist on this 321 ndp->package_whitelist = 0x1 << package->id;
317 * package 322 ndp->multi_package = false;
318 */
319 spin_unlock_irqrestore(&ndp->lock, flags);
320 netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
321 channel_id);
322 return -ERANGE;
323 }
324
325 ndp->force_package = package;
326 ndp->force_channel = channel;
327 spin_unlock_irqrestore(&ndp->lock, flags); 323 spin_unlock_irqrestore(&ndp->lock, flags);
328 324
329 netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", 325 spin_lock_irqsave(&package->lock, flags);
330 package_id, channel_id, 326 package->multi_channel = false;
331 channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); 327 if (channel) {
328 package->channel_whitelist = 0x1 << channel->id;
329 package->preferred_channel = channel;
330 } else {
331 /* Allow any channel */
332 package->channel_whitelist = UINT_MAX;
333 package->preferred_channel = NULL;
334 }
335 spin_unlock_irqrestore(&package->lock, flags);
336
337 if (channel)
338 netdev_info(ndp->ndev.dev,
339 "Set package 0x%x, channel 0x%x as preferred\n",
340 package_id, channel_id);
341 else
342 netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
343 package_id);
332 344
333 /* Update channel configuration */ 345 /* Update channel configuration */
334 if (!(ndp->flags & NCSI_DEV_RESET)) 346 if (!(ndp->flags & NCSI_DEV_RESET))
@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
340static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 352static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
341{ 353{
342 struct ncsi_dev_priv *ndp; 354 struct ncsi_dev_priv *ndp;
355 struct ncsi_package *np;
343 unsigned long flags; 356 unsigned long flags;
344 357
345 if (!info || !info->attrs) 358 if (!info || !info->attrs)
@@ -353,11 +366,19 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
353 if (!ndp) 366 if (!ndp)
354 return -ENODEV; 367 return -ENODEV;
355 368
356 /* Clear any override */ 369 /* Reset any whitelists and disable multi mode */
357 spin_lock_irqsave(&ndp->lock, flags); 370 spin_lock_irqsave(&ndp->lock, flags);
358 ndp->force_package = NULL; 371 ndp->package_whitelist = UINT_MAX;
359 ndp->force_channel = NULL; 372 ndp->multi_package = false;
360 spin_unlock_irqrestore(&ndp->lock, flags); 373 spin_unlock_irqrestore(&ndp->lock, flags);
374
375 NCSI_FOR_EACH_PACKAGE(ndp, np) {
376 spin_lock_irqsave(&np->lock, flags);
377 np->multi_channel = false;
378 np->channel_whitelist = UINT_MAX;
379 np->preferred_channel = NULL;
380 spin_unlock_irqrestore(&np->lock, flags);
381 }
361 netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 382 netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
362 383
363 /* Update channel configuration */ 384 /* Update channel configuration */
@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev,
563 return nlmsg_unicast(net->genl_sock, skb, snd_portid); 584 return nlmsg_unicast(net->genl_sock, skb, snd_portid);
564} 585}
565 586
587static int ncsi_set_package_mask_nl(struct sk_buff *msg,
588 struct genl_info *info)
589{
590 struct ncsi_dev_priv *ndp;
591 unsigned long flags;
592 int rc;
593
594 if (!info || !info->attrs)
595 return -EINVAL;
596
597 if (!info->attrs[NCSI_ATTR_IFINDEX])
598 return -EINVAL;
599
600 if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
601 return -EINVAL;
602
603 ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
604 nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
605 if (!ndp)
606 return -ENODEV;
607
608 spin_lock_irqsave(&ndp->lock, flags);
609 if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
610 if (ndp->flags & NCSI_DEV_HWA) {
611 ndp->multi_package = true;
612 rc = 0;
613 } else {
614 netdev_err(ndp->ndev.dev,
615 "NCSI: Can't use multiple packages without HWA\n");
616 rc = -EPERM;
617 }
618 } else {
619 ndp->multi_package = false;
620 rc = 0;
621 }
622
623 if (!rc)
624 ndp->package_whitelist =
625 nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
626 spin_unlock_irqrestore(&ndp->lock, flags);
627
628 if (!rc) {
629 /* Update channel configuration */
630 if (!(ndp->flags & NCSI_DEV_RESET))
631 ncsi_reset_dev(&ndp->ndev);
632 }
633
634 return rc;
635}
636
637static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
638 struct genl_info *info)
639{
640 struct ncsi_package *np, *package;
641 struct ncsi_channel *nc, *channel;
642 u32 package_id, channel_id;
643 struct ncsi_dev_priv *ndp;
644 unsigned long flags;
645
646 if (!info || !info->attrs)
647 return -EINVAL;
648
649 if (!info->attrs[NCSI_ATTR_IFINDEX])
650 return -EINVAL;
651
652 if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
653 return -EINVAL;
654
655 if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
656 return -EINVAL;
657
658 ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
659 nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
660 if (!ndp)
661 return -ENODEV;
662
663 package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
664 package = NULL;
665 NCSI_FOR_EACH_PACKAGE(ndp, np)
666 if (np->id == package_id) {
667 package = np;
668 break;
669 }
670 if (!package)
671 return -ERANGE;
672
673 spin_lock_irqsave(&package->lock, flags);
674
675 channel = NULL;
676 if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
677 channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
678 NCSI_FOR_EACH_CHANNEL(np, nc)
679 if (nc->id == channel_id) {
680 channel = nc;
681 break;
682 }
683 if (!channel) {
684 spin_unlock_irqrestore(&package->lock, flags);
685 return -ERANGE;
686 }
687 netdev_dbg(ndp->ndev.dev,
688 "NCSI: Channel %u set as preferred channel\n",
689 channel->id);
690 }
691
692 package->channel_whitelist =
693 nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
694 if (package->channel_whitelist == 0)
695 netdev_dbg(ndp->ndev.dev,
696 "NCSI: Package %u set to all channels disabled\n",
697 package->id);
698
699 package->preferred_channel = channel;
700
701 if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
702 package->multi_channel = true;
703 netdev_info(ndp->ndev.dev,
704 "NCSI: Multi-channel enabled on package %u\n",
705 package_id);
706 } else {
707 package->multi_channel = false;
708 }
709
710 spin_unlock_irqrestore(&package->lock, flags);
711
712 /* Update channel configuration */
713 if (!(ndp->flags & NCSI_DEV_RESET))
714 ncsi_reset_dev(&ndp->ndev);
715
716 return 0;
717}
718
566static const struct genl_ops ncsi_ops[] = { 719static const struct genl_ops ncsi_ops[] = {
567 { 720 {
568 .cmd = NCSI_CMD_PKG_INFO, 721 .cmd = NCSI_CMD_PKG_INFO,
@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = {
589 .doit = ncsi_send_cmd_nl, 742 .doit = ncsi_send_cmd_nl,
590 .flags = GENL_ADMIN_PERM, 743 .flags = GENL_ADMIN_PERM,
591 }, 744 },
745 {
746 .cmd = NCSI_CMD_SET_PACKAGE_MASK,
747 .policy = ncsi_genl_policy,
748 .doit = ncsi_set_package_mask_nl,
749 .flags = GENL_ADMIN_PERM,
750 },
751 {
752 .cmd = NCSI_CMD_SET_CHANNEL_MASK,
753 .policy = ncsi_genl_policy,
754 .doit = ncsi_set_channel_mask_nl,
755 .flags = GENL_ADMIN_PERM,
756 },
592}; 757};
593 758
594static struct genl_family ncsi_genl_family __ro_after_init = { 759static struct genl_family ncsi_genl_family __ro_after_init = {
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index 77e07ba3f493..de7737a27889 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -256,7 +256,7 @@ static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr)
256 if (!ncm->enable) 256 if (!ncm->enable)
257 return 0; 257 return 0;
258 258
259 ncm->enable = 1; 259 ncm->enable = 0;
260 return 0; 260 return 0;
261} 261}
262 262