From: Greg Kroah-Hartman Date: Mon, 12 Apr 2021 07:18:31 +0000 (+0200) Subject: 4.14-stable patches X-Git-Tag: v4.19.187~26 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7a92efa27c0fdea96ccda38f2e179ce608b7308f;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches 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 --- 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 index 00000000000..3ca581fca6f --- /dev/null +++ b/queue-4.14/net-ncsi-add-generic-netlink-family.patch @@ -0,0 +1,709 @@ +From 955dc68cb9b23b42999cafe6df3684309bc686c6 Mon Sep 17 00:00:00 2001 +From: Samuel Mendoza-Jonas +Date: Mon, 5 Mar 2018 11:39:05 +1100 +Subject: net/ncsi: Add generic netlink family + +From: Samuel Mendoza-Jonas + +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 +Signed-off-by: David S. Miller +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + #include +-#include + + #include + #include +@@ -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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#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 index 00000000000..fead9f53eff --- /dev/null +++ b/queue-4.14/net-ncsi-avoid-gfp_kernel-in-response-handler.patch @@ -0,0 +1,73 @@ +From b0949618826cbb64e9ba764bdd52aa14eaf5073d Mon Sep 17 00:00:00 2001 +From: Samuel Mendoza-Jonas +Date: Thu, 31 May 2018 17:02:54 +1000 +Subject: net/ncsi: Avoid GFP_KERNEL in response handler + +From: Samuel Mendoza-Jonas + +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 +Signed-off-by: David S. Miller +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..6ddb208bdc0 --- /dev/null +++ b/queue-4.14/net-ncsi-don-t-return-error-on-normal-response.patch @@ -0,0 +1,149 @@ +From 04bad8bda9e25afe676a6f4452f3b304c1fdea16 Mon Sep 17 00:00:00 2001 +From: Samuel Mendoza-Jonas +Date: Wed, 8 Nov 2017 16:30:45 +1100 +Subject: net/ncsi: Don't return error on normal response + +From: Samuel Mendoza-Jonas + +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 +Signed-off-by: David S. Miller +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..0fcff4ffd6f --- /dev/null +++ b/queue-4.14/net-ncsi-improve-general-state-logging.patch @@ -0,0 +1,355 @@ +From 9ef8690be13d8ae3130749fbcc0cc21e4e3f738c Mon Sep 17 00:00:00 2001 +From: Samuel Mendoza-Jonas +Date: Wed, 8 Nov 2017 16:30:44 +1100 +Subject: net/ncsi: Improve general state logging + +From: Samuel Mendoza-Jonas + +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 +Signed-off-by: David S. Miller +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..50d77e30598 --- /dev/null +++ b/queue-4.14/net-ncsi-make-local-function-ncsi_get_filter-static.patch @@ -0,0 +1,33 @@ +From 5a6d80034471d4407052c4bf3758071df5cadf33 Mon Sep 17 00:00:00 2001 +From: Wei Yongjun +Date: Thu, 2 Nov 2017 11:15:28 +0000 +Subject: net/ncsi: Make local function ncsi_get_filter() static + +From: Wei Yongjun + +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 +Signed-off-by: David S. Miller +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..5aa3c3cacc6 --- /dev/null +++ b/queue-4.14/net-ncsi-refactor-mac-vlan-filters.patch @@ -0,0 +1,708 @@ +From 062b3e1b6d4f2a33c1d0fd7ae9b4550da5cf7e4b Mon Sep 17 00:00:00 2001 +From: Samuel Mendoza-Jonas +Date: Tue, 17 Apr 2018 14:23:23 +1000 +Subject: net/ncsi: Refactor MAC, VLAN filters + +From: Samuel Mendoza-Jonas + +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 +Signed-off-by: Samuel Mendoza-Jonas +Signed-off-by: David S. Miller +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } diff --git a/queue-4.14/series b/queue-4.14/series index b3b9c18e64f..be5e110ff64 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -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