summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch1425
1 files changed, 0 insertions, 1425 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch
deleted file mode 100644
index 83717369c..000000000
--- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch
+++ /dev/null
@@ -1,1425 +0,0 @@
-From 58c3299017c5e6022fb2a2a74b662b2a4c0306f5 Mon Sep 17 00:00:00 2001
-From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
-Date: Tue, 20 Nov 2018 10:14:47 -0800
-Subject: [PATCH] net/ncsi: backport ncsi patches
-
-net/ncsi: Allow enabling multiple packages & channels
-
-This series extends the NCSI driver to configure multiple packages
-and/or channels simultaneously. Since the RFC series this includes a few
-extra changes to fix areas in the driver that either made this harder or
-were roadblocks due to deviations from the NCSI specification.
-
-Patches 1 & 2 fix two issues where the driver made assumptions about the
-capabilities of the NCSI topology.
-Patches 3 & 4 change some internal semantics slightly to make multi-mode
-easier.
-Patch 5 introduces a cleaner way of reconfiguring the NCSI configuration
-and keeping track of channel states.
-Patch 6 implements the main multi-package/multi-channel configuration,
-configured via the Netlink interface.
-
-Readers who have an interesting NCSI setup - especially multi-package
-with HWA - please test! I think I've covered all permutations but I
-don't have infinite hardware to test on.
-
-net/ncsi: Don't enable all channels when HWA available
-
-NCSI hardware arbitration allows multiple packages to be enabled at once
-and share the same wiring. If the NCSI driver recognises that HWA is
-available it unconditionally enables all packages and channels; but that
-is a configuration decision rather than something required by HWA.
-Additionally the current implementation will not failover on link events
-which can cause connectivity to be lost unless the interface is manually
-bounced.
-
-Retain basic HWA support but remove the separate configuration path to
-enable all channels, leaving this to be handled by a later
-implementation.
-
-net/ncsi: Probe single packages to avoid conflict
-
-Currently the NCSI driver sends a select-package command to all possible
-packages simultaneously to discover what packages are available. However
-at this stage in the probe process the driver does not know if
-hardware arbitration is available: if it isn't then this process could
-cause collisions on the RMII bus when packages try to respond.
-
-Update the probe loop to probe each package one by one, and once
-complete check if HWA is universally supported.
-
-net/ncsi: Don't deselect package in suspend if active
-
-When a package is deselected all channels of that package cease
-communication. If there are other channels active on the package of the
-suspended channel this will disable them as well, so only send a
-deselect-package command if no other channels are active.
-
-net/ncsi: Don't mark configured channels inactive
-
-The concepts of a channel being 'active' and it having link are slightly
-muddled in the NCSI driver. Tweak this slightly so that
-NCSI_CHANNEL_ACTIVE represents a channel that has been configured and
-enabled, and NCSI_CHANNEL_INACTIVE represents a de-configured channel.
-This distinction is important because a channel can be 'active' but have
-its link down; in this case the channel may still need to be configured
-so that it may receive AEN link-state-change packets.
-
-net/ncsi: Reset channel state in ncsi_start_dev()
-
-When the NCSI driver is stopped with ncsi_stop_dev() the channel
-monitors are stopped and the state set to "inactive". However the
-channels are still configured and active from the perspective of the
-network controller. We should suspend each active channel but in the
-context of ncsi_stop_dev() the transmit queue has been or is about to be
-stopped so we won't have time to do so.
-
-Instead when ncsi_start_dev() is called if the NCSI topology has already
-been probed then call ncsi_reset_dev() to suspend any channels that were
-previously active. This resets the network controller to a known state,
-provides an up to date view of channel link state, and makes sure that
-mode flags such as NCSI_MODE_TX_ENABLE are properly reset.
-
-In addition to ncsi_start_dev() use ncsi_reset_dev() in ncsi-netlink.c
-to update the channel configuration more cleanly.
-
-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: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
----
- include/uapi/linux/ncsi.h | 15 ++
- net/ncsi/internal.h | 19 +-
- net/ncsi/ncsi-aen.c | 75 +++++--
- net/ncsi/ncsi-manage.c | 522 ++++++++++++++++++++++++++++++++--------------
- net/ncsi/ncsi-netlink.c | 233 ++++++++++++++++++---
- net/ncsi/ncsi-rsp.c | 2 +-
- 6 files changed, 660 insertions(+), 206 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 @@
- * @NCSI_CMD_SEND_CMD: send NC-SI command to network card.
- * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID
- * and NCSI_ATTR_CHANNEL_ID.
-+ * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages.
-+ * Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK.
-+ * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels.
-+ * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and
-+ * NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets
-+ * the primary channel.
- * @NCSI_CMD_MAX: highest command number
- */
- enum ncsi_nl_commands {
-@@ -34,6 +40,8 @@ enum ncsi_nl_commands {
- NCSI_CMD_SET_INTERFACE,
- NCSI_CMD_CLEAR_INTERFACE,
- NCSI_CMD_SEND_CMD,
-+ NCSI_CMD_SET_PACKAGE_MASK,
-+ NCSI_CMD_SET_CHANNEL_MASK,
-
- __NCSI_CMD_AFTER_LAST,
- NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
-@@ -48,6 +56,10 @@ enum ncsi_nl_commands {
- * @NCSI_ATTR_PACKAGE_ID: package ID
- * @NCSI_ATTR_CHANNEL_ID: channel ID
- * @NCSI_ATTR_DATA: command payload
-+ * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with
-+ * NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK.
-+ * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages.
-+ * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels.
- * @NCSI_ATTR_MAX: highest attribute number
- */
- enum ncsi_nl_attrs {
-@@ -57,6 +69,9 @@ enum ncsi_nl_attrs {
- NCSI_ATTR_PACKAGE_ID,
- NCSI_ATTR_CHANNEL_ID,
- NCSI_ATTR_DATA,
-+ NCSI_ATTR_MULTI_FLAG,
-+ NCSI_ATTR_PACKAGE_MASK,
-+ NCSI_ATTR_CHANNEL_MASK,
-
- __NCSI_ATTR_AFTER_LAST,
- NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
-diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
-index 1dae77c54009..9e3642b802c4 100644
---- a/net/ncsi/internal.h
-+++ b/net/ncsi/internal.h
-@@ -222,6 +222,10 @@ struct ncsi_package {
- unsigned int channel_num; /* Number of channels */
- struct list_head channels; /* List of chanels */
- struct list_head node; /* Form list of packages */
-+
-+ bool multi_channel; /* Enable multiple channels */
-+ u32 channel_whitelist; /* Channels to configure */
-+ struct ncsi_channel *preferred_channel; /* Primary channel */
- };
-
- struct ncsi_request {
-@@ -287,16 +291,16 @@ struct ncsi_dev_priv {
- #define NCSI_DEV_PROBED 1 /* Finalized NCSI topology */
- #define NCSI_DEV_HWA 2 /* Enabled HW arbitration */
- #define NCSI_DEV_RESHUFFLE 4
-+#define NCSI_DEV_RESET 8 /* Reset state of NC */
- unsigned int gma_flag; /* OEM GMA flag */
- spinlock_t lock; /* Protect the NCSI device */
- #if IS_ENABLED(CONFIG_IPV6)
- unsigned int inet6_addr_num; /* Number of IPv6 addresses */
- #endif
-+ unsigned int package_probe_id;/* Current ID during probe */
- unsigned int package_num; /* Number of packages */
- struct list_head packages; /* List of packages */
- struct ncsi_channel *hot_channel; /* Channel was ever active */
-- struct ncsi_package *force_package; /* Force a specific package */
-- struct ncsi_channel *force_channel; /* Force a specific channel */
- struct ncsi_request requests[256]; /* Request table */
- unsigned int request_id; /* Last used request ID */
- #define NCSI_REQ_START_IDX 1
-@@ -309,6 +313,9 @@ struct ncsi_dev_priv {
- struct list_head node; /* Form NCSI device list */
- #define NCSI_MAX_VLAN_VIDS 15
- struct list_head vlan_vids; /* List of active VLAN IDs */
-+
-+ bool multi_package; /* Enable multiple packages */
-+ u32 package_whitelist; /* Packages to configure */
- };
-
- struct ncsi_cmd_arg {
-@@ -341,6 +348,7 @@ extern spinlock_t ncsi_dev_lock;
- list_for_each_entry_rcu(nc, &np->channels, node)
-
- /* Resources */
-+int ncsi_reset_dev(struct ncsi_dev *nd);
- void ncsi_start_channel_monitor(struct ncsi_channel *nc);
- void ncsi_stop_channel_monitor(struct ncsi_channel *nc);
- struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
-@@ -361,6 +369,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
- void ncsi_free_request(struct ncsi_request *nr);
- struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
- int ncsi_process_next_channel(struct ncsi_dev_priv *ndp);
-+bool ncsi_channel_has_link(struct ncsi_channel *channel);
-+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
-+ struct ncsi_channel *channel);
-+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
-+ struct ncsi_package *np,
-+ struct ncsi_channel *disable,
-+ struct ncsi_channel *enable);
-
- /* Packet handlers */
- u32 ncsi_calculate_checksum(unsigned char *data, int len);
-diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
-index 25e483e8278b..26d67e27551f 100644
---- a/net/ncsi/ncsi-aen.c
-+++ b/net/ncsi/ncsi-aen.c
-@@ -50,13 +50,15 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
- static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
- struct ncsi_aen_pkt_hdr *h)
- {
-- struct ncsi_aen_lsc_pkt *lsc;
-- struct ncsi_channel *nc;
-+ struct ncsi_channel *nc, *tmp;
- struct ncsi_channel_mode *ncm;
-- bool chained;
-- int state;
- unsigned long old_data, data;
-+ struct ncsi_aen_lsc_pkt *lsc;
-+ struct ncsi_package *np;
-+ bool had_link, has_link;
- unsigned long flags;
-+ bool chained;
-+ int state;
-
- /* Find the NCSI channel */
- ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
-@@ -73,6 +75,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
- ncm->data[2] = data;
- ncm->data[4] = ntohl(lsc->oem_status);
-
-+ had_link = !!(old_data & 0x1);
-+ has_link = !!(data & 0x1);
-+
- netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
- nc->id, data & 0x1 ? "up" : "down");
-
-@@ -80,22 +85,60 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
- state = nc->state;
- spin_unlock_irqrestore(&nc->lock, flags);
-
-- if (!((old_data ^ data) & 0x1) || chained)
-- return 0;
-- if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) &&
-- !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1)))
-+ if (state == NCSI_CHANNEL_INACTIVE)
-+ netdev_warn(ndp->ndev.dev,
-+ "NCSI: Inactive channel %u received AEN!\n",
-+ nc->id);
-+
-+ if ((had_link == has_link) || chained)
- return 0;
-
-- if (!(ndp->flags & NCSI_DEV_HWA) &&
-- state == NCSI_CHANNEL_ACTIVE)
-- ndp->flags |= NCSI_DEV_RESHUFFLE;
-+ if (!ndp->multi_package && !nc->package->multi_channel) {
-+ if (had_link) {
-+ ndp->flags |= NCSI_DEV_RESHUFFLE;
-+ ncsi_stop_channel_monitor(nc);
-+ spin_lock_irqsave(&ndp->lock, flags);
-+ list_add_tail_rcu(&nc->link, &ndp->channel_queue);
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+ return ncsi_process_next_channel(ndp);
-+ }
-+ /* Configured channel came up */
-+ return 0;
-+ }
-
-- ncsi_stop_channel_monitor(nc);
-- spin_lock_irqsave(&ndp->lock, flags);
-- list_add_tail_rcu(&nc->link, &ndp->channel_queue);
-- spin_unlock_irqrestore(&ndp->lock, flags);
-+ if (had_link) {
-+ ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
-+ if (ncsi_channel_is_last(ndp, nc)) {
-+ /* No channels left, reconfigure */
-+ return ncsi_reset_dev(&ndp->ndev);
-+ } else if (ncm->enable) {
-+ /* Need to failover Tx channel */
-+ ncsi_update_tx_channel(ndp, nc->package, nc, NULL);
-+ }
-+ } else if (has_link && nc->package->preferred_channel == nc) {
-+ /* Return Tx to preferred channel */
-+ ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
-+ } else if (has_link) {
-+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
-+ NCSI_FOR_EACH_CHANNEL(np, tmp) {
-+ /* Enable Tx on this channel if the current Tx
-+ * channel is down.
-+ */
-+ ncm = &tmp->modes[NCSI_MODE_TX_ENABLE];
-+ if (ncm->enable &&
-+ !ncsi_channel_has_link(tmp)) {
-+ ncsi_update_tx_channel(ndp, nc->package,
-+ tmp, nc);
-+ break;
-+ }
-+ }
-+ }
-+ }
-
-- return ncsi_process_next_channel(ndp);
-+ /* Leave configured channels active in a multi-channel scenario so
-+ * AEN events are still received.
-+ */
-+ return 0;
- }
-
- 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 bfc43b28c7a6..92e59f07f9a7 100644
---- a/net/ncsi/ncsi-manage.c
-+++ b/net/ncsi/ncsi-manage.c
-@@ -28,6 +28,29 @@
- LIST_HEAD(ncsi_dev_list);
- DEFINE_SPINLOCK(ncsi_dev_lock);
-
-+bool ncsi_channel_has_link(struct ncsi_channel *channel)
-+{
-+ return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
-+}
-+
-+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
-+ struct ncsi_channel *channel)
-+{
-+ struct ncsi_package *np;
-+ struct ncsi_channel *nc;
-+
-+ NCSI_FOR_EACH_PACKAGE(ndp, np)
-+ NCSI_FOR_EACH_CHANNEL(np, nc) {
-+ if (nc == channel)
-+ continue;
-+ if (nc->state == NCSI_CHANNEL_ACTIVE &&
-+ ncsi_channel_has_link(nc))
-+ return false;
-+ }
-+
-+ return true;
-+}
-+
- static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
- {
- struct ncsi_dev *nd = &ndp->ndev;
-@@ -52,7 +75,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
- continue;
- }
-
-- if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
-+ if (ncsi_channel_has_link(nc)) {
- spin_unlock_irqrestore(&nc->lock, flags);
- nd->link_up = 1;
- goto report;
-@@ -113,10 +136,8 @@ static void ncsi_channel_monitor(struct timer_list *t)
- default:
- netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
- nc->id);
-- if (!(ndp->flags & NCSI_DEV_HWA)) {
-- ncsi_report_link(ndp, true);
-- ndp->flags |= NCSI_DEV_RESHUFFLE;
-- }
-+ ncsi_report_link(ndp, true);
-+ ndp->flags |= NCSI_DEV_RESHUFFLE;
-
- ncsi_stop_channel_monitor(nc);
-
-@@ -269,6 +290,7 @@ struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
- np->ndp = ndp;
- spin_lock_init(&np->lock);
- INIT_LIST_HEAD(&np->channels);
-+ np->channel_whitelist = UINT_MAX;
-
- spin_lock_irqsave(&ndp->lock, flags);
- tmp = ncsi_find_package(ndp, id);
-@@ -442,12 +464,14 @@ static void ncsi_request_timeout(struct timer_list *t)
- static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
- {
- struct ncsi_dev *nd = &ndp->ndev;
-- struct ncsi_package *np = ndp->active_package;
-- struct ncsi_channel *nc = ndp->active_channel;
-+ struct ncsi_package *np;
-+ struct ncsi_channel *nc, *tmp;
- struct ncsi_cmd_arg nca;
- unsigned long flags;
- int ret;
-
-+ np = ndp->active_package;
-+ nc = ndp->active_channel;
- nca.ndp = ndp;
- nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
- switch (nd->state) {
-@@ -523,6 +547,15 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
- if (ret)
- goto error;
-
-+ NCSI_FOR_EACH_CHANNEL(np, tmp) {
-+ /* If there is another channel active on this package
-+ * do not deselect the package.
-+ */
-+ if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
-+ nd->state = ncsi_dev_state_suspend_done;
-+ break;
-+ }
-+ }
- break;
- case ncsi_dev_state_suspend_deselect:
- ndp->pending_req_num = 1;
-@@ -541,8 +574,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
- spin_lock_irqsave(&nc->lock, flags);
- nc->state = NCSI_CHANNEL_INACTIVE;
- spin_unlock_irqrestore(&nc->lock, flags);
-- ncsi_process_next_channel(ndp);
--
-+ if (ndp->flags & NCSI_DEV_RESET)
-+ ncsi_reset_dev(nd);
-+ else
-+ ncsi_process_next_channel(ndp);
- break;
- default:
- netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
-@@ -717,13 +752,144 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
-
- #endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
-
-+/* Determine if a given channel from the channel_queue should be used for Tx */
-+static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
-+ struct ncsi_channel *nc)
-+{
-+ struct ncsi_channel_mode *ncm;
-+ struct ncsi_channel *channel;
-+ struct ncsi_package *np;
-+
-+ /* Check if any other channel has Tx enabled; a channel may have already
-+ * been configured and removed from the channel queue.
-+ */
-+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
-+ if (!ndp->multi_package && np != nc->package)
-+ continue;
-+ NCSI_FOR_EACH_CHANNEL(np, channel) {
-+ ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
-+ if (ncm->enable)
-+ return false;
-+ }
-+ }
-+
-+ /* This channel is the preferred channel and has link */
-+ list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
-+ np = channel->package;
-+ if (np->preferred_channel &&
-+ ncsi_channel_has_link(np->preferred_channel)) {
-+ return np->preferred_channel == nc;
-+ }
-+ }
-+
-+ /* This channel has link */
-+ if (ncsi_channel_has_link(nc))
-+ return true;
-+
-+ list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
-+ if (ncsi_channel_has_link(channel))
-+ return false;
-+
-+ /* No other channel has link; default to this one */
-+ return true;
-+}
-+
-+/* Change the active Tx channel in a multi-channel setup */
-+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
-+ struct ncsi_package *package,
-+ struct ncsi_channel *disable,
-+ struct ncsi_channel *enable)
-+{
-+ struct ncsi_cmd_arg nca;
-+ struct ncsi_channel *nc;
-+ struct ncsi_package *np;
-+ int ret = 0;
-+
-+ if (!package->multi_channel && !ndp->multi_package)
-+ netdev_warn(ndp->ndev.dev,
-+ "NCSI: Trying to update Tx channel in single-channel mode\n");
-+ nca.ndp = ndp;
-+ nca.req_flags = 0;
-+
-+ /* Find current channel with Tx enabled */
-+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
-+ if (disable)
-+ break;
-+ if (!ndp->multi_package && np != package)
-+ continue;
-+
-+ NCSI_FOR_EACH_CHANNEL(np, nc)
-+ if (nc->modes[NCSI_MODE_TX_ENABLE].enable) {
-+ disable = nc;
-+ break;
-+ }
-+ }
-+
-+ /* Find a suitable channel for Tx */
-+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
-+ if (enable)
-+ break;
-+ if (!ndp->multi_package && np != package)
-+ continue;
-+ if (!(ndp->package_whitelist & (0x1 << np->id)))
-+ continue;
-+
-+ if (np->preferred_channel &&
-+ ncsi_channel_has_link(np->preferred_channel)) {
-+ enable = np->preferred_channel;
-+ break;
-+ }
-+
-+ NCSI_FOR_EACH_CHANNEL(np, nc) {
-+ if (!(np->channel_whitelist & 0x1 << nc->id))
-+ continue;
-+ if (nc->state != NCSI_CHANNEL_ACTIVE)
-+ continue;
-+ if (ncsi_channel_has_link(nc)) {
-+ enable = nc;
-+ break;
-+ }
-+ }
-+ }
-+
-+ if (disable == enable)
-+ return -1;
-+
-+ if (!enable)
-+ return -1;
-+
-+ if (disable) {
-+ nca.channel = disable->id;
-+ nca.package = disable->package->id;
-+ nca.type = NCSI_PKT_CMD_DCNT;
-+ ret = ncsi_xmit_cmd(&nca);
-+ if (ret)
-+ netdev_err(ndp->ndev.dev,
-+ "Error %d sending DCNT\n",
-+ ret);
-+ }
-+
-+ netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
-+
-+ nca.channel = enable->id;
-+ nca.package = enable->package->id;
-+ nca.type = NCSI_PKT_CMD_ECNT;
-+ ret = ncsi_xmit_cmd(&nca);
-+ if (ret)
-+ netdev_err(ndp->ndev.dev,
-+ "Error %d sending ECNT\n",
-+ ret);
-+
-+ return ret;
-+}
-+
- static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
- {
-- struct ncsi_dev *nd = &ndp->ndev;
-- struct net_device *dev = nd->dev;
- struct ncsi_package *np = ndp->active_package;
- struct ncsi_channel *nc = ndp->active_channel;
- struct ncsi_channel *hot_nc = NULL;
-+ struct ncsi_dev *nd = &ndp->ndev;
-+ struct net_device *dev = nd->dev;
- struct ncsi_cmd_arg nca;
- unsigned char index;
- unsigned long flags;
-@@ -845,20 +1011,29 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
- } else if (nd->state == ncsi_dev_state_config_ebf) {
- nca.type = NCSI_PKT_CMD_EBF;
- nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
-- nd->state = ncsi_dev_state_config_ecnt;
-+ if (ncsi_channel_is_tx(ndp, nc))
-+ nd->state = ncsi_dev_state_config_ecnt;
-+ else
-+ nd->state = ncsi_dev_state_config_ec;
- #if IS_ENABLED(CONFIG_IPV6)
- if (ndp->inet6_addr_num > 0 &&
- (nc->caps[NCSI_CAP_GENERIC].cap &
- NCSI_CAP_GENERIC_MC))
- nd->state = ncsi_dev_state_config_egmf;
-- else
-- nd->state = ncsi_dev_state_config_ecnt;
- } else if (nd->state == ncsi_dev_state_config_egmf) {
- nca.type = NCSI_PKT_CMD_EGMF;
- nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
-- nd->state = ncsi_dev_state_config_ecnt;
-+ if (ncsi_channel_is_tx(ndp, nc))
-+ nd->state = ncsi_dev_state_config_ecnt;
-+ else
-+ nd->state = ncsi_dev_state_config_ec;
- #endif /* CONFIG_IPV6 */
- } else if (nd->state == ncsi_dev_state_config_ecnt) {
-+ if (np->preferred_channel &&
-+ nc != np->preferred_channel)
-+ netdev_info(ndp->ndev.dev,
-+ "NCSI: Tx failed over to channel %u\n",
-+ nc->id);
- nca.type = NCSI_PKT_CMD_ECNT;
- nd->state = ncsi_dev_state_config_ec;
- } else if (nd->state == ncsi_dev_state_config_ec) {
-@@ -889,6 +1064,16 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
- netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
- nc->id);
- spin_lock_irqsave(&nc->lock, flags);
-+ nc->state = NCSI_CHANNEL_ACTIVE;
-+
-+ if (ndp->flags & NCSI_DEV_RESET) {
-+ /* A reset event happened during config, start it now */
-+ nc->reconfigure_needed = false;
-+ spin_unlock_irqrestore(&nc->lock, flags);
-+ ncsi_reset_dev(nd);
-+ break;
-+ }
-+
- if (nc->reconfigure_needed) {
- /* This channel's configuration has been updated
- * part-way during the config state - start the
-@@ -909,10 +1094,8 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
-
- if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
- hot_nc = nc;
-- nc->state = NCSI_CHANNEL_ACTIVE;
- } else {
- hot_nc = NULL;
-- nc->state = NCSI_CHANNEL_INACTIVE;
- netdev_dbg(ndp->ndev.dev,
- "NCSI: channel %u link down after config\n",
- nc->id);
-@@ -940,43 +1123,35 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
-
- static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
- {
-- struct ncsi_package *np, *force_package;
-- struct ncsi_channel *nc, *found, *hot_nc, *force_channel;
-+ struct ncsi_channel *nc, *found, *hot_nc;
- struct ncsi_channel_mode *ncm;
-- unsigned long flags;
-+ unsigned long flags, cflags;
-+ struct ncsi_package *np;
-+ bool with_link;
-
- spin_lock_irqsave(&ndp->lock, flags);
- hot_nc = ndp->hot_channel;
-- force_channel = ndp->force_channel;
-- force_package = ndp->force_package;
- spin_unlock_irqrestore(&ndp->lock, flags);
-
-- /* Force a specific channel whether or not it has link if we have been
-- * configured to do so
-- */
-- if (force_package && force_channel) {
-- found = force_channel;
-- ncm = &found->modes[NCSI_MODE_LINK];
-- if (!(ncm->data[2] & 0x1))
-- netdev_info(ndp->ndev.dev,
-- "NCSI: Channel %u forced, but it is link down\n",
-- found->id);
-- goto out;
-- }
--
-- /* The search is done once an inactive channel with up
-- * link is found.
-+ /* By default the search is done once an inactive channel with up
-+ * link is found, unless a preferred channel is set.
-+ * If multi_package or multi_channel are configured all channels in the
-+ * whitelist are added to the channel queue.
- */
- found = NULL;
-+ with_link = false;
- NCSI_FOR_EACH_PACKAGE(ndp, np) {
-- if (ndp->force_package && np != ndp->force_package)
-+ if (!(ndp->package_whitelist & (0x1 << np->id)))
- continue;
- NCSI_FOR_EACH_CHANNEL(np, nc) {
-- spin_lock_irqsave(&nc->lock, flags);
-+ if (!(np->channel_whitelist & (0x1 << nc->id)))
-+ continue;
-+
-+ spin_lock_irqsave(&nc->lock, cflags);
-
- if (!list_empty(&nc->link) ||
- nc->state != NCSI_CHANNEL_INACTIVE) {
-- spin_unlock_irqrestore(&nc->lock, flags);
-+ spin_unlock_irqrestore(&nc->lock, cflags);
- continue;
- }
-
-@@ -988,32 +1163,49 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
-
- ncm = &nc->modes[NCSI_MODE_LINK];
- if (ncm->data[2] & 0x1) {
-- spin_unlock_irqrestore(&nc->lock, flags);
- found = nc;
-- goto out;
-+ with_link = true;
- }
-
-- spin_unlock_irqrestore(&nc->lock, flags);
-+ /* If multi_channel is enabled configure all valid
-+ * channels whether or not they currently have link
-+ * so they will have AENs enabled.
-+ */
-+ if (with_link || np->multi_channel) {
-+ spin_lock_irqsave(&ndp->lock, flags);
-+ list_add_tail_rcu(&nc->link,
-+ &ndp->channel_queue);
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+
-+ netdev_dbg(ndp->ndev.dev,
-+ "NCSI: Channel %u added to queue (link %s)\n",
-+ nc->id,
-+ ncm->data[2] & 0x1 ? "up" : "down");
-+ }
-+
-+ spin_unlock_irqrestore(&nc->lock, cflags);
-+
-+ if (with_link && !np->multi_channel)
-+ break;
- }
-+ if (with_link && !ndp->multi_package)
-+ break;
- }
-
-- if (!found) {
-+ if (list_empty(&ndp->channel_queue) && found) {
-+ netdev_info(ndp->ndev.dev,
-+ "NCSI: No channel with link found, configuring channel %u\n",
-+ found->id);
-+ spin_lock_irqsave(&ndp->lock, flags);
-+ list_add_tail_rcu(&found->link, &ndp->channel_queue);
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+ } else if (!found) {
- netdev_warn(ndp->ndev.dev,
-- "NCSI: No channel found with link\n");
-+ "NCSI: No channel found to configure!\n");
- ncsi_report_link(ndp, true);
- return -ENODEV;
- }
-
-- ncm = &found->modes[NCSI_MODE_LINK];
-- netdev_dbg(ndp->ndev.dev,
-- "NCSI: Channel %u added to queue (link %s)\n",
-- found->id, ncm->data[2] & 0x1 ? "up" : "down");
--
--out:
-- spin_lock_irqsave(&ndp->lock, flags);
-- list_add_tail_rcu(&found->link, &ndp->channel_queue);
-- spin_unlock_irqrestore(&ndp->lock, flags);
--
- return ncsi_process_next_channel(ndp);
- }
-
-@@ -1050,35 +1242,6 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
- return false;
- }
-
--static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp)
--{
-- struct ncsi_package *np;
-- struct ncsi_channel *nc;
-- unsigned long flags;
--
-- /* Move all available channels to processing queue */
-- spin_lock_irqsave(&ndp->lock, flags);
-- NCSI_FOR_EACH_PACKAGE(ndp, np) {
-- NCSI_FOR_EACH_CHANNEL(np, nc) {
-- WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE ||
-- !list_empty(&nc->link));
-- ncsi_stop_channel_monitor(nc);
-- list_add_tail_rcu(&nc->link, &ndp->channel_queue);
-- }
-- }
-- spin_unlock_irqrestore(&ndp->lock, flags);
--
-- /* We can have no channels in extremely case */
-- if (list_empty(&ndp->channel_queue)) {
-- netdev_err(ndp->ndev.dev,
-- "NCSI: No available channels for HWA\n");
-- ncsi_report_link(ndp, false);
-- return -ENOENT;
-- }
--
-- return ncsi_process_next_channel(ndp);
--}
--
- static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
- {
- struct ncsi_dev *nd = &ndp->ndev;
-@@ -1110,70 +1273,28 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
- nd->state = ncsi_dev_state_probe_package;
- break;
- case ncsi_dev_state_probe_package:
-- ndp->pending_req_num = 16;
-+ ndp->pending_req_num = 1;
-
-- /* Select all possible packages */
- nca.type = NCSI_PKT_CMD_SP;
- nca.bytes[0] = 1;
-+ nca.package = ndp->package_probe_id;
- nca.channel = NCSI_RESERVED_CHANNEL;
-- for (index = 0; index < 8; index++) {
-- nca.package = index;
-- ret = ncsi_xmit_cmd(&nca);
-- if (ret)
-- goto error;
-- }
--
-- /* Disable all possible packages */
-- nca.type = NCSI_PKT_CMD_DP;
-- for (index = 0; index < 8; index++) {
-- nca.package = index;
-- ret = ncsi_xmit_cmd(&nca);
-- if (ret)
-- goto error;
-- }
--
-+ ret = ncsi_xmit_cmd(&nca);
-+ if (ret)
-+ goto error;
- nd->state = ncsi_dev_state_probe_channel;
- break;
- case ncsi_dev_state_probe_channel:
-- if (!ndp->active_package)
-- ndp->active_package = list_first_or_null_rcu(
-- &ndp->packages, struct ncsi_package, node);
-- else if (list_is_last(&ndp->active_package->node,
-- &ndp->packages))
-- ndp->active_package = NULL;
-- else
-- ndp->active_package = list_next_entry(
-- ndp->active_package, node);
--
-- /* All available packages and channels are enumerated. The
-- * enumeration happens for once when the NCSI interface is
-- * started. So we need continue to start the interface after
-- * the enumeration.
-- *
-- * We have to choose an active channel before configuring it.
-- * Note that we possibly don't have active channel in extreme
-- * situation.
-- */
-+ ndp->active_package = ncsi_find_package(ndp,
-+ ndp->package_probe_id);
- if (!ndp->active_package) {
-- ndp->flags |= NCSI_DEV_PROBED;
-- if (ncsi_check_hwa(ndp))
-- ncsi_enable_hwa(ndp);
-- else
-- ncsi_choose_active_channel(ndp);
-- return;
-+ /* No response */
-+ nd->state = ncsi_dev_state_probe_dp;
-+ schedule_work(&ndp->work);
-+ break;
- }
--
-- /* Select the active package */
-- ndp->pending_req_num = 1;
-- nca.type = NCSI_PKT_CMD_SP;
-- nca.bytes[0] = 1;
-- nca.package = ndp->active_package->id;
-- nca.channel = NCSI_RESERVED_CHANNEL;
-- ret = ncsi_xmit_cmd(&nca);
-- if (ret)
-- goto error;
--
- nd->state = ncsi_dev_state_probe_cis;
-+ schedule_work(&ndp->work);
- break;
- case ncsi_dev_state_probe_cis:
- ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
-@@ -1222,22 +1343,35 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
- case ncsi_dev_state_probe_dp:
- ndp->pending_req_num = 1;
-
-- /* Deselect the active package */
-+ /* Deselect the current package */
- nca.type = NCSI_PKT_CMD_DP;
-- nca.package = ndp->active_package->id;
-+ nca.package = ndp->package_probe_id;
- nca.channel = NCSI_RESERVED_CHANNEL;
- ret = ncsi_xmit_cmd(&nca);
- if (ret)
- goto error;
-
-- /* Scan channels in next package */
-- nd->state = ncsi_dev_state_probe_channel;
-+ /* Probe next package */
-+ ndp->package_probe_id++;
-+ if (ndp->package_probe_id >= 8) {
-+ /* Probe finished */
-+ ndp->flags |= NCSI_DEV_PROBED;
-+ break;
-+ }
-+ nd->state = ncsi_dev_state_probe_package;
-+ ndp->active_package = NULL;
- break;
- default:
- netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
- nd->state);
- }
-
-+ if (ndp->flags & NCSI_DEV_PROBED) {
-+ /* Check if all packages have HWA support */
-+ ncsi_check_hwa(ndp);
-+ ncsi_choose_active_channel(ndp);
-+ }
-+
- return;
- error:
- netdev_err(ndp->ndev.dev,
-@@ -1556,6 +1690,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
- INIT_LIST_HEAD(&ndp->channel_queue);
- INIT_LIST_HEAD(&ndp->vlan_vids);
- INIT_WORK(&ndp->work, ncsi_dev_work);
-+ ndp->package_whitelist = UINT_MAX;
-
- /* Initialize private NCSI device */
- spin_lock_init(&ndp->lock);
-@@ -1592,26 +1727,19 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev);
- int ncsi_start_dev(struct ncsi_dev *nd)
- {
- struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
-- int ret;
-
- if (nd->state != ncsi_dev_state_registered &&
- nd->state != ncsi_dev_state_functional)
- return -ENOTTY;
-
- if (!(ndp->flags & NCSI_DEV_PROBED)) {
-+ ndp->package_probe_id = 0;
- nd->state = ncsi_dev_state_probe;
- schedule_work(&ndp->work);
- return 0;
- }
-
-- if (ndp->flags & NCSI_DEV_HWA) {
-- netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n");
-- ret = ncsi_enable_hwa(ndp);
-- } else {
-- ret = ncsi_choose_active_channel(ndp);
-- }
--
-- return ret;
-+ return ncsi_reset_dev(nd);
- }
- EXPORT_SYMBOL_GPL(ncsi_start_dev);
-
-@@ -1624,7 +1752,10 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
- int old_state;
- unsigned long flags;
-
-- /* Stop the channel monitor and reset channel's state */
-+ /* Stop the channel monitor on any active channels. Don't reset the
-+ * channel state so we know which were active when ncsi_start_dev()
-+ * is next called.
-+ */
- NCSI_FOR_EACH_PACKAGE(ndp, np) {
- NCSI_FOR_EACH_CHANNEL(np, nc) {
- ncsi_stop_channel_monitor(nc);
-@@ -1632,7 +1763,6 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
- spin_lock_irqsave(&nc->lock, flags);
- chained = !list_empty(&nc->link);
- old_state = nc->state;
-- nc->state = NCSI_CHANNEL_INACTIVE;
- spin_unlock_irqrestore(&nc->lock, flags);
-
- WARN_ON_ONCE(chained ||
-@@ -1645,6 +1775,92 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
- }
- EXPORT_SYMBOL_GPL(ncsi_stop_dev);
-
-+int ncsi_reset_dev(struct ncsi_dev *nd)
-+{
-+ struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
-+ struct ncsi_channel *nc, *active, *tmp;
-+ struct ncsi_package *np;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&ndp->lock, flags);
-+
-+ if (!(ndp->flags & NCSI_DEV_RESET)) {
-+ /* Haven't been called yet, check states */
-+ switch (nd->state & ncsi_dev_state_major) {
-+ case ncsi_dev_state_registered:
-+ case ncsi_dev_state_probe:
-+ /* Not even probed yet - do nothing */
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+ return 0;
-+ case ncsi_dev_state_suspend:
-+ case ncsi_dev_state_config:
-+ /* Wait for the channel to finish its suspend/config
-+ * operation; once it finishes it will check for
-+ * NCSI_DEV_RESET and reset the state.
-+ */
-+ ndp->flags |= NCSI_DEV_RESET;
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+ return 0;
-+ }
-+ } else {
-+ switch (nd->state) {
-+ case ncsi_dev_state_suspend_done:
-+ case ncsi_dev_state_config_done:
-+ case ncsi_dev_state_functional:
-+ /* Ok */
-+ break;
-+ default:
-+ /* Current reset operation happening */
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+ return 0;
-+ }
-+ }
-+
-+ if (!list_empty(&ndp->channel_queue)) {
-+ /* Clear any channel queue we may have interrupted */
-+ list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
-+ list_del_init(&nc->link);
-+ }
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+
-+ active = NULL;
-+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
-+ NCSI_FOR_EACH_CHANNEL(np, nc) {
-+ spin_lock_irqsave(&nc->lock, flags);
-+
-+ if (nc->state == NCSI_CHANNEL_ACTIVE) {
-+ active = nc;
-+ nc->state = NCSI_CHANNEL_INVISIBLE;
-+ spin_unlock_irqrestore(&nc->lock, flags);
-+ ncsi_stop_channel_monitor(nc);
-+ break;
-+ }
-+
-+ spin_unlock_irqrestore(&nc->lock, flags);
-+ }
-+ if (active)
-+ break;
-+ }
-+
-+ if (!active) {
-+ /* Done */
-+ spin_lock_irqsave(&ndp->lock, flags);
-+ ndp->flags &= ~NCSI_DEV_RESET;
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+ return ncsi_choose_active_channel(ndp);
-+ }
-+
-+ spin_lock_irqsave(&ndp->lock, flags);
-+ ndp->flags |= NCSI_DEV_RESET;
-+ ndp->active_channel = active;
-+ ndp->active_package = active->package;
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+
-+ nd->state = ncsi_dev_state_suspend;
-+ schedule_work(&ndp->work);
-+ return 0;
-+}
-+
- void ncsi_unregister_dev(struct ncsi_dev *nd)
- {
- struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
-diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c
-index 33314381b4f5..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] = {
- [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 },
- [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 },
- [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 },
-+ [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG },
-+ [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 },
-+ [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 },
- };
-
- 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,
- nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
- if (nc->state == NCSI_CHANNEL_ACTIVE)
- nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
-- if (ndp->force_channel == nc)
-+ if (nc == nc->package->preferred_channel)
- nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
-
- 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,
- if (!pnest)
- return -ENOMEM;
- nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
-- if (ndp->force_package == np)
-+ if ((0x1 << np->id) == ndp->package_whitelist)
- nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
- cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
- if (!cnest) {
-@@ -290,49 +293,58 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
- package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
- package = NULL;
-
-- spin_lock_irqsave(&ndp->lock, flags);
--
- NCSI_FOR_EACH_PACKAGE(ndp, np)
- if (np->id == package_id)
- package = np;
- if (!package) {
- /* The user has set a package that does not exist */
-- spin_unlock_irqrestore(&ndp->lock, flags);
- return -ERANGE;
- }
-
- channel = NULL;
-- if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
-- /* Allow any channel */
-- channel_id = NCSI_RESERVED_CHANNEL;
-- } else {
-+ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
- channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
- NCSI_FOR_EACH_CHANNEL(package, nc)
-- if (nc->id == channel_id)
-+ if (nc->id == channel_id) {
- channel = nc;
-+ break;
-+ }
-+ if (!channel) {
-+ netdev_info(ndp->ndev.dev,
-+ "NCSI: Channel %u does not exist!\n",
-+ channel_id);
-+ return -ERANGE;
-+ }
- }
-
-- if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
-- /* The user has set a channel that does not exist on this
-- * package
-- */
-- spin_unlock_irqrestore(&ndp->lock, flags);
-- netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
-- channel_id);
-- return -ERANGE;
-- }
--
-- ndp->force_package = package;
-- ndp->force_channel = channel;
-+ spin_lock_irqsave(&ndp->lock, flags);
-+ ndp->package_whitelist = 0x1 << package->id;
-+ ndp->multi_package = false;
- spin_unlock_irqrestore(&ndp->lock, flags);
-
-- netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n",
-- package_id, channel_id,
-- channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : "");
-+ spin_lock_irqsave(&package->lock, flags);
-+ package->multi_channel = false;
-+ if (channel) {
-+ package->channel_whitelist = 0x1 << channel->id;
-+ package->preferred_channel = channel;
-+ } else {
-+ /* Allow any channel */
-+ package->channel_whitelist = UINT_MAX;
-+ package->preferred_channel = NULL;
-+ }
-+ spin_unlock_irqrestore(&package->lock, flags);
-+
-+ if (channel)
-+ netdev_info(ndp->ndev.dev,
-+ "Set package 0x%x, channel 0x%x as preferred\n",
-+ package_id, channel_id);
-+ else
-+ netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
-+ package_id);
-
-- /* Bounce the NCSI channel to set changes */
-- ncsi_stop_dev(&ndp->ndev);
-- ncsi_start_dev(&ndp->ndev);
-+ /* Update channel configuration */
-+ if (!(ndp->flags & NCSI_DEV_RESET))
-+ ncsi_reset_dev(&ndp->ndev);
-
- return 0;
- }
-@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
- static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
- {
- struct ncsi_dev_priv *ndp;
-+ struct ncsi_package *np;
- unsigned long flags;
-
- if (!info || !info->attrs)
-@@ -353,16 +366,24 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
- if (!ndp)
- return -ENODEV;
-
-- /* Clear any override */
-+ /* Reset any whitelists and disable multi mode */
- spin_lock_irqsave(&ndp->lock, flags);
-- ndp->force_package = NULL;
-- ndp->force_channel = NULL;
-+ ndp->package_whitelist = UINT_MAX;
-+ ndp->multi_package = false;
- spin_unlock_irqrestore(&ndp->lock, flags);
-+
-+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
-+ spin_lock_irqsave(&np->lock, flags);
-+ np->multi_channel = false;
-+ np->channel_whitelist = UINT_MAX;
-+ np->preferred_channel = NULL;
-+ spin_unlock_irqrestore(&np->lock, flags);
-+ }
- netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
-
-- /* Bounce the NCSI channel to set changes */
-- ncsi_stop_dev(&ndp->ndev);
-- ncsi_start_dev(&ndp->ndev);
-+ /* Update channel configuration */
-+ if (!(ndp->flags & NCSI_DEV_RESET))
-+ ncsi_reset_dev(&ndp->ndev);
-
- return 0;
- }
-@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev,
- return nlmsg_unicast(net->genl_sock, skb, snd_portid);
- }
-
-+static int ncsi_set_package_mask_nl(struct sk_buff *msg,
-+ struct genl_info *info)
-+{
-+ struct ncsi_dev_priv *ndp;
-+ unsigned long flags;
-+ int rc;
-+
-+ if (!info || !info->attrs)
-+ return -EINVAL;
-+
-+ if (!info->attrs[NCSI_ATTR_IFINDEX])
-+ return -EINVAL;
-+
-+ if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
-+ return -EINVAL;
-+
-+ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
-+ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
-+ if (!ndp)
-+ return -ENODEV;
-+
-+ spin_lock_irqsave(&ndp->lock, flags);
-+ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
-+ if (ndp->flags & NCSI_DEV_HWA) {
-+ ndp->multi_package = true;
-+ rc = 0;
-+ } else {
-+ netdev_err(ndp->ndev.dev,
-+ "NCSI: Can't use multiple packages without HWA\n");
-+ rc = -EPERM;
-+ }
-+ } else {
-+ ndp->multi_package = false;
-+ rc = 0;
-+ }
-+
-+ if (!rc)
-+ ndp->package_whitelist =
-+ nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
-+ spin_unlock_irqrestore(&ndp->lock, flags);
-+
-+ if (!rc) {
-+ /* Update channel configuration */
-+ if (!(ndp->flags & NCSI_DEV_RESET))
-+ ncsi_reset_dev(&ndp->ndev);
-+ }
-+
-+ return rc;
-+}
-+
-+static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
-+ struct genl_info *info)
-+{
-+ struct ncsi_package *np, *package;
-+ struct ncsi_channel *nc, *channel;
-+ u32 package_id, channel_id;
-+ struct ncsi_dev_priv *ndp;
-+ unsigned long flags;
-+
-+ if (!info || !info->attrs)
-+ return -EINVAL;
-+
-+ if (!info->attrs[NCSI_ATTR_IFINDEX])
-+ return -EINVAL;
-+
-+ if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
-+ return -EINVAL;
-+
-+ if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
-+ return -EINVAL;
-+
-+ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
-+ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
-+ if (!ndp)
-+ return -ENODEV;
-+
-+ package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
-+ package = NULL;
-+ NCSI_FOR_EACH_PACKAGE(ndp, np)
-+ if (np->id == package_id) {
-+ package = np;
-+ break;
-+ }
-+ if (!package)
-+ return -ERANGE;
-+
-+ spin_lock_irqsave(&package->lock, flags);
-+
-+ channel = NULL;
-+ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
-+ channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
-+ NCSI_FOR_EACH_CHANNEL(np, nc)
-+ if (nc->id == channel_id) {
-+ channel = nc;
-+ break;
-+ }
-+ if (!channel) {
-+ spin_unlock_irqrestore(&package->lock, flags);
-+ return -ERANGE;
-+ }
-+ netdev_dbg(ndp->ndev.dev,
-+ "NCSI: Channel %u set as preferred channel\n",
-+ channel->id);
-+ }
-+
-+ package->channel_whitelist =
-+ nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
-+ if (package->channel_whitelist == 0)
-+ netdev_dbg(ndp->ndev.dev,
-+ "NCSI: Package %u set to all channels disabled\n",
-+ package->id);
-+
-+ package->preferred_channel = channel;
-+
-+ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
-+ package->multi_channel = true;
-+ netdev_info(ndp->ndev.dev,
-+ "NCSI: Multi-channel enabled on package %u\n",
-+ package_id);
-+ } else {
-+ package->multi_channel = false;
-+ }
-+
-+ spin_unlock_irqrestore(&package->lock, flags);
-+
-+ /* Update channel configuration */
-+ if (!(ndp->flags & NCSI_DEV_RESET))
-+ ncsi_reset_dev(&ndp->ndev);
-+
-+ return 0;
-+}
-+
- static const struct genl_ops ncsi_ops[] = {
- {
- .cmd = NCSI_CMD_PKG_INFO,
-@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = {
- .doit = ncsi_send_cmd_nl,
- .flags = GENL_ADMIN_PERM,
- },
-+ {
-+ .cmd = NCSI_CMD_SET_PACKAGE_MASK,
-+ .policy = ncsi_genl_policy,
-+ .doit = ncsi_set_package_mask_nl,
-+ .flags = GENL_ADMIN_PERM,
-+ },
-+ {
-+ .cmd = NCSI_CMD_SET_CHANNEL_MASK,
-+ .policy = ncsi_genl_policy,
-+ .doit = ncsi_set_channel_mask_nl,
-+ .flags = GENL_ADMIN_PERM,
-+ },
- };
-
- 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)
- if (!ncm->enable)
- return 0;
-
-- ncm->enable = 1;
-+ ncm->enable = 0;
- return 0;
- }
-
---
-2.7.4
-