--- /dev/null
+From 955dc68cb9b23b42999cafe6df3684309bc686c6 Mon Sep 17 00:00:00 2001
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Date: Mon, 5 Mar 2018 11:39:05 +1100
+Subject: net/ncsi: Add generic netlink family
+
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+
+commit 955dc68cb9b23b42999cafe6df3684309bc686c6 upstream.
+
+Add a generic netlink family for NCSI. This supports three commands;
+NCSI_CMD_PKG_INFO which returns information on packages and their
+associated channels, NCSI_CMD_SET_INTERFACE which allows a specific
+package or package/channel combination to be set as the preferred
+choice, and NCSI_CMD_CLEAR_INTERFACE which clears any preferred setting.
+
+Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/uapi/linux/ncsi.h | 115 ++++++++++++
+ net/ncsi/Makefile | 2
+ net/ncsi/internal.h | 3
+ net/ncsi/ncsi-manage.c | 30 ++-
+ net/ncsi/ncsi-netlink.c | 421 ++++++++++++++++++++++++++++++++++++++++++++++
+ net/ncsi/ncsi-netlink.h | 20 ++
+ 6 files changed, 586 insertions(+), 5 deletions(-)
+ create mode 100644 include/uapi/linux/ncsi.h
+ create mode 100644 net/ncsi/ncsi-netlink.c
+ create mode 100644 net/ncsi/ncsi-netlink.h
+
+--- /dev/null
++++ b/include/uapi/linux/ncsi.h
+@@ -0,0 +1,115 @@
++/*
++ * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#ifndef __UAPI_NCSI_NETLINK_H__
++#define __UAPI_NCSI_NETLINK_H__
++
++/**
++ * enum ncsi_nl_commands - supported NCSI commands
++ *
++ * @NCSI_CMD_UNSPEC: unspecified command to catch errors
++ * @NCSI_CMD_PKG_INFO: list package and channel attributes. Requires
++ * NCSI_ATTR_IFINDEX. If NCSI_ATTR_PACKAGE_ID is specified returns the
++ * specific package and its channels - otherwise a dump request returns
++ * all packages and their associated channels.
++ * @NCSI_CMD_SET_INTERFACE: set preferred package and channel combination.
++ * Requires NCSI_ATTR_IFINDEX and the preferred NCSI_ATTR_PACKAGE_ID and
++ * optionally the preferred NCSI_ATTR_CHANNEL_ID.
++ * @NCSI_CMD_CLEAR_INTERFACE: clear any preferred package/channel combination.
++ * Requires NCSI_ATTR_IFINDEX.
++ * @NCSI_CMD_MAX: highest command number
++ */
++enum ncsi_nl_commands {
++ NCSI_CMD_UNSPEC,
++ NCSI_CMD_PKG_INFO,
++ NCSI_CMD_SET_INTERFACE,
++ NCSI_CMD_CLEAR_INTERFACE,
++
++ __NCSI_CMD_AFTER_LAST,
++ NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
++};
++
++/**
++ * enum ncsi_nl_attrs - General NCSI netlink attributes
++ *
++ * @NCSI_ATTR_UNSPEC: unspecified attributes to catch errors
++ * @NCSI_ATTR_IFINDEX: ifindex of network device using NCSI
++ * @NCSI_ATTR_PACKAGE_LIST: nested array of NCSI_PKG_ATTR attributes
++ * @NCSI_ATTR_PACKAGE_ID: package ID
++ * @NCSI_ATTR_CHANNEL_ID: channel ID
++ * @NCSI_ATTR_MAX: highest attribute number
++ */
++enum ncsi_nl_attrs {
++ NCSI_ATTR_UNSPEC,
++ NCSI_ATTR_IFINDEX,
++ NCSI_ATTR_PACKAGE_LIST,
++ NCSI_ATTR_PACKAGE_ID,
++ NCSI_ATTR_CHANNEL_ID,
++
++ __NCSI_ATTR_AFTER_LAST,
++ NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
++};
++
++/**
++ * enum ncsi_nl_pkg_attrs - NCSI netlink package-specific attributes
++ *
++ * @NCSI_PKG_ATTR_UNSPEC: unspecified attributes to catch errors
++ * @NCSI_PKG_ATTR: nested array of package attributes
++ * @NCSI_PKG_ATTR_ID: package ID
++ * @NCSI_PKG_ATTR_FORCED: flag signifying a package has been set as preferred
++ * @NCSI_PKG_ATTR_CHANNEL_LIST: nested array of NCSI_CHANNEL_ATTR attributes
++ * @NCSI_PKG_ATTR_MAX: highest attribute number
++ */
++enum ncsi_nl_pkg_attrs {
++ NCSI_PKG_ATTR_UNSPEC,
++ NCSI_PKG_ATTR,
++ NCSI_PKG_ATTR_ID,
++ NCSI_PKG_ATTR_FORCED,
++ NCSI_PKG_ATTR_CHANNEL_LIST,
++
++ __NCSI_PKG_ATTR_AFTER_LAST,
++ NCSI_PKG_ATTR_MAX = __NCSI_PKG_ATTR_AFTER_LAST - 1
++};
++
++/**
++ * enum ncsi_nl_channel_attrs - NCSI netlink channel-specific attributes
++ *
++ * @NCSI_CHANNEL_ATTR_UNSPEC: unspecified attributes to catch errors
++ * @NCSI_CHANNEL_ATTR: nested array of channel attributes
++ * @NCSI_CHANNEL_ATTR_ID: channel ID
++ * @NCSI_CHANNEL_ATTR_VERSION_MAJOR: channel major version number
++ * @NCSI_CHANNEL_ATTR_VERSION_MINOR: channel minor version number
++ * @NCSI_CHANNEL_ATTR_VERSION_STR: channel version string
++ * @NCSI_CHANNEL_ATTR_LINK_STATE: channel link state flags
++ * @NCSI_CHANNEL_ATTR_ACTIVE: channels with this flag are in
++ * NCSI_CHANNEL_ACTIVE state
++ * @NCSI_CHANNEL_ATTR_FORCED: flag signifying a channel has been set as
++ * preferred
++ * @NCSI_CHANNEL_ATTR_VLAN_LIST: nested array of NCSI_CHANNEL_ATTR_VLAN_IDs
++ * @NCSI_CHANNEL_ATTR_VLAN_ID: VLAN ID being filtered on this channel
++ * @NCSI_CHANNEL_ATTR_MAX: highest attribute number
++ */
++enum ncsi_nl_channel_attrs {
++ NCSI_CHANNEL_ATTR_UNSPEC,
++ NCSI_CHANNEL_ATTR,
++ NCSI_CHANNEL_ATTR_ID,
++ NCSI_CHANNEL_ATTR_VERSION_MAJOR,
++ NCSI_CHANNEL_ATTR_VERSION_MINOR,
++ NCSI_CHANNEL_ATTR_VERSION_STR,
++ NCSI_CHANNEL_ATTR_LINK_STATE,
++ NCSI_CHANNEL_ATTR_ACTIVE,
++ NCSI_CHANNEL_ATTR_FORCED,
++ NCSI_CHANNEL_ATTR_VLAN_LIST,
++ NCSI_CHANNEL_ATTR_VLAN_ID,
++
++ __NCSI_CHANNEL_ATTR_AFTER_LAST,
++ NCSI_CHANNEL_ATTR_MAX = __NCSI_CHANNEL_ATTR_AFTER_LAST - 1
++};
++
++#endif /* __UAPI_NCSI_NETLINK_H__ */
+--- a/net/ncsi/Makefile
++++ b/net/ncsi/Makefile
+@@ -1,4 +1,4 @@
+ #
+ # Makefile for NCSI API
+ #
+-obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o
++obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o ncsi-netlink.o
+--- a/net/ncsi/internal.h
++++ b/net/ncsi/internal.h
+@@ -276,6 +276,8 @@ struct ncsi_dev_priv {
+ 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
+@@ -318,6 +320,7 @@ extern spinlock_t ncsi_dev_lock;
+ list_for_each_entry_rcu(nc, &np->channels, node)
+
+ /* Resources */
++u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index);
+ int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data);
+ int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data);
+ int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index);
+--- a/net/ncsi/ncsi-manage.c
++++ b/net/ncsi/ncsi-manage.c
+@@ -12,7 +12,6 @@
+ #include <linux/init.h>
+ #include <linux/netdevice.h>
+ #include <linux/skbuff.h>
+-#include <linux/netlink.h>
+
+ #include <net/ncsi.h>
+ #include <net/net_namespace.h>
+@@ -23,6 +22,7 @@
+
+ #include "internal.h"
+ #include "ncsi-pkt.h"
++#include "ncsi-netlink.h"
+
+ LIST_HEAD(ncsi_dev_list);
+ DEFINE_SPINLOCK(ncsi_dev_lock);
+@@ -38,7 +38,7 @@ static inline int ncsi_filter_size(int t
+ return sizes[table];
+ }
+
+-static u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
++u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
+ {
+ struct ncsi_channel_filter *ncf;
+ int size;
+@@ -972,20 +972,37 @@ error:
+
+ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
+ {
+- struct ncsi_package *np;
+- struct ncsi_channel *nc, *found, *hot_nc;
++ struct ncsi_package *np, *force_package;
++ struct ncsi_channel *nc, *found, *hot_nc, *force_channel;
+ struct ncsi_channel_mode *ncm;
+ unsigned long flags;
+
+ 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.
+ */
+ found = NULL;
+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
++ if (ndp->force_package && np != ndp->force_package)
++ continue;
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ spin_lock_irqsave(&nc->lock, flags);
+
+@@ -1603,6 +1620,9 @@ struct ncsi_dev *ncsi_register_dev(struc
+ ndp->ptype.dev = dev;
+ dev_add_pack(&ndp->ptype);
+
++ /* Set up generic netlink interface */
++ ncsi_init_netlink(dev);
++
+ return nd;
+ }
+ EXPORT_SYMBOL_GPL(ncsi_register_dev);
+@@ -1682,6 +1702,8 @@ void ncsi_unregister_dev(struct ncsi_dev
+ #endif
+ spin_unlock_irqrestore(&ncsi_dev_lock, flags);
+
++ ncsi_unregister_netlink(nd->dev);
++
+ kfree(ndp);
+ }
+ EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
+--- /dev/null
++++ b/net/ncsi/ncsi-netlink.c
+@@ -0,0 +1,421 @@
++/*
++ * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/if_arp.h>
++#include <linux/rtnetlink.h>
++#include <linux/etherdevice.h>
++#include <linux/module.h>
++#include <net/genetlink.h>
++#include <net/ncsi.h>
++#include <linux/skbuff.h>
++#include <net/sock.h>
++#include <uapi/linux/ncsi.h>
++
++#include "internal.h"
++#include "ncsi-netlink.h"
++
++static struct genl_family ncsi_genl_family;
++
++static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
++ [NCSI_ATTR_IFINDEX] = { .type = NLA_U32 },
++ [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED },
++ [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 },
++ [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 },
++};
++
++static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
++{
++ struct ncsi_dev_priv *ndp;
++ struct net_device *dev;
++ struct ncsi_dev *nd;
++ struct ncsi_dev;
++
++ if (!net)
++ return NULL;
++
++ dev = dev_get_by_index(net, ifindex);
++ if (!dev) {
++ pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
++ return NULL;
++ }
++
++ nd = ncsi_find_dev(dev);
++ ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
++
++ dev_put(dev);
++ return ndp;
++}
++
++static int ncsi_write_channel_info(struct sk_buff *skb,
++ struct ncsi_dev_priv *ndp,
++ struct ncsi_channel *nc)
++{
++ struct nlattr *vid_nest;
++ struct ncsi_channel_filter *ncf;
++ struct ncsi_channel_mode *m;
++ u32 *data;
++ int i;
++
++ nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
++ m = &nc->modes[NCSI_MODE_LINK];
++ 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)
++ nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
++
++ nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
++ nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2);
++ nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
++
++ vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
++ if (!vid_nest)
++ return -ENOMEM;
++ ncf = nc->filters[NCSI_FILTER_VLAN];
++ i = -1;
++ if (ncf) {
++ while ((i = find_next_bit((void *)&ncf->bitmap, ncf->total,
++ i + 1)) < ncf->total) {
++ data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, i);
++ /* Uninitialised channels will have 'zero' vlan ids */
++ if (!data || !*data)
++ continue;
++ nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
++ *(u16 *)data);
++ }
++ }
++ nla_nest_end(skb, vid_nest);
++
++ return 0;
++}
++
++static int ncsi_write_package_info(struct sk_buff *skb,
++ struct ncsi_dev_priv *ndp, unsigned int id)
++{
++ struct nlattr *pnest, *cnest, *nest;
++ struct ncsi_package *np;
++ struct ncsi_channel *nc;
++ bool found;
++ int rc;
++
++ if (id > ndp->package_num) {
++ netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
++ return -ENODEV;
++ }
++
++ found = false;
++ NCSI_FOR_EACH_PACKAGE(ndp, np) {
++ if (np->id != id)
++ continue;
++ pnest = nla_nest_start(skb, NCSI_PKG_ATTR);
++ if (!pnest)
++ return -ENOMEM;
++ nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
++ if (ndp->force_package == np)
++ nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
++ cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
++ if (!cnest) {
++ nla_nest_cancel(skb, pnest);
++ return -ENOMEM;
++ }
++ NCSI_FOR_EACH_CHANNEL(np, nc) {
++ nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR);
++ if (!nest) {
++ nla_nest_cancel(skb, cnest);
++ nla_nest_cancel(skb, pnest);
++ return -ENOMEM;
++ }
++ rc = ncsi_write_channel_info(skb, ndp, nc);
++ if (rc) {
++ nla_nest_cancel(skb, nest);
++ nla_nest_cancel(skb, cnest);
++ nla_nest_cancel(skb, pnest);
++ return rc;
++ }
++ nla_nest_end(skb, nest);
++ }
++ nla_nest_end(skb, cnest);
++ nla_nest_end(skb, pnest);
++ found = true;
++ }
++
++ if (!found)
++ return -ENODEV;
++
++ return 0;
++}
++
++static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
++{
++ struct ncsi_dev_priv *ndp;
++ unsigned int package_id;
++ struct sk_buff *skb;
++ struct nlattr *attr;
++ void *hdr;
++ int rc;
++
++ if (!info || !info->attrs)
++ return -EINVAL;
++
++ if (!info->attrs[NCSI_ATTR_IFINDEX])
++ return -EINVAL;
++
++ if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
++ return -EINVAL;
++
++ ndp = ndp_from_ifindex(genl_info_net(info),
++ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
++ if (!ndp)
++ return -ENODEV;
++
++ skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
++ if (!skb)
++ return -ENOMEM;
++
++ hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
++ &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
++ if (!hdr) {
++ kfree(skb);
++ return -EMSGSIZE;
++ }
++
++ package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
++
++ attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
++ rc = ncsi_write_package_info(skb, ndp, package_id);
++
++ if (rc) {
++ nla_nest_cancel(skb, attr);
++ goto err;
++ }
++
++ nla_nest_end(skb, attr);
++
++ genlmsg_end(skb, hdr);
++ return genlmsg_reply(skb, info);
++
++err:
++ genlmsg_cancel(skb, hdr);
++ kfree(skb);
++ return rc;
++}
++
++static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
++ struct netlink_callback *cb)
++{
++ struct nlattr *attrs[NCSI_ATTR_MAX];
++ struct ncsi_package *np, *package;
++ struct ncsi_dev_priv *ndp;
++ unsigned int package_id;
++ struct nlattr *attr;
++ void *hdr;
++ int rc;
++
++ rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
++ ncsi_genl_policy, NULL);
++ if (rc)
++ return rc;
++
++ if (!attrs[NCSI_ATTR_IFINDEX])
++ return -EINVAL;
++
++ ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
++ nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
++
++ if (!ndp)
++ return -ENODEV;
++
++ package_id = cb->args[0];
++ package = NULL;
++ NCSI_FOR_EACH_PACKAGE(ndp, np)
++ if (np->id == package_id)
++ package = np;
++
++ if (!package)
++ return 0; /* done */
++
++ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
++ &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
++ if (!hdr) {
++ rc = -EMSGSIZE;
++ goto err;
++ }
++
++ attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
++ rc = ncsi_write_package_info(skb, ndp, package->id);
++ if (rc) {
++ nla_nest_cancel(skb, attr);
++ goto err;
++ }
++
++ nla_nest_end(skb, attr);
++ genlmsg_end(skb, hdr);
++
++ cb->args[0] = package_id + 1;
++
++ return skb->len;
++err:
++ genlmsg_cancel(skb, hdr);
++ return rc;
++}
++
++static int ncsi_set_interface_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;
++
++ 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;
++
++ 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 */
++ return -ERANGE;
++ }
++
++ channel = NULL;
++ if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
++ /* Allow any channel */
++ channel_id = NCSI_RESERVED_CHANNEL;
++ } else {
++ channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
++ NCSI_FOR_EACH_CHANNEL(package, nc)
++ if (nc->id == channel_id)
++ channel = nc;
++ }
++
++ if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
++ /* The user has set a channel that does not exist on this
++ * package
++ */
++ 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_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)" : "");
++
++ /* Bounce the NCSI channel to set changes */
++ ncsi_stop_dev(&ndp->ndev);
++ ncsi_start_dev(&ndp->ndev);
++
++ return 0;
++}
++
++static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
++{
++ struct ncsi_dev_priv *ndp;
++ unsigned long flags;
++
++ if (!info || !info->attrs)
++ return -EINVAL;
++
++ if (!info->attrs[NCSI_ATTR_IFINDEX])
++ return -EINVAL;
++
++ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
++ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
++ if (!ndp)
++ return -ENODEV;
++
++ /* Clear any override */
++ spin_lock_irqsave(&ndp->lock, flags);
++ ndp->force_package = NULL;
++ ndp->force_channel = NULL;
++ spin_unlock_irqrestore(&ndp->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);
++
++ return 0;
++}
++
++static const struct genl_ops ncsi_ops[] = {
++ {
++ .cmd = NCSI_CMD_PKG_INFO,
++ .policy = ncsi_genl_policy,
++ .doit = ncsi_pkg_info_nl,
++ .dumpit = ncsi_pkg_info_all_nl,
++ .flags = 0,
++ },
++ {
++ .cmd = NCSI_CMD_SET_INTERFACE,
++ .policy = ncsi_genl_policy,
++ .doit = ncsi_set_interface_nl,
++ .flags = GENL_ADMIN_PERM,
++ },
++ {
++ .cmd = NCSI_CMD_CLEAR_INTERFACE,
++ .policy = ncsi_genl_policy,
++ .doit = ncsi_clear_interface_nl,
++ .flags = GENL_ADMIN_PERM,
++ },
++};
++
++static struct genl_family ncsi_genl_family __ro_after_init = {
++ .name = "NCSI",
++ .version = 0,
++ .maxattr = NCSI_ATTR_MAX,
++ .module = THIS_MODULE,
++ .ops = ncsi_ops,
++ .n_ops = ARRAY_SIZE(ncsi_ops),
++};
++
++int ncsi_init_netlink(struct net_device *dev)
++{
++ int rc;
++
++ rc = genl_register_family(&ncsi_genl_family);
++ if (rc)
++ netdev_err(dev, "ncsi: failed to register netlink family\n");
++
++ return rc;
++}
++
++int ncsi_unregister_netlink(struct net_device *dev)
++{
++ int rc;
++
++ rc = genl_unregister_family(&ncsi_genl_family);
++ if (rc)
++ netdev_err(dev, "ncsi: failed to unregister netlink family\n");
++
++ return rc;
++}
+--- /dev/null
++++ b/net/ncsi/ncsi-netlink.h
+@@ -0,0 +1,20 @@
++/*
++ * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#ifndef __NCSI_NETLINK_H__
++#define __NCSI_NETLINK_H__
++
++#include <linux/netdevice.h>
++
++#include "internal.h"
++
++int ncsi_init_netlink(struct net_device *dev);
++int ncsi_unregister_netlink(struct net_device *dev);
++
++#endif /* __NCSI_NETLINK_H__ */
--- /dev/null
+From b0949618826cbb64e9ba764bdd52aa14eaf5073d Mon Sep 17 00:00:00 2001
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Date: Thu, 31 May 2018 17:02:54 +1000
+Subject: net/ncsi: Avoid GFP_KERNEL in response handler
+
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+
+commit b0949618826cbb64e9ba764bdd52aa14eaf5073d upstream.
+
+ncsi_rsp_handler_gc() allocates the filter arrays using GFP_KERNEL in
+softirq context, causing the below backtrace. This allocation is only a
+few dozen bytes during probing so allocate with GFP_ATOMIC instead.
+
+[ 42.813372] BUG: sleeping function called from invalid context at mm/slab.h:416
+[ 42.820900] in_atomic(): 1, irqs_disabled(): 0, pid: 213, name: kworker/0:1
+[ 42.827893] INFO: lockdep is turned off.
+[ 42.832023] CPU: 0 PID: 213 Comm: kworker/0:1 Tainted: G W 4.13.16-01441-gad99b38 #65
+[ 42.841007] Hardware name: Generic DT based system
+[ 42.845966] Workqueue: events ncsi_dev_work
+[ 42.850251] [<8010a494>] (unwind_backtrace) from [<80107510>] (show_stack+0x20/0x24)
+[ 42.858046] [<80107510>] (show_stack) from [<80612770>] (dump_stack+0x20/0x28)
+[ 42.865309] [<80612770>] (dump_stack) from [<80148248>] (___might_sleep+0x230/0x2b0)
+[ 42.873241] [<80148248>] (___might_sleep) from [<80148334>] (__might_sleep+0x6c/0xac)
+[ 42.881129] [<80148334>] (__might_sleep) from [<80240d6c>] (__kmalloc+0x210/0x2fc)
+[ 42.888737] [<80240d6c>] (__kmalloc) from [<8060ad54>] (ncsi_rsp_handler_gc+0xd0/0x170)
+[ 42.896770] [<8060ad54>] (ncsi_rsp_handler_gc) from [<8060b454>] (ncsi_rcv_rsp+0x16c/0x1d4)
+[ 42.905314] [<8060b454>] (ncsi_rcv_rsp) from [<804d86c8>] (__netif_receive_skb_core+0x3c8/0xb50)
+[ 42.914158] [<804d86c8>] (__netif_receive_skb_core) from [<804d96cc>] (__netif_receive_skb+0x20/0x7c)
+[ 42.923420] [<804d96cc>] (__netif_receive_skb) from [<804de4b0>] (netif_receive_skb_internal+0x78/0x6a4)
+[ 42.932931] [<804de4b0>] (netif_receive_skb_internal) from [<804df980>] (netif_receive_skb+0x78/0x158)
+[ 42.942292] [<804df980>] (netif_receive_skb) from [<8042f204>] (ftgmac100_poll+0x43c/0x4e8)
+[ 42.950855] [<8042f204>] (ftgmac100_poll) from [<804e094c>] (net_rx_action+0x278/0x4c4)
+[ 42.958918] [<804e094c>] (net_rx_action) from [<801016a8>] (__do_softirq+0xe0/0x4c4)
+[ 42.966716] [<801016a8>] (__do_softirq) from [<8011cd9c>] (do_softirq.part.4+0x50/0x78)
+[ 42.974756] [<8011cd9c>] (do_softirq.part.4) from [<8011cebc>] (__local_bh_enable_ip+0xf8/0x11c)
+[ 42.983579] [<8011cebc>] (__local_bh_enable_ip) from [<804dde08>] (__dev_queue_xmit+0x260/0x890)
+[ 42.992392] [<804dde08>] (__dev_queue_xmit) from [<804df1f0>] (dev_queue_xmit+0x1c/0x20)
+[ 43.000689] [<804df1f0>] (dev_queue_xmit) from [<806099c0>] (ncsi_xmit_cmd+0x1c0/0x244)
+[ 43.008763] [<806099c0>] (ncsi_xmit_cmd) from [<8060dc14>] (ncsi_dev_work+0x2e0/0x4c8)
+[ 43.016725] [<8060dc14>] (ncsi_dev_work) from [<80133dfc>] (process_one_work+0x214/0x6f8)
+[ 43.024940] [<80133dfc>] (process_one_work) from [<80134328>] (worker_thread+0x48/0x558)
+[ 43.033070] [<80134328>] (worker_thread) from [<8013ba80>] (kthread+0x130/0x174)
+[ 43.040506] [<8013ba80>] (kthread) from [<80102950>] (ret_from_fork+0x14/0x24)
+
+Fixes: 062b3e1b6d4f ("net/ncsi: Refactor MAC, VLAN filters")
+Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ncsi/ncsi-rsp.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/ncsi/ncsi-rsp.c
++++ b/net/ncsi/ncsi-rsp.c
+@@ -651,7 +651,7 @@ static int ncsi_rsp_handler_gc(struct nc
+ NCSI_CAP_VLAN_MASK;
+
+ size = (rsp->uc_cnt + rsp->mc_cnt + rsp->mixed_cnt) * ETH_ALEN;
+- nc->mac_filter.addrs = kzalloc(size, GFP_KERNEL);
++ nc->mac_filter.addrs = kzalloc(size, GFP_ATOMIC);
+ if (!nc->mac_filter.addrs)
+ return -ENOMEM;
+ nc->mac_filter.n_uc = rsp->uc_cnt;
+@@ -660,7 +660,7 @@ static int ncsi_rsp_handler_gc(struct nc
+
+ nc->vlan_filter.vids = kcalloc(rsp->vlan_cnt,
+ sizeof(*nc->vlan_filter.vids),
+- GFP_KERNEL);
++ GFP_ATOMIC);
+ if (!nc->vlan_filter.vids)
+ return -ENOMEM;
+ /* Set VLAN filters active so they are cleared in the first
--- /dev/null
+From 04bad8bda9e25afe676a6f4452f3b304c1fdea16 Mon Sep 17 00:00:00 2001
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Date: Wed, 8 Nov 2017 16:30:45 +1100
+Subject: net/ncsi: Don't return error on normal response
+
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+
+commit 04bad8bda9e25afe676a6f4452f3b304c1fdea16 upstream.
+
+Several response handlers return EBUSY if the data corresponding to the
+command/response pair is already set. There is no reason to return an
+error here; the channel is advertising something as enabled because we
+told it to enable it, and it's possible that the feature has been
+enabled previously.
+
+Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ncsi/ncsi-rsp.c | 31 ++++++++++++++-----------------
+ 1 file changed, 14 insertions(+), 17 deletions(-)
+
+--- a/net/ncsi/ncsi-rsp.c
++++ b/net/ncsi/ncsi-rsp.c
+@@ -146,7 +146,7 @@ static int ncsi_rsp_handler_ec(struct nc
+
+ ncm = &nc->modes[NCSI_MODE_ENABLE];
+ if (ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ ncm->enable = 1;
+ return 0;
+@@ -173,7 +173,7 @@ static int ncsi_rsp_handler_dc(struct nc
+
+ ncm = &nc->modes[NCSI_MODE_ENABLE];
+ if (!ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ ncm->enable = 0;
+ return 0;
+@@ -217,7 +217,7 @@ static int ncsi_rsp_handler_ecnt(struct
+
+ ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
+ if (ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ ncm->enable = 1;
+ return 0;
+@@ -239,7 +239,7 @@ static int ncsi_rsp_handler_dcnt(struct
+
+ ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
+ if (!ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ ncm->enable = 1;
+ return 0;
+@@ -263,7 +263,7 @@ static int ncsi_rsp_handler_ae(struct nc
+ /* Check if the AEN has been enabled */
+ ncm = &nc->modes[NCSI_MODE_AEN];
+ if (ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to AEN configuration */
+ cmd = (struct ncsi_cmd_ae_pkt *)skb_network_header(nr->cmd);
+@@ -382,7 +382,7 @@ static int ncsi_rsp_handler_ev(struct nc
+ /* Check if VLAN mode has been enabled */
+ ncm = &nc->modes[NCSI_MODE_VLAN];
+ if (ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to VLAN mode */
+ cmd = (struct ncsi_cmd_ev_pkt *)skb_network_header(nr->cmd);
+@@ -409,7 +409,7 @@ static int ncsi_rsp_handler_dv(struct nc
+ /* Check if VLAN mode has been enabled */
+ ncm = &nc->modes[NCSI_MODE_VLAN];
+ if (!ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to VLAN mode */
+ ncm->enable = 0;
+@@ -455,13 +455,10 @@ static int ncsi_rsp_handler_sma(struct n
+
+ bitmap = &ncf->bitmap;
+ if (cmd->at_e & 0x1) {
+- if (test_and_set_bit(cmd->index, bitmap))
+- return -EBUSY;
++ set_bit(cmd->index, bitmap);
+ memcpy(ncf->data + 6 * cmd->index, cmd->mac, 6);
+ } else {
+- if (!test_and_clear_bit(cmd->index, bitmap))
+- return -EBUSY;
+-
++ clear_bit(cmd->index, bitmap);
+ memset(ncf->data + 6 * cmd->index, 0, 6);
+ }
+
+@@ -485,7 +482,7 @@ static int ncsi_rsp_handler_ebf(struct n
+ /* Check if broadcast filter has been enabled */
+ ncm = &nc->modes[NCSI_MODE_BC];
+ if (ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to broadcast filter mode */
+ cmd = (struct ncsi_cmd_ebf_pkt *)skb_network_header(nr->cmd);
+@@ -511,7 +508,7 @@ static int ncsi_rsp_handler_dbf(struct n
+ /* Check if broadcast filter isn't enabled */
+ ncm = &nc->modes[NCSI_MODE_BC];
+ if (!ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to broadcast filter mode */
+ ncm->enable = 0;
+@@ -538,7 +535,7 @@ static int ncsi_rsp_handler_egmf(struct
+ /* Check if multicast filter has been enabled */
+ ncm = &nc->modes[NCSI_MODE_MC];
+ if (ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to multicast filter mode */
+ cmd = (struct ncsi_cmd_egmf_pkt *)skb_network_header(nr->cmd);
+@@ -564,7 +561,7 @@ static int ncsi_rsp_handler_dgmf(struct
+ /* Check if multicast filter has been enabled */
+ ncm = &nc->modes[NCSI_MODE_MC];
+ if (!ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to multicast filter mode */
+ ncm->enable = 0;
+@@ -591,7 +588,7 @@ static int ncsi_rsp_handler_snfc(struct
+ /* Check if flow control has been enabled */
+ ncm = &nc->modes[NCSI_MODE_FC];
+ if (ncm->enable)
+- return -EBUSY;
++ return 0;
+
+ /* Update to flow control mode */
+ cmd = (struct ncsi_cmd_snfc_pkt *)skb_network_header(nr->cmd);
--- /dev/null
+From 9ef8690be13d8ae3130749fbcc0cc21e4e3f738c Mon Sep 17 00:00:00 2001
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Date: Wed, 8 Nov 2017 16:30:44 +1100
+Subject: net/ncsi: Improve general state logging
+
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+
+commit 9ef8690be13d8ae3130749fbcc0cc21e4e3f738c upstream.
+
+The NCSI driver is mostly silent which becomes a headache when trying to
+determine what has occurred on the NCSI connection. This adds additional
+logging in a few key areas such as state transitions and calling out
+certain errors more visibly.
+
+Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ncsi/ncsi-aen.c | 15 +++++++++
+ net/ncsi/ncsi-manage.c | 76 ++++++++++++++++++++++++++++++++++++-------------
+ net/ncsi/ncsi-rsp.c | 10 +++++-
+ 3 files changed, 80 insertions(+), 21 deletions(-)
+
+--- a/net/ncsi/ncsi-aen.c
++++ b/net/ncsi/ncsi-aen.c
+@@ -73,6 +73,9 @@ static int ncsi_aen_handler_lsc(struct n
+ ncm->data[2] = data;
+ ncm->data[4] = ntohl(lsc->oem_status);
+
++ netdev_info(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
++ nc->id, data & 0x1 ? "up" : "down");
++
+ chained = !list_empty(&nc->link);
+ state = nc->state;
+ spin_unlock_irqrestore(&nc->lock, flags);
+@@ -145,6 +148,8 @@ static int ncsi_aen_handler_hncdsc(struc
+ ncm = &nc->modes[NCSI_MODE_LINK];
+ hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
+ ncm->data[3] = ntohl(hncdsc->status);
++ netdev_info(ndp->ndev.dev, "NCSI: HNCDSC AEN - channel %u state %s\n",
++ nc->id, ncm->data[3] & 0x3 ? "up" : "down");
+ if (!list_empty(&nc->link) ||
+ nc->state != NCSI_CHANNEL_ACTIVE) {
+ spin_unlock_irqrestore(&nc->lock, flags);
+@@ -212,10 +217,18 @@ int ncsi_aen_handler(struct ncsi_dev_pri
+ }
+
+ ret = ncsi_validate_aen_pkt(h, nah->payload);
+- if (ret)
++ if (ret) {
++ netdev_warn(ndp->ndev.dev,
++ "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
++ h->type);
+ goto out;
++ }
+
+ ret = nah->handler(ndp, h);
++ if (ret)
++ netdev_err(ndp->ndev.dev,
++ "NCSI: Handler for AEN type 0x%x returned %d\n",
++ h->type, ret);
+ out:
+ consume_skb(skb);
+ return ret;
+--- a/net/ncsi/ncsi-manage.c
++++ b/net/ncsi/ncsi-manage.c
+@@ -236,6 +236,8 @@ bad_state:
+ case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
+ break;
+ 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;
+@@ -688,7 +690,7 @@ static int clear_one_vid(struct ncsi_dev
+ data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, index);
+ if (!data) {
+ netdev_err(ndp->ndev.dev,
+- "ncsi: failed to retrieve filter %d\n", index);
++ "NCSI: failed to retrieve filter %d\n", index);
+ /* Set the VLAN id to 0 - this will still disable the entry in
+ * the filter table, but we won't know what it was.
+ */
+@@ -698,7 +700,7 @@ static int clear_one_vid(struct ncsi_dev
+ }
+
+ netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "ncsi: removed vlan tag %u at index %d\n",
++ "NCSI: removed vlan tag %u at index %d\n",
+ vid, index + 1);
+ ncsi_remove_filter(nc, NCSI_FILTER_VLAN, index);
+
+@@ -724,7 +726,7 @@ static int set_one_vid(struct ncsi_dev_p
+ if (index < 0) {
+ /* New tag to add */
+ netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "ncsi: new vlan id to set: %u\n",
++ "NCSI: new vlan id to set: %u\n",
+ vlan->vid);
+ break;
+ }
+@@ -751,7 +753,7 @@ static int set_one_vid(struct ncsi_dev_p
+ }
+
+ netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "ncsi: set vid %u in packet, index %u\n",
++ "NCSI: set vid %u in packet, index %u\n",
+ vlan->vid, index + 1);
+ nca->type = NCSI_PKT_CMD_SVF;
+ nca->words[1] = vlan->vid;
+@@ -790,8 +792,11 @@ static void ncsi_configure_channel(struc
+ nca.package = np->id;
+ nca.channel = NCSI_RESERVED_CHANNEL;
+ ret = ncsi_xmit_cmd(&nca);
+- if (ret)
++ if (ret) {
++ netdev_err(ndp->ndev.dev,
++ "NCSI: Failed to transmit CMD_SP\n");
+ goto error;
++ }
+
+ nd->state = ncsi_dev_state_config_cis;
+ break;
+@@ -803,8 +808,11 @@ static void ncsi_configure_channel(struc
+ nca.package = np->id;
+ nca.channel = nc->id;
+ ret = ncsi_xmit_cmd(&nca);
+- if (ret)
++ if (ret) {
++ netdev_err(ndp->ndev.dev,
++ "NCSI: Failed to transmit CMD_CIS\n");
+ goto error;
++ }
+
+ nd->state = ncsi_dev_state_config_clear_vids;
+ break;
+@@ -901,10 +909,16 @@ static void ncsi_configure_channel(struc
+ }
+
+ ret = ncsi_xmit_cmd(&nca);
+- if (ret)
++ if (ret) {
++ netdev_err(ndp->ndev.dev,
++ "NCSI: Failed to transmit CMD %x\n",
++ nca.type);
+ goto error;
++ }
+ break;
+ case ncsi_dev_state_config_done:
++ netdev_printk(KERN_DEBUG, ndp->ndev.dev,
++ "NCSI: channel %u config done\n", nc->id);
+ spin_lock_irqsave(&nc->lock, flags);
+ if (nc->reconfigure_needed) {
+ /* This channel's configuration has been updated
+@@ -931,6 +945,9 @@ static void ncsi_configure_channel(struc
+ } else {
+ hot_nc = NULL;
+ nc->state = NCSI_CHANNEL_INACTIVE;
++ netdev_warn(ndp->ndev.dev,
++ "NCSI: channel %u link down after config\n",
++ nc->id);
+ }
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+@@ -943,8 +960,8 @@ static void ncsi_configure_channel(struc
+ ncsi_process_next_channel(ndp);
+ break;
+ default:
+- netdev_warn(dev, "Wrong NCSI state 0x%x in config\n",
+- nd->state);
++ netdev_alert(dev, "Wrong NCSI state 0x%x in config\n",
++ nd->state);
+ }
+
+ return;
+@@ -996,10 +1013,17 @@ static int ncsi_choose_active_channel(st
+ }
+
+ if (!found) {
++ netdev_warn(ndp->ndev.dev,
++ "NCSI: No channel found with link\n");
+ ncsi_report_link(ndp, true);
+ return -ENODEV;
+ }
+
++ ncm = &found->modes[NCSI_MODE_LINK];
++ netdev_printk(KERN_DEBUG, 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);
+@@ -1061,6 +1085,8 @@ static int ncsi_enable_hwa(struct ncsi_d
+
+ /* 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;
+ }
+@@ -1229,6 +1255,9 @@ static void ncsi_probe_channel(struct nc
+
+ return;
+ error:
++ netdev_err(ndp->ndev.dev,
++ "NCSI: Failed to transmit cmd 0x%x during probe\n",
++ nca.type);
+ ncsi_report_link(ndp, true);
+ }
+
+@@ -1282,10 +1311,14 @@ int ncsi_process_next_channel(struct ncs
+ switch (old_state) {
+ case NCSI_CHANNEL_INACTIVE:
+ ndp->ndev.state = ncsi_dev_state_config;
++ netdev_info(ndp->ndev.dev, "NCSI: configuring channel %u\n",
++ nc->id);
+ ncsi_configure_channel(ndp);
+ break;
+ case NCSI_CHANNEL_ACTIVE:
+ ndp->ndev.state = ncsi_dev_state_suspend;
++ netdev_info(ndp->ndev.dev, "NCSI: suspending channel %u\n",
++ nc->id);
+ ncsi_suspend_channel(ndp);
+ break;
+ default:
+@@ -1305,6 +1338,8 @@ out:
+ return ncsi_choose_active_channel(ndp);
+ }
+
++ netdev_printk(KERN_DEBUG, ndp->ndev.dev,
++ "NCSI: No more channels to process\n");
+ ncsi_report_link(ndp, false);
+ return -ENODEV;
+ }
+@@ -1396,7 +1431,7 @@ static int ncsi_kick_channels(struct ncs
+ ncsi_dev_state_config ||
+ !list_empty(&nc->link)) {
+ netdev_printk(KERN_DEBUG, nd->dev,
+- "ncsi: channel %p marked dirty\n",
++ "NCSI: channel %p marked dirty\n",
+ nc);
+ nc->reconfigure_needed = true;
+ }
+@@ -1416,7 +1451,7 @@ static int ncsi_kick_channels(struct ncs
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ netdev_printk(KERN_DEBUG, nd->dev,
+- "ncsi: kicked channel %p\n", nc);
++ "NCSI: kicked channel %p\n", nc);
+ n++;
+ }
+ }
+@@ -1437,7 +1472,7 @@ int ncsi_vlan_rx_add_vid(struct net_devi
+
+ nd = ncsi_find_dev(dev);
+ if (!nd) {
+- netdev_warn(dev, "ncsi: No net_device?\n");
++ netdev_warn(dev, "NCSI: No net_device?\n");
+ return 0;
+ }
+
+@@ -1448,7 +1483,7 @@ int ncsi_vlan_rx_add_vid(struct net_devi
+ n_vids++;
+ if (vlan->vid == vid) {
+ netdev_printk(KERN_DEBUG, dev,
+- "vid %u already registered\n", vid);
++ "NCSI: vid %u already registered\n", vid);
+ return 0;
+ }
+ }
+@@ -1467,7 +1502,7 @@ int ncsi_vlan_rx_add_vid(struct net_devi
+ vlan->vid = vid;
+ list_add_rcu(&vlan->list, &ndp->vlan_vids);
+
+- netdev_printk(KERN_DEBUG, dev, "Added new vid %u\n", vid);
++ netdev_printk(KERN_DEBUG, dev, "NCSI: Added new vid %u\n", vid);
+
+ found = ncsi_kick_channels(ndp) != 0;
+
+@@ -1487,7 +1522,7 @@ int ncsi_vlan_rx_kill_vid(struct net_dev
+
+ nd = ncsi_find_dev(dev);
+ if (!nd) {
+- netdev_warn(dev, "ncsi: no net_device?\n");
++ netdev_warn(dev, "NCSI: no net_device?\n");
+ return 0;
+ }
+
+@@ -1497,14 +1532,14 @@ int ncsi_vlan_rx_kill_vid(struct net_dev
+ list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
+ if (vlan->vid == vid) {
+ netdev_printk(KERN_DEBUG, dev,
+- "vid %u found, removing\n", vid);
++ "NCSI: vid %u found, removing\n", vid);
+ list_del_rcu(&vlan->list);
+ found = true;
+ kfree(vlan);
+ }
+
+ if (!found) {
+- netdev_err(dev, "ncsi: vid %u wasn't registered!\n", vid);
++ netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid);
+ return -EINVAL;
+ }
+
+@@ -1587,10 +1622,12 @@ int ncsi_start_dev(struct ncsi_dev *nd)
+ return 0;
+ }
+
+- if (ndp->flags & NCSI_DEV_HWA)
++ if (ndp->flags & NCSI_DEV_HWA) {
++ netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n");
+ ret = ncsi_enable_hwa(ndp);
+- else
++ } else {
+ ret = ncsi_choose_active_channel(ndp);
++ }
+
+ return ret;
+ }
+@@ -1621,6 +1658,7 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
+ }
+ }
+
++ netdev_printk(KERN_DEBUG, ndp->ndev.dev, "NCSI: Stopping device\n");
+ ncsi_report_link(ndp, true);
+ }
+ EXPORT_SYMBOL_GPL(ncsi_stop_dev);
+--- a/net/ncsi/ncsi-rsp.c
++++ b/net/ncsi/ncsi-rsp.c
+@@ -1032,11 +1032,19 @@ int ncsi_rcv_rsp(struct sk_buff *skb, st
+ if (payload < 0)
+ payload = ntohs(hdr->length);
+ ret = ncsi_validate_rsp_pkt(nr, payload);
+- if (ret)
++ if (ret) {
++ netdev_warn(ndp->ndev.dev,
++ "NCSI: 'bad' packet ignored for type 0x%x\n",
++ hdr->type);
+ goto out;
++ }
+
+ /* Process the packet */
+ ret = nrh->handler(nr);
++ if (ret)
++ netdev_err(ndp->ndev.dev,
++ "NCSI: Handler for packet type 0x%x returned %d\n",
++ hdr->type, ret);
+ out:
+ ncsi_free_request(nr);
+ return ret;
--- /dev/null
+From 5a6d80034471d4407052c4bf3758071df5cadf33 Mon Sep 17 00:00:00 2001
+From: Wei Yongjun <weiyongjun1@huawei.com>
+Date: Thu, 2 Nov 2017 11:15:28 +0000
+Subject: net/ncsi: Make local function ncsi_get_filter() static
+
+From: Wei Yongjun <weiyongjun1@huawei.com>
+
+commit 5a6d80034471d4407052c4bf3758071df5cadf33 upstream.
+
+Fixes the following sparse warnings:
+
+net/ncsi/ncsi-manage.c:41:5: warning:
+ symbol 'ncsi_get_filter' was not declared. Should it be static?
+
+Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ncsi/ncsi-manage.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/ncsi/ncsi-manage.c
++++ b/net/ncsi/ncsi-manage.c
+@@ -38,7 +38,7 @@ static inline int ncsi_filter_size(int t
+ return sizes[table];
+ }
+
+-u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
++static u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
+ {
+ struct ncsi_channel_filter *ncf;
+ int size;
--- /dev/null
+From 062b3e1b6d4f2a33c1d0fd7ae9b4550da5cf7e4b Mon Sep 17 00:00:00 2001
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Date: Tue, 17 Apr 2018 14:23:23 +1000
+Subject: net/ncsi: Refactor MAC, VLAN filters
+
+From: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+
+commit 062b3e1b6d4f2a33c1d0fd7ae9b4550da5cf7e4b upstream.
+
+The NCSI driver defines a generic ncsi_channel_filter struct that can be
+used to store arbitrarily formatted filters, and several generic methods
+of accessing data stored in such a filter.
+However in both the driver and as defined in the NCSI specification
+there are only two actual filters: VLAN ID filters and MAC address
+filters. The splitting of the MAC filter into unicast, multicast, and
+mixed is also technically not necessary as these are stored in the same
+location in hardware.
+
+To save complexity, particularly in the set up and accessing of these
+generic filters, remove them in favour of two specific structs. These
+can be acted on directly and do not need several generic helper
+functions to use.
+
+This also fixes a memory error found by KASAN on ARM32 (which is not
+upstream yet), where response handlers accessing a filter's data field
+could write past allocated memory.
+
+[ 114.926512] ==================================================================
+[ 114.933861] BUG: KASAN: slab-out-of-bounds in ncsi_configure_channel+0x4b8/0xc58
+[ 114.941304] Read of size 2 at addr 94888558 by task kworker/0:2/546
+[ 114.947593]
+[ 114.949146] CPU: 0 PID: 546 Comm: kworker/0:2 Not tainted 4.16.0-rc6-00119-ge156398bfcad #13
+...
+[ 115.170233] The buggy address belongs to the object at 94888540
+[ 115.170233] which belongs to the cache kmalloc-32 of size 32
+[ 115.181917] The buggy address is located 24 bytes inside of
+[ 115.181917] 32-byte region [94888540, 94888560)
+[ 115.192115] The buggy address belongs to the page:
+[ 115.196943] page:9eeac100 count:1 mapcount:0 mapping:94888000 index:0x94888fc1
+[ 115.204200] flags: 0x100(slab)
+[ 115.207330] raw: 00000100 94888000 94888fc1 0000003f 00000001 9eea2014 9eecaa74 96c003e0
+[ 115.215444] page dumped because: kasan: bad access detected
+[ 115.221036]
+[ 115.222544] Memory state around the buggy address:
+[ 115.227384] 94888400: fb fb fb fb fc fc fc fc 04 fc fc fc fc fc fc fc
+[ 115.233959] 94888480: 00 00 00 fc fc fc fc fc 00 04 fc fc fc fc fc fc
+[ 115.240529] >94888500: 00 00 04 fc fc fc fc fc 00 00 04 fc fc fc fc fc
+[ 115.247077] ^
+[ 115.252523] 94888580: 00 04 fc fc fc fc fc fc 06 fc fc fc fc fc fc fc
+[ 115.259093] 94888600: 00 00 06 fc fc fc fc fc 00 00 04 fc fc fc fc fc
+[ 115.265639] ==================================================================
+
+Reported-by: Joel Stanley <joel@jms.id.au>
+Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ncsi/internal.h | 34 +++----
+ net/ncsi/ncsi-manage.c | 226 +++++++++++-------------------------------------
+ net/ncsi/ncsi-netlink.c | 20 +---
+ net/ncsi/ncsi-rsp.c | 178 +++++++++++++++----------------------
+ 4 files changed, 147 insertions(+), 311 deletions(-)
+
+--- a/net/ncsi/internal.h
++++ b/net/ncsi/internal.h
+@@ -68,15 +68,6 @@ enum {
+ NCSI_MODE_MAX
+ };
+
+-enum {
+- NCSI_FILTER_BASE = 0,
+- NCSI_FILTER_VLAN = 0,
+- NCSI_FILTER_UC,
+- NCSI_FILTER_MC,
+- NCSI_FILTER_MIXED,
+- NCSI_FILTER_MAX
+-};
+-
+ struct ncsi_channel_version {
+ u32 version; /* Supported BCD encoded NCSI version */
+ u32 alpha2; /* Supported BCD encoded NCSI version */
+@@ -98,11 +89,18 @@ struct ncsi_channel_mode {
+ u32 data[8]; /* Data entries */
+ };
+
+-struct ncsi_channel_filter {
+- u32 index; /* Index of channel filters */
+- u32 total; /* Total entries in the filter table */
+- u64 bitmap; /* Bitmap of valid entries */
+- u32 data[]; /* Data for the valid entries */
++struct ncsi_channel_mac_filter {
++ u8 n_uc;
++ u8 n_mc;
++ u8 n_mixed;
++ u64 bitmap;
++ unsigned char *addrs;
++};
++
++struct ncsi_channel_vlan_filter {
++ u8 n_vids;
++ u64 bitmap;
++ u16 *vids;
+ };
+
+ struct ncsi_channel_stats {
+@@ -186,7 +184,9 @@ struct ncsi_channel {
+ struct ncsi_channel_version version;
+ struct ncsi_channel_cap caps[NCSI_CAP_MAX];
+ struct ncsi_channel_mode modes[NCSI_MODE_MAX];
+- struct ncsi_channel_filter *filters[NCSI_FILTER_MAX];
++ /* Filtering Settings */
++ struct ncsi_channel_mac_filter mac_filter;
++ struct ncsi_channel_vlan_filter vlan_filter;
+ struct ncsi_channel_stats stats;
+ struct {
+ struct timer_list timer;
+@@ -320,10 +320,6 @@ extern spinlock_t ncsi_dev_lock;
+ list_for_each_entry_rcu(nc, &np->channels, node)
+
+ /* Resources */
+-u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index);
+-int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data);
+-int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data);
+-int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index);
+ 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,
+--- a/net/ncsi/ncsi-manage.c
++++ b/net/ncsi/ncsi-manage.c
+@@ -27,125 +27,6 @@
+ LIST_HEAD(ncsi_dev_list);
+ DEFINE_SPINLOCK(ncsi_dev_lock);
+
+-static inline int ncsi_filter_size(int table)
+-{
+- int sizes[] = { 2, 6, 6, 6 };
+-
+- BUILD_BUG_ON(ARRAY_SIZE(sizes) != NCSI_FILTER_MAX);
+- if (table < NCSI_FILTER_BASE || table >= NCSI_FILTER_MAX)
+- return -EINVAL;
+-
+- return sizes[table];
+-}
+-
+-u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
+-{
+- struct ncsi_channel_filter *ncf;
+- int size;
+-
+- ncf = nc->filters[table];
+- if (!ncf)
+- return NULL;
+-
+- size = ncsi_filter_size(table);
+- if (size < 0)
+- return NULL;
+-
+- return ncf->data + size * index;
+-}
+-
+-/* Find the first active filter in a filter table that matches the given
+- * data parameter. If data is NULL, this returns the first active filter.
+- */
+-int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data)
+-{
+- struct ncsi_channel_filter *ncf;
+- void *bitmap;
+- int index, size;
+- unsigned long flags;
+-
+- ncf = nc->filters[table];
+- if (!ncf)
+- return -ENXIO;
+-
+- size = ncsi_filter_size(table);
+- if (size < 0)
+- return size;
+-
+- spin_lock_irqsave(&nc->lock, flags);
+- bitmap = (void *)&ncf->bitmap;
+- index = -1;
+- while ((index = find_next_bit(bitmap, ncf->total, index + 1))
+- < ncf->total) {
+- if (!data || !memcmp(ncf->data + size * index, data, size)) {
+- spin_unlock_irqrestore(&nc->lock, flags);
+- return index;
+- }
+- }
+- spin_unlock_irqrestore(&nc->lock, flags);
+-
+- return -ENOENT;
+-}
+-
+-int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data)
+-{
+- struct ncsi_channel_filter *ncf;
+- int index, size;
+- void *bitmap;
+- unsigned long flags;
+-
+- size = ncsi_filter_size(table);
+- if (size < 0)
+- return size;
+-
+- index = ncsi_find_filter(nc, table, data);
+- if (index >= 0)
+- return index;
+-
+- ncf = nc->filters[table];
+- if (!ncf)
+- return -ENODEV;
+-
+- spin_lock_irqsave(&nc->lock, flags);
+- bitmap = (void *)&ncf->bitmap;
+- do {
+- index = find_next_zero_bit(bitmap, ncf->total, 0);
+- if (index >= ncf->total) {
+- spin_unlock_irqrestore(&nc->lock, flags);
+- return -ENOSPC;
+- }
+- } while (test_and_set_bit(index, bitmap));
+-
+- memcpy(ncf->data + size * index, data, size);
+- spin_unlock_irqrestore(&nc->lock, flags);
+-
+- return index;
+-}
+-
+-int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index)
+-{
+- struct ncsi_channel_filter *ncf;
+- int size;
+- void *bitmap;
+- unsigned long flags;
+-
+- size = ncsi_filter_size(table);
+- if (size < 0)
+- return size;
+-
+- ncf = nc->filters[table];
+- if (!ncf || index >= ncf->total)
+- return -ENODEV;
+-
+- spin_lock_irqsave(&nc->lock, flags);
+- bitmap = (void *)&ncf->bitmap;
+- if (test_and_clear_bit(index, bitmap))
+- memset(ncf->data + size * index, 0, size);
+- spin_unlock_irqrestore(&nc->lock, flags);
+-
+- return 0;
+-}
+-
+ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
+ {
+ struct ncsi_dev *nd = &ndp->ndev;
+@@ -346,20 +227,13 @@ struct ncsi_channel *ncsi_add_channel(st
+ static void ncsi_remove_channel(struct ncsi_channel *nc)
+ {
+ struct ncsi_package *np = nc->package;
+- struct ncsi_channel_filter *ncf;
+ unsigned long flags;
+- int i;
+
+- /* Release filters */
+ spin_lock_irqsave(&nc->lock, flags);
+- for (i = 0; i < NCSI_FILTER_MAX; i++) {
+- ncf = nc->filters[i];
+- if (!ncf)
+- continue;
+
+- nc->filters[i] = NULL;
+- kfree(ncf);
+- }
++ /* Release filters */
++ kfree(nc->mac_filter.addrs);
++ kfree(nc->vlan_filter.vids);
+
+ nc->state = NCSI_CHANNEL_INACTIVE;
+ spin_unlock_irqrestore(&nc->lock, flags);
+@@ -677,32 +551,26 @@ error:
+ static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
+ struct ncsi_cmd_arg *nca)
+ {
++ struct ncsi_channel_vlan_filter *ncf;
++ unsigned long flags;
++ void *bitmap;
+ int index;
+- u32 *data;
+ u16 vid;
+
+- index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, NULL);
+- if (index < 0) {
+- /* Filter table empty */
+- return -1;
+- }
++ ncf = &nc->vlan_filter;
++ bitmap = &ncf->bitmap;
+
+- data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, index);
+- if (!data) {
+- netdev_err(ndp->ndev.dev,
+- "NCSI: failed to retrieve filter %d\n", index);
+- /* Set the VLAN id to 0 - this will still disable the entry in
+- * the filter table, but we won't know what it was.
+- */
+- vid = 0;
+- } else {
+- vid = *(u16 *)data;
++ spin_lock_irqsave(&nc->lock, flags);
++ index = find_next_bit(bitmap, ncf->n_vids, 0);
++ if (index >= ncf->n_vids) {
++ spin_unlock_irqrestore(&nc->lock, flags);
++ return -1;
+ }
++ vid = ncf->vids[index];
+
+- netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "NCSI: removed vlan tag %u at index %d\n",
+- vid, index + 1);
+- ncsi_remove_filter(nc, NCSI_FILTER_VLAN, index);
++ clear_bit(index, bitmap);
++ ncf->vids[index] = 0;
++ spin_unlock_irqrestore(&nc->lock, flags);
+
+ nca->type = NCSI_PKT_CMD_SVF;
+ nca->words[1] = vid;
+@@ -718,45 +586,55 @@ static int clear_one_vid(struct ncsi_dev
+ static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
+ struct ncsi_cmd_arg *nca)
+ {
++ struct ncsi_channel_vlan_filter *ncf;
+ struct vlan_vid *vlan = NULL;
+- int index = 0;
++ unsigned long flags;
++ int i, index;
++ void *bitmap;
++ u16 vid;
++
++ if (list_empty(&ndp->vlan_vids))
++ return -1;
++
++ ncf = &nc->vlan_filter;
++ bitmap = &ncf->bitmap;
+
++ spin_lock_irqsave(&nc->lock, flags);
++
++ rcu_read_lock();
+ list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
+- index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
+- if (index < 0) {
+- /* New tag to add */
+- netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "NCSI: new vlan id to set: %u\n",
+- vlan->vid);
++ vid = vlan->vid;
++ for (i = 0; i < ncf->n_vids; i++)
++ if (ncf->vids[i] == vid) {
++ vid = 0;
++ break;
++ }
++ if (vid)
+ break;
+- }
+- netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "vid %u already at filter pos %d\n",
+- vlan->vid, index);
+ }
++ rcu_read_unlock();
+
+- if (!vlan || index >= 0) {
+- netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "no vlan ids left to set\n");
++ if (!vid) {
++ /* No VLAN ID is not set */
++ spin_unlock_irqrestore(&nc->lock, flags);
+ return -1;
+ }
+
+- index = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
+- if (index < 0) {
++ index = find_next_zero_bit(bitmap, ncf->n_vids, 0);
++ if (index < 0 || index >= ncf->n_vids) {
+ netdev_err(ndp->ndev.dev,
+- "Failed to add new VLAN tag, error %d\n", index);
+- if (index == -ENOSPC)
+- netdev_err(ndp->ndev.dev,
+- "Channel %u already has all VLAN filters set\n",
+- nc->id);
++ "Channel %u already has all VLAN filters set\n",
++ nc->id);
++ spin_unlock_irqrestore(&nc->lock, flags);
+ return -1;
+ }
+
+- netdev_printk(KERN_DEBUG, ndp->ndev.dev,
+- "NCSI: set vid %u in packet, index %u\n",
+- vlan->vid, index + 1);
++ ncf->vids[index] = vid;
++ set_bit(index, bitmap);
++ spin_unlock_irqrestore(&nc->lock, flags);
++
+ nca->type = NCSI_PKT_CMD_SVF;
+- nca->words[1] = vlan->vid;
++ nca->words[1] = vid;
+ /* HW filter index starts at 1 */
+ nca->bytes[6] = index + 1;
+ nca->bytes[7] = 0x01;
+--- a/net/ncsi/ncsi-netlink.c
++++ b/net/ncsi/ncsi-netlink.c
+@@ -58,10 +58,9 @@ static int ncsi_write_channel_info(struc
+ struct ncsi_dev_priv *ndp,
+ struct ncsi_channel *nc)
+ {
+- struct nlattr *vid_nest;
+- struct ncsi_channel_filter *ncf;
++ struct ncsi_channel_vlan_filter *ncf;
+ struct ncsi_channel_mode *m;
+- u32 *data;
++ struct nlattr *vid_nest;
+ int i;
+
+ nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
+@@ -79,18 +78,13 @@ static int ncsi_write_channel_info(struc
+ vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
+ if (!vid_nest)
+ return -ENOMEM;
+- ncf = nc->filters[NCSI_FILTER_VLAN];
++ ncf = &nc->vlan_filter;
+ i = -1;
+- if (ncf) {
+- while ((i = find_next_bit((void *)&ncf->bitmap, ncf->total,
+- i + 1)) < ncf->total) {
+- data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, i);
+- /* Uninitialised channels will have 'zero' vlan ids */
+- if (!data || !*data)
+- continue;
++ while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
++ i + 1)) < ncf->n_vids) {
++ if (ncf->vids[i])
+ nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
+- *(u16 *)data);
+- }
++ ncf->vids[i]);
+ }
+ nla_nest_end(skb, vid_nest);
+
+--- a/net/ncsi/ncsi-rsp.c
++++ b/net/ncsi/ncsi-rsp.c
+@@ -334,9 +334,9 @@ static int ncsi_rsp_handler_svf(struct n
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+- struct ncsi_channel_filter *ncf;
+- unsigned short vlan;
+- int ret;
++ struct ncsi_channel_vlan_filter *ncf;
++ unsigned long flags;
++ void *bitmap;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+@@ -346,22 +346,23 @@ static int ncsi_rsp_handler_svf(struct n
+ return -ENODEV;
+
+ cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd);
+- ncf = nc->filters[NCSI_FILTER_VLAN];
+- if (!ncf)
+- return -ENOENT;
+- if (cmd->index >= ncf->total)
++ ncf = &nc->vlan_filter;
++ if (cmd->index > ncf->n_vids)
+ return -ERANGE;
+
+- /* Add or remove the VLAN filter */
++ /* Add or remove the VLAN filter. Remember HW indexes from 1 */
++ spin_lock_irqsave(&nc->lock, flags);
++ bitmap = &ncf->bitmap;
+ if (!(cmd->enable & 0x1)) {
+- /* HW indexes from 1 */
+- ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index - 1);
++ if (test_and_clear_bit(cmd->index - 1, bitmap))
++ ncf->vids[cmd->index - 1] = 0;
+ } else {
+- vlan = ntohs(cmd->vlan);
+- ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
++ set_bit(cmd->index - 1, bitmap);
++ ncf->vids[cmd->index - 1] = ntohs(cmd->vlan);
+ }
++ spin_unlock_irqrestore(&nc->lock, flags);
+
+- return ret;
++ return 0;
+ }
+
+ static int ncsi_rsp_handler_ev(struct ncsi_request *nr)
+@@ -422,8 +423,12 @@ static int ncsi_rsp_handler_sma(struct n
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+- struct ncsi_channel_filter *ncf;
++ struct ncsi_channel_mac_filter *ncf;
++ unsigned long flags;
+ void *bitmap;
++ bool enabled;
++ int index;
++
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+@@ -436,31 +441,23 @@ static int ncsi_rsp_handler_sma(struct n
+ * isn't supported yet.
+ */
+ cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->cmd);
+- switch (cmd->at_e >> 5) {
+- case 0x0: /* UC address */
+- ncf = nc->filters[NCSI_FILTER_UC];
+- break;
+- case 0x1: /* MC address */
+- ncf = nc->filters[NCSI_FILTER_MC];
+- break;
+- default:
+- return -EINVAL;
+- }
++ enabled = cmd->at_e & 0x1;
++ ncf = &nc->mac_filter;
++ bitmap = &ncf->bitmap;
+
+- /* Sanity check on the filter */
+- if (!ncf)
+- return -ENOENT;
+- else if (cmd->index >= ncf->total)
++ if (cmd->index > ncf->n_uc + ncf->n_mc + ncf->n_mixed)
+ return -ERANGE;
+
+- bitmap = &ncf->bitmap;
+- if (cmd->at_e & 0x1) {
+- set_bit(cmd->index, bitmap);
+- memcpy(ncf->data + 6 * cmd->index, cmd->mac, 6);
++ index = (cmd->index - 1) * ETH_ALEN;
++ spin_lock_irqsave(&nc->lock, flags);
++ if (enabled) {
++ set_bit(cmd->index - 1, bitmap);
++ memcpy(&ncf->addrs[index], cmd->mac, ETH_ALEN);
+ } else {
+- clear_bit(cmd->index, bitmap);
+- memset(ncf->data + 6 * cmd->index, 0, 6);
++ clear_bit(cmd->index - 1, bitmap);
++ memset(&ncf->addrs[index], 0, ETH_ALEN);
+ }
++ spin_unlock_irqrestore(&nc->lock, flags);
+
+ return 0;
+ }
+@@ -631,9 +628,7 @@ static int ncsi_rsp_handler_gc(struct nc
+ struct ncsi_rsp_gc_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+- struct ncsi_channel_filter *ncf;
+- size_t size, entry_size;
+- int cnt, i;
++ size_t size;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp);
+@@ -655,64 +650,40 @@ static int ncsi_rsp_handler_gc(struct nc
+ nc->caps[NCSI_CAP_VLAN].cap = rsp->vlan_mode &
+ NCSI_CAP_VLAN_MASK;
+
+- /* Build filters */
+- for (i = 0; i < NCSI_FILTER_MAX; i++) {
+- switch (i) {
+- case NCSI_FILTER_VLAN:
+- cnt = rsp->vlan_cnt;
+- entry_size = 2;
+- break;
+- case NCSI_FILTER_MIXED:
+- cnt = rsp->mixed_cnt;
+- entry_size = 6;
+- break;
+- case NCSI_FILTER_MC:
+- cnt = rsp->mc_cnt;
+- entry_size = 6;
+- break;
+- case NCSI_FILTER_UC:
+- cnt = rsp->uc_cnt;
+- entry_size = 6;
+- break;
+- default:
+- continue;
+- }
+-
+- if (!cnt || nc->filters[i])
+- continue;
+-
+- size = sizeof(*ncf) + cnt * entry_size;
+- ncf = kzalloc(size, GFP_ATOMIC);
+- if (!ncf) {
+- pr_warn("%s: Cannot alloc filter table (%d)\n",
+- __func__, i);
+- return -ENOMEM;
+- }
+-
+- ncf->index = i;
+- ncf->total = cnt;
+- if (i == NCSI_FILTER_VLAN) {
+- /* Set VLAN filters active so they are cleared in
+- * first configuration state
+- */
+- ncf->bitmap = U64_MAX;
+- } else {
+- ncf->bitmap = 0x0ul;
+- }
+- nc->filters[i] = ncf;
+- }
++ size = (rsp->uc_cnt + rsp->mc_cnt + rsp->mixed_cnt) * ETH_ALEN;
++ nc->mac_filter.addrs = kzalloc(size, GFP_KERNEL);
++ if (!nc->mac_filter.addrs)
++ return -ENOMEM;
++ nc->mac_filter.n_uc = rsp->uc_cnt;
++ nc->mac_filter.n_mc = rsp->mc_cnt;
++ nc->mac_filter.n_mixed = rsp->mixed_cnt;
++
++ nc->vlan_filter.vids = kcalloc(rsp->vlan_cnt,
++ sizeof(*nc->vlan_filter.vids),
++ GFP_KERNEL);
++ if (!nc->vlan_filter.vids)
++ return -ENOMEM;
++ /* Set VLAN filters active so they are cleared in the first
++ * configuration state
++ */
++ nc->vlan_filter.bitmap = U64_MAX;
++ nc->vlan_filter.n_vids = rsp->vlan_cnt;
+
+ return 0;
+ }
+
+ static int ncsi_rsp_handler_gp(struct ncsi_request *nr)
+ {
+- struct ncsi_rsp_gp_pkt *rsp;
++ struct ncsi_channel_vlan_filter *ncvf;
++ struct ncsi_channel_mac_filter *ncmf;
+ struct ncsi_dev_priv *ndp = nr->ndp;
++ struct ncsi_rsp_gp_pkt *rsp;
+ struct ncsi_channel *nc;
+- unsigned short enable, vlan;
++ unsigned short enable;
+ unsigned char *pdata;
+- int table, i;
++ unsigned long flags;
++ void *bitmap;
++ int i;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->rsp);
+@@ -746,36 +717,33 @@ static int ncsi_rsp_handler_gp(struct nc
+ /* MAC addresses filter table */
+ pdata = (unsigned char *)rsp + 48;
+ enable = rsp->mac_enable;
++ ncmf = &nc->mac_filter;
++ spin_lock_irqsave(&nc->lock, flags);
++ bitmap = &ncmf->bitmap;
+ for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
+- if (i >= (nc->filters[NCSI_FILTER_UC]->total +
+- nc->filters[NCSI_FILTER_MC]->total))
+- table = NCSI_FILTER_MIXED;
+- else if (i >= nc->filters[NCSI_FILTER_UC]->total)
+- table = NCSI_FILTER_MC;
+- else
+- table = NCSI_FILTER_UC;
+-
+ if (!(enable & (0x1 << i)))
+- continue;
+-
+- if (ncsi_find_filter(nc, table, pdata) >= 0)
+- continue;
++ clear_bit(i, bitmap);
++ else
++ set_bit(i, bitmap);
+
+- ncsi_add_filter(nc, table, pdata);
++ memcpy(&ncmf->addrs[i * ETH_ALEN], pdata, ETH_ALEN);
+ }
++ spin_unlock_irqrestore(&nc->lock, flags);
+
+ /* VLAN filter table */
+ enable = ntohs(rsp->vlan_enable);
++ ncvf = &nc->vlan_filter;
++ bitmap = &ncvf->bitmap;
++ spin_lock_irqsave(&nc->lock, flags);
+ for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
+ if (!(enable & (0x1 << i)))
+- continue;
+-
+- vlan = ntohs(*(__be16 *)pdata);
+- if (ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan) >= 0)
+- continue;
++ clear_bit(i, bitmap);
++ else
++ set_bit(i, bitmap);
+
+- ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
++ ncvf->vids[i] = ntohs(*(__be16 *)pdata);
+ }
++ spin_unlock_irqrestore(&nc->lock, flags);
+
+ return 0;
+ }
net-mlx5-fix-placement-of-log_max_flow_counter.patch
rdma-cxgb4-check-for-ipv6-address-properly-while-des.patch
clk-socfpga-fix-iomem-pointer-cast-on-64-bit.patch
+net-ncsi-make-local-function-ncsi_get_filter-static.patch
+net-ncsi-improve-general-state-logging.patch
+net-ncsi-don-t-return-error-on-normal-response.patch
+net-ncsi-add-generic-netlink-family.patch
+net-ncsi-refactor-mac-vlan-filters.patch
+net-ncsi-avoid-gfp_kernel-in-response-handler.patch