]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 12 Apr 2021 07:18:31 +0000 (09:18 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 12 Apr 2021 07:18:31 +0000 (09:18 +0200)
added patches:
net-ncsi-add-generic-netlink-family.patch
net-ncsi-avoid-gfp_kernel-in-response-handler.patch
net-ncsi-don-t-return-error-on-normal-response.patch
net-ncsi-improve-general-state-logging.patch
net-ncsi-make-local-function-ncsi_get_filter-static.patch
net-ncsi-refactor-mac-vlan-filters.patch

queue-4.14/net-ncsi-add-generic-netlink-family.patch [new file with mode: 0644]
queue-4.14/net-ncsi-avoid-gfp_kernel-in-response-handler.patch [new file with mode: 0644]
queue-4.14/net-ncsi-don-t-return-error-on-normal-response.patch [new file with mode: 0644]
queue-4.14/net-ncsi-improve-general-state-logging.patch [new file with mode: 0644]
queue-4.14/net-ncsi-make-local-function-ncsi_get_filter-static.patch [new file with mode: 0644]
queue-4.14/net-ncsi-refactor-mac-vlan-filters.patch [new file with mode: 0644]
queue-4.14/series

diff --git a/queue-4.14/net-ncsi-add-generic-netlink-family.patch b/queue-4.14/net-ncsi-add-generic-netlink-family.patch
new file mode 100644 (file)
index 0000000..3ca581f
--- /dev/null
@@ -0,0 +1,709 @@
+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__ */
diff --git a/queue-4.14/net-ncsi-avoid-gfp_kernel-in-response-handler.patch b/queue-4.14/net-ncsi-avoid-gfp_kernel-in-response-handler.patch
new file mode 100644 (file)
index 0000000..fead9f5
--- /dev/null
@@ -0,0 +1,73 @@
+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
diff --git a/queue-4.14/net-ncsi-don-t-return-error-on-normal-response.patch b/queue-4.14/net-ncsi-don-t-return-error-on-normal-response.patch
new file mode 100644 (file)
index 0000000..6ddb208
--- /dev/null
@@ -0,0 +1,149 @@
+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);
diff --git a/queue-4.14/net-ncsi-improve-general-state-logging.patch b/queue-4.14/net-ncsi-improve-general-state-logging.patch
new file mode 100644 (file)
index 0000000..0fcff4f
--- /dev/null
@@ -0,0 +1,355 @@
+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;
diff --git a/queue-4.14/net-ncsi-make-local-function-ncsi_get_filter-static.patch b/queue-4.14/net-ncsi-make-local-function-ncsi_get_filter-static.patch
new file mode 100644 (file)
index 0000000..50d77e3
--- /dev/null
@@ -0,0 +1,33 @@
+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;
diff --git a/queue-4.14/net-ncsi-refactor-mac-vlan-filters.patch b/queue-4.14/net-ncsi-refactor-mac-vlan-filters.patch
new file mode 100644 (file)
index 0000000..5aa3c3c
--- /dev/null
@@ -0,0 +1,708 @@
+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;
+ }
index b3b9c18e64f82430b53311256cb5b22ad12926be..be5e110ff646b1fffc84022953be954bbd34db0a 100644 (file)
@@ -36,3 +36,9 @@ s390-cpcmd-fix-inline-assembly-register-clobbering.patch
 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