diff options
author | Samuel Mendoza-Jonas <sam@mendozajonas.com> | 2018-11-15 23:51:59 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-11-18 00:09:49 -0500 |
commit | 8d951a75d022d94a05f5fa74217670a981e8302d (patch) | |
tree | d360d9898e404a423779ba2b3ced1a5801515997 | |
parent | 2878a2cfe57a5db21844801cf502fe535a3134b2 (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.h | 15 | ||||
-rw-r--r-- | net/ncsi/internal.h | 16 | ||||
-rw-r--r-- | net/ncsi/ncsi-aen.c | 63 | ||||
-rw-r--r-- | net/ncsi/ncsi-manage.c | 264 | ||||
-rw-r--r-- | net/ncsi/ncsi-netlink.c | 221 | ||||
-rw-r--r-- | net/ncsi/ncsi-rsp.c | 2 |
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 | */ |
31 | enum ncsi_nl_commands { | 37 | enum 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 | */ |
53 | enum ncsi_nl_attrs { | 65 | enum 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 | ||
227 | struct ncsi_request { | 231 | struct 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 | ||
316 | struct ncsi_cmd_arg { | 321 | struct ncsi_cmd_arg { |
@@ -364,6 +369,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, | |||
364 | void ncsi_free_request(struct ncsi_request *nr); | 369 | void ncsi_free_request(struct ncsi_request *nr); |
365 | struct ncsi_dev *ncsi_find_dev(struct net_device *dev); | 370 | struct ncsi_dev *ncsi_find_dev(struct net_device *dev); |
366 | int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); | 371 | int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); |
372 | bool ncsi_channel_has_link(struct ncsi_channel *channel); | ||
373 | bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, | ||
374 | struct ncsi_channel *channel); | ||
375 | int 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 */ |
369 | u32 ncsi_calculate_checksum(unsigned char *data, int len); | 381 | u32 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, | |||
50 | static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, | 50 | static 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 | ||
105 | static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, | 144 | static 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 @@ | |||
28 | LIST_HEAD(ncsi_dev_list); | 28 | LIST_HEAD(ncsi_dev_list); |
29 | DEFINE_SPINLOCK(ncsi_dev_lock); | 29 | DEFINE_SPINLOCK(ncsi_dev_lock); |
30 | 30 | ||
31 | bool ncsi_channel_has_link(struct ncsi_channel *channel) | ||
32 | { | ||
33 | return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1); | ||
34 | } | ||
35 | |||
36 | bool 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 | |||
31 | static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) | 54 | static 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 */ | ||
756 | static 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 */ | ||
798 | int 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 | |||
731 | static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) | 886 | static 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 | ||
960 | static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) | 1124 | static 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 | |||
1031 | out: | ||
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 | ||
35 | static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) | 38 | static 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) | |||
340 | static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) | 352 | static 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 | ||
587 | static 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 | |||
637 | static 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 | |||
566 | static const struct genl_ops ncsi_ops[] = { | 719 | static 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 | ||
594 | static struct genl_family ncsi_genl_family __ro_after_init = { | 759 | static 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 | ||