* [net-next 6/7] ip6tlvs: Add netlink interface
From: Jeff Kirsher @ 2019-08-22 22:59 UTC (permalink / raw)
To: davem; +Cc: Tom Herbert, netdev, nhorman, sassmann, Andrew Bowers,
Jeff Kirsher
In-Reply-To: <20190822225940.14235-1-jeffrey.t.kirsher@intel.com>
From: Tom Herbert <tom@herbertland.com>
Add a netlink interface to manage the TX TLV parameters. Managed
parameters include those for validating and sending TLVs being sent
such as alignment, TLV ordering, length limits, etc.
Signed-off-by: Tom Herbert <tom@herbertland.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
include/net/ipeh.h | 16 +++
include/net/ipv6.h | 1 +
include/uapi/linux/in6.h | 6 +
include/uapi/linux/ipeh.h | 29 +++++
net/ipv6/exthdrs_common.c | 238 +++++++++++++++++++++++++++++++++++++
net/ipv6/exthdrs_options.c | 81 ++++++++++++-
6 files changed, 369 insertions(+), 2 deletions(-)
diff --git a/include/net/ipeh.h b/include/net/ipeh.h
index de6d9d05f43e..8474a43f8a08 100644
--- a/include/net/ipeh.h
+++ b/include/net/ipeh.h
@@ -3,6 +3,7 @@
#define _NET_IPEH_H
#include <linux/skbuff.h>
+#include <net/genetlink.h>
/*
* Parsing tlv encoded headers.
@@ -106,6 +107,21 @@ static inline int ipeh_tlv_unset_proc(struct tlv_param_table *tlv_param_table,
return __ipeh_tlv_unset(tlv_param_table, type, false);
}
+extern const struct nla_policy ipeh_tlv_nl_policy[];
+
+int ipeh_tlv_nl_cmd_set(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct genl_info *info);
+int ipeh_tlv_nl_cmd_unset(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct genl_info *info);
+int ipeh_tlv_nl_cmd_get(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct genl_info *info);
+int ipeh_tlv_nl_dump(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct netlink_callback *cb);
+
/* ipeh_tlv_get_proc_by_type assumes rcu_read_lock is held */
static inline struct tlv_proc *ipeh_tlv_get_proc_by_type(
struct tlv_param_table *tlv_param_table, unsigned char type)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 07bafad74e76..51517a16e528 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -14,6 +14,7 @@
#include <linux/jhash.h>
#include <linux/refcount.h>
#include <linux/jump_label_ratelimit.h>
+#include <net/genetlink.h>
#include <net/if_inet6.h>
#include <net/ndisc.h>
#include <net/flow.h>
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 9f2273a08356..d5fe3d9a0b59 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -297,4 +297,10 @@ struct in6_flowlabel_req {
* ...
* MRT6_MAX
*/
+
+ /* NETLINK_GENERIC related info for IPv6 TLVs */
+
+#define IPV6_TLV_GENL_NAME "ipv6-tlv"
+#define IPV6_TLV_GENL_VERSION 0x1
+
#endif /* _UAPI_LINUX_IN6_H */
diff --git a/include/uapi/linux/ipeh.h b/include/uapi/linux/ipeh.h
index dbf0728ddce4..bac36a7faaae 100644
--- a/include/uapi/linux/ipeh.h
+++ b/include/uapi/linux/ipeh.h
@@ -21,4 +21,33 @@ enum {
IPEH_TLV_PERM_MAX = IPEH_TLV_PERM_NO_CHECK
};
+/* NETLINK_GENERIC related info for IP TLVs */
+
+enum {
+ IPEH_TLV_ATTR_UNSPEC,
+ IPEH_TLV_ATTR_TYPE, /* u8, > 1 */
+ IPEH_TLV_ATTR_ORDER, /* u16 */
+ IPEH_TLV_ATTR_ADMIN_PERM, /* u8, perm value */
+ IPEH_TLV_ATTR_USER_PERM, /* u8, perm value */
+ IPEH_TLV_ATTR_CLASS, /* u8, 3 bit flags */
+ IPEH_TLV_ATTR_ALIGN_MULT, /* u8, 1 to 16 */
+ IPEH_TLV_ATTR_ALIGN_OFF, /* u8, 0 to 15 */
+ IPEH_TLV_ATTR_MIN_DATA_LEN, /* u8 (option data length) */
+ IPEH_TLV_ATTR_MAX_DATA_LEN, /* u8 (option data length) */
+ IPEH_TLV_ATTR_DATA_LEN_MULT, /* u8, 1 to 16 */
+ IPEH_TLV_ATTR_DATA_LEN_OFF, /* u8, 0 to 15 */
+
+ __IPEH_TLV_ATTR_MAX,
+};
+
+#define IPEH_TLV_ATTR_MAX (__IPEH_TLV_ATTR_MAX - 1)
+
+enum {
+ IPEH_TLV_CMD_SET,
+ IPEH_TLV_CMD_UNSET,
+ IPEH_TLV_CMD_GET,
+
+ __IPEH_TLV_CMD_MAX,
+};
+
#endif /* _UAPI_LINUX_IPEH_H */
diff --git a/net/ipv6/exthdrs_common.c b/net/ipv6/exthdrs_common.c
index 791f6e4036c7..b44c6fd05f92 100644
--- a/net/ipv6/exthdrs_common.c
+++ b/net/ipv6/exthdrs_common.c
@@ -454,6 +454,244 @@ int __ipeh_tlv_unset(struct tlv_param_table *tlv_param_table,
}
EXPORT_SYMBOL(__ipeh_tlv_unset);
+const struct nla_policy ipeh_tlv_nl_policy[IPEH_TLV_ATTR_MAX + 1] = {
+ [IPEH_TLV_ATTR_TYPE] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_ORDER] = { .type = NLA_U16, },
+ [IPEH_TLV_ATTR_ADMIN_PERM] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_USER_PERM] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_CLASS] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_ALIGN_MULT] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_ALIGN_OFF] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_MIN_DATA_LEN] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_MAX_DATA_LEN] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_DATA_LEN_OFF] = { .type = NLA_U8, },
+ [IPEH_TLV_ATTR_DATA_LEN_MULT] = { .type = NLA_U8, },
+};
+EXPORT_SYMBOL(ipeh_tlv_nl_policy);
+
+int ipeh_tlv_nl_cmd_set(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ struct tlv_params new_params;
+ struct tlv_proc *tproc;
+ unsigned char type;
+ unsigned int v;
+ int retv = -EINVAL;
+
+ if (!info->attrs[IPEH_TLV_ATTR_TYPE])
+ return -EINVAL;
+
+ type = nla_get_u8(info->attrs[IPEH_TLV_ATTR_TYPE]);
+ if (type < 2)
+ return -EINVAL;
+
+ rcu_read_lock();
+
+ /* Base new parameters on existing ones */
+ tproc = ipeh_tlv_get_proc_by_type(tlv_param_table, type);
+ new_params = tproc->params;
+
+ if (info->attrs[IPEH_TLV_ATTR_ORDER]) {
+ v = nla_get_u16(info->attrs[IPEH_TLV_ATTR_ORDER]);
+ new_params.t.preferred_order = v;
+ }
+
+ if (info->attrs[IPEH_TLV_ATTR_ADMIN_PERM]) {
+ v = nla_get_u8(info->attrs[IPEH_TLV_ATTR_ADMIN_PERM]);
+ if (v > IPEH_TLV_PERM_MAX)
+ goto out;
+ new_params.t.admin_perm = v;
+ }
+
+ if (info->attrs[IPEH_TLV_ATTR_USER_PERM]) {
+ v = nla_get_u8(info->attrs[IPEH_TLV_ATTR_USER_PERM]);
+ if (v > IPEH_TLV_PERM_MAX)
+ goto out;
+ new_params.t.user_perm = v;
+ }
+
+ if (info->attrs[IPEH_TLV_ATTR_CLASS]) {
+ v = nla_get_u8(info->attrs[IPEH_TLV_ATTR_CLASS]);
+ if (!v || (v & ~IPEH_TLV_CLASS_FLAG_MASK))
+ goto out;
+ new_params.t.class = v;
+ }
+
+ if (info->attrs[IPEH_TLV_ATTR_ALIGN_MULT]) {
+ v = nla_get_u8(info->attrs[IPEH_TLV_ATTR_ALIGN_MULT]);
+ if (v > 16 || v < 1)
+ goto out;
+ new_params.t.align_mult = v - 1;
+ }
+
+ if (info->attrs[IPEH_TLV_ATTR_ALIGN_OFF]) {
+ v = nla_get_u8(info->attrs[IPEH_TLV_ATTR_ALIGN_OFF]);
+ if (v > 15)
+ goto out;
+ new_params.t.align_off = v;
+ }
+
+ if (info->attrs[IPEH_TLV_ATTR_MAX_DATA_LEN])
+ new_params.t.max_data_len =
+ nla_get_u8(info->attrs[IPEH_TLV_ATTR_MAX_DATA_LEN]);
+
+ if (info->attrs[IPEH_TLV_ATTR_MIN_DATA_LEN])
+ new_params.t.min_data_len =
+ nla_get_u8(info->attrs[IPEH_TLV_ATTR_MIN_DATA_LEN]);
+
+ if (info->attrs[IPEH_TLV_ATTR_DATA_LEN_MULT]) {
+ v = nla_get_u8(info->attrs[IPEH_TLV_ATTR_DATA_LEN_MULT]);
+ if (v > 16 || v < 1)
+ goto out;
+ new_params.t.data_len_mult = v - 1;
+ }
+
+ if (info->attrs[IPEH_TLV_ATTR_DATA_LEN_OFF]) {
+ v = nla_get_u8(info->attrs[IPEH_TLV_ATTR_DATA_LEN_OFF]);
+ if (v > 15)
+ goto out;
+ new_params.t.data_len_off = v;
+ }
+
+ retv = ipeh_tlv_set_params(tlv_param_table, type, &new_params);
+
+out:
+ rcu_read_unlock();
+ return retv;
+}
+EXPORT_SYMBOL(ipeh_tlv_nl_cmd_set);
+
+int ipeh_tlv_nl_cmd_unset(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ unsigned char type;
+
+ if (!info->attrs[IPEH_TLV_ATTR_TYPE])
+ return -EINVAL;
+
+ type = nla_get_u8(info->attrs[IPEH_TLV_ATTR_TYPE]);
+ if (type < 2)
+ return -EINVAL;
+
+ return ipeh_tlv_unset_params(tlv_param_table, type);
+}
+EXPORT_SYMBOL(ipeh_tlv_nl_cmd_unset);
+
+static int tlv_fill_info(struct tlv_proc *tproc, unsigned char type,
+ struct sk_buff *msg, bool admin)
+{
+ struct tlv_params *tp = &tproc->params;
+ int ret = 0;
+
+ if (nla_put_u8(msg, IPEH_TLV_ATTR_TYPE, type) ||
+ nla_put_u16(msg, IPEH_TLV_ATTR_ORDER, tp->t.preferred_order) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_USER_PERM, tp->t.user_perm) ||
+ (admin && nla_put_u8(msg, IPEH_TLV_ATTR_ADMIN_PERM,
+ tp->t.admin_perm)) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_CLASS, tp->t.class) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_ALIGN_MULT, tp->t.align_mult + 1) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_ALIGN_OFF, tp->t.align_off) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_MIN_DATA_LEN, tp->t.min_data_len) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_MAX_DATA_LEN, tp->t.max_data_len) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_DATA_LEN_MULT,
+ tp->t.data_len_mult + 1) ||
+ nla_put_u8(msg, IPEH_TLV_ATTR_DATA_LEN_OFF, tp->t.data_len_off))
+ ret = -1;
+
+ return ret;
+}
+
+static int tlv_dump_info(struct tlv_proc *tproc, unsigned char type,
+ struct genl_family *tlv_nl_family, u32 portid,
+ u32 seq, u32 flags, struct sk_buff *skb, u8 cmd,
+ bool admin)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, portid, seq, tlv_nl_family, flags, cmd);
+ if (!hdr)
+ return -ENOMEM;
+
+ if (tlv_fill_info(tproc, type, skb, admin) < 0) {
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+ }
+
+ genlmsg_end(skb, hdr);
+
+ return 0;
+}
+
+int ipeh_tlv_nl_cmd_get(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ struct tlv_proc *tproc;
+ struct sk_buff *msg;
+ unsigned char type;
+ int ret;
+
+ if (!info->attrs[IPEH_TLV_ATTR_TYPE])
+ return -EINVAL;
+
+ type = nla_get_u8(info->attrs[IPEH_TLV_ATTR_TYPE]);
+ if (type < 2)
+ return -EINVAL;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ rcu_read_lock();
+
+ tproc = ipeh_tlv_get_proc_by_type(tlv_param_table, type);
+ ret = tlv_dump_info(tproc, type, tlv_nl_family, info->snd_portid,
+ info->snd_seq, 0, msg, info->genlhdr->cmd,
+ netlink_capable(skb, CAP_NET_ADMIN));
+
+ rcu_read_unlock();
+
+ if (ret < 0) {
+ nlmsg_free(msg);
+ return ret;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+EXPORT_SYMBOL(ipeh_tlv_nl_cmd_get);
+
+int ipeh_tlv_nl_dump(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct tlv_proc *tproc;
+ int idx = 0, ret, i;
+
+ rcu_read_lock();
+
+ for (i = 2; i < 256; i++) {
+ if (idx++ < cb->args[0])
+ continue;
+
+ tproc = ipeh_tlv_get_proc_by_type(tlv_param_table, i);
+ ret = tlv_dump_info(tproc, i, tlv_nl_family,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ skb, IPEH_TLV_CMD_GET,
+ netlink_capable(cb->skb, CAP_NET_ADMIN));
+ if (ret)
+ break;
+ }
+
+ rcu_read_unlock();
+
+ cb->args[0] = idx;
+ return skb->len;
+}
+EXPORT_SYMBOL(ipeh_tlv_nl_dump);
+
int ipeh_exthdrs_init(struct tlv_param_table *tlv_param_table,
const struct tlv_proc_init *tlv_init_params,
int num_init_params)
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index 3b50b584dd2d..c1889f67adb1 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -6,6 +6,7 @@
#include <linux/socket.h>
#include <linux/types.h>
#include <net/calipso.h>
+#include <net/genetlink.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
#if IS_ENABLED(CONFIG_IPV6_MIP6)
@@ -253,13 +254,89 @@ static const struct tlv_proc_init tlv_ipv6_init_params[] __initconst = {
struct tlv_param_table __rcu ipv6_tlv_param_table;
EXPORT_SYMBOL(ipv6_tlv_param_table);
+static int ipv6_tlv_nl_cmd_set(struct sk_buff *skb, struct genl_info *info);
+static int ipv6_tlv_nl_cmd_unset(struct sk_buff *skb, struct genl_info *info);
+static int ipv6_tlv_nl_cmd_get(struct sk_buff *skb, struct genl_info *info);
+static int ipv6_tlv_nl_dump(struct sk_buff *skb, struct netlink_callback *cb);
+
+static const struct genl_ops ipv6_tlv_nl_ops[] = {
+{
+ .cmd = IPEH_TLV_CMD_SET,
+ .doit = ipv6_tlv_nl_cmd_set,
+ .flags = GENL_ADMIN_PERM,
+},
+{
+ .cmd = IPEH_TLV_CMD_UNSET,
+ .doit = ipv6_tlv_nl_cmd_unset,
+ .flags = GENL_ADMIN_PERM,
+},
+{
+ .cmd = IPEH_TLV_CMD_GET,
+ .doit = ipv6_tlv_nl_cmd_get,
+ .dumpit = ipv6_tlv_nl_dump,
+},
+};
+
+struct genl_family ipv6_tlv_nl_family __ro_after_init = {
+ .hdrsize = 0,
+ .name = IPV6_TLV_GENL_NAME,
+ .version = IPV6_TLV_GENL_VERSION,
+ .maxattr = IPEH_TLV_ATTR_MAX,
+ .policy = ipeh_tlv_nl_policy,
+ .netnsok = true,
+ .parallel_ops = true,
+ .ops = ipv6_tlv_nl_ops,
+ .n_ops = ARRAY_SIZE(ipv6_tlv_nl_ops),
+ .module = THIS_MODULE,
+};
+
+static int ipv6_tlv_nl_cmd_set(struct sk_buff *skb, struct genl_info *info)
+{
+ return ipeh_tlv_nl_cmd_set(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, info);
+}
+
+static int ipv6_tlv_nl_cmd_unset(struct sk_buff *skb, struct genl_info *info)
+{
+ return ipeh_tlv_nl_cmd_unset(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, info);
+}
+
+static int ipv6_tlv_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
+{
+ return ipeh_tlv_nl_cmd_get(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, info);
+}
+
+static int ipv6_tlv_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ return ipeh_tlv_nl_dump(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, cb);
+}
+
int __init ipv6_exthdrs_options_init(void)
{
- return ipeh_exthdrs_init(&ipv6_tlv_param_table, tlv_ipv6_init_params,
- ARRAY_SIZE(tlv_ipv6_init_params));
+ int err;
+
+ err = genl_register_family(&ipv6_tlv_nl_family);
+ if (err)
+ goto genl_fail;
+
+ ipeh_exthdrs_init(&ipv6_tlv_param_table, tlv_ipv6_init_params,
+ ARRAY_SIZE(tlv_ipv6_init_params));
+ if (err)
+ goto ipv6_fail;
+
+ return 0;
+
+ipv6_fail:
+ genl_unregister_family(&ipv6_tlv_nl_family);
+genl_fail:
+ return err;
}
void ipv6_exthdrs_options_exit(void)
{
ipeh_exthdrs_fini(&ipv6_tlv_param_table);
+ genl_unregister_family(&ipv6_tlv_nl_family);
}
--
2.21.0
^ permalink raw reply related
* [net-next 4/7] ip6tlvs: Registration of TLV handlers and parameters
From: Jeff Kirsher @ 2019-08-22 22:59 UTC (permalink / raw)
To: davem; +Cc: Tom Herbert, netdev, nhorman, sassmann, Andrew Bowers,
Jeff Kirsher
In-Reply-To: <20190822225940.14235-1-jeffrey.t.kirsher@intel.com>
From: Tom Herbert <tom@herbertland.com>
Create a single TLV parameter table that holds meta information for IPv6
Hop-by-Hop and Destination TLVs. The data structure is composed of a 256
element array of u8's (one entry for each TLV type to allow O(1)
lookup). Each entry provides an offset into an array of TLV proc data
structures which follows the array of u8s. The TLV proc data structure
contains parameters and handler functions for receiving and transmitting
TLVs. The zeroth element in the TLV proc array provides default
parameters for TLVs.
A class attribute indicates the type of extension header in which the
TLV may be used (e.g. Hop-by-Hop options, Destination options, or
Destination options before the routing header).
Functions are defined to manipulate entries in the TLV parameter table.
* tlv_{set|unset}_proc set a TLV proc entry (ops and parameters)
* tlv_{set|unset}_params set parameters only
Receive TLV lookup and processing is modified to be a lookup in the TLV
parameter table. An init table containing parameters for TLVs supported
by the kernel is used to initialize the TLV table.
Signed-off-by: Tom Herbert <tom@herbertland.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
include/net/ipeh.h | 107 ++++++++++++++-
include/net/ipv6.h | 3 +
include/uapi/linux/ipeh.h | 16 +++
net/ipv6/exthdrs.c | 14 +-
net/ipv6/exthdrs_common.c | 271 ++++++++++++++++++++++++++++++++++---
net/ipv6/exthdrs_options.c | 63 ++++++---
6 files changed, 421 insertions(+), 53 deletions(-)
create mode 100644 include/uapi/linux/ipeh.h
diff --git a/include/net/ipeh.h b/include/net/ipeh.h
index c1aa7b6b37a7..aaa2910be3ab 100644
--- a/include/net/ipeh.h
+++ b/include/net/ipeh.h
@@ -11,13 +11,105 @@
* and false, if it failed.
* It MUST NOT touch skb->h.
*/
-struct tlvtype_proc {
- int type;
- bool (*func)(struct sk_buff *skb, int offset);
+struct tlv_ops {
+ bool (*func)(unsigned int class, struct sk_buff *skb, int offset);
};
-extern const struct tlvtype_proc tlvprocdestopt_lst[];
-extern const struct tlvtype_proc tlvprochopopt_lst[];
+struct tlv_rx_params {
+ unsigned char class : 4;
+};
+
+struct tlv_tx_params {
+};
+
+struct tlv_params {
+ struct tlv_rx_params r;
+ struct tlv_tx_params t;
+};
+
+struct tlv_proc {
+ struct tlv_ops ops;
+ struct tlv_params params;
+};
+
+struct tlv_type {
+ struct tlv_proc proc;
+};
+
+struct tlv_proc_init {
+ int type;
+ struct tlv_proc proc;
+};
+
+struct tlv_param_table_data {
+ unsigned char entries[256];
+ unsigned char count;
+ struct rcu_head rcu;
+ struct tlv_type types[0];
+};
+
+struct tlv_param_table {
+ struct tlv_param_table_data __rcu *data;
+};
+
+extern struct tlv_param_table ipv6_tlv_param_table;
+
+int __ipeh_tlv_set(struct tlv_param_table *tlv_param_table,
+ unsigned char type, const struct tlv_params *params,
+ const struct tlv_ops *ops);
+
+static inline int ipeh_tlv_set_params(struct tlv_param_table *tlv_param_table,
+ unsigned char type,
+ const struct tlv_params *params)
+{
+ return __ipeh_tlv_set(tlv_param_table, type, params, NULL);
+}
+
+static inline int ipeh_tlv_set_proc(struct tlv_param_table *tlv_param_table,
+ unsigned char type,
+ const struct tlv_proc *proc)
+{
+ return __ipeh_tlv_set(tlv_param_table, type,
+ &proc->params, &proc->ops);
+}
+
+int __ipeh_tlv_unset(struct tlv_param_table *tlv_param_table,
+ unsigned char type, bool params_only);
+
+static inline int ipeh_tlv_unset_params(struct tlv_param_table *tlv_param_table,
+ unsigned char type)
+{
+ return __ipeh_tlv_unset(tlv_param_table, type, true);
+}
+
+static inline int ipeh_tlv_unset_proc(struct tlv_param_table *tlv_param_table,
+ unsigned char type)
+{
+ return __ipeh_tlv_unset(tlv_param_table, type, false);
+}
+
+/* ipeh_tlv_get_proc_by_type assumes rcu_read_lock is held */
+static inline struct tlv_proc *ipeh_tlv_get_proc_by_type(
+ struct tlv_param_table *tlv_param_table, unsigned char type)
+{
+ struct tlv_param_table_data *tpt =
+ rcu_dereference(tlv_param_table->data);
+
+ return &tpt->types[tpt->entries[type]].proc;
+}
+
+/* ipeh_tlv_get_proc assumes rcu_read_lock is held */
+static inline struct tlv_proc *ipeh_tlv_get_proc(
+ struct tlv_param_table *tlv_param_table,
+ const __u8 *tlv)
+{
+ return ipeh_tlv_get_proc_by_type(tlv_param_table, tlv[0]);
+}
+
+int ipeh_exthdrs_init(struct tlv_param_table *tlv_param_table,
+ const struct tlv_proc_init *init_params,
+ int num_init_params);
+void ipeh_exthdrs_fini(struct tlv_param_table *tlv_param_table);
struct ipv6_txoptions;
struct ipv6_opt_hdr;
@@ -51,8 +143,9 @@ enum ipeh_parse_errors {
#define IPEH_TLV_PAD1 0
#define IPEH_TLV_PADN 1
-bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,
- int max_count, int off, int len,
+bool ipeh_parse_tlv(unsigned int class,
+ struct tlv_param_table *tlv_param_table,
+ struct sk_buff *skb, int max_count, int off, int len,
bool (*parse_error)(struct sk_buff *skb,
int off, enum ipeh_parse_errors error));
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 1c6878b73db2..07bafad74e76 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -429,6 +429,9 @@ int ip6_ra_control(struct sock *sk, int sel);
int ipv6_parse_hopopts(struct sk_buff *skb);
+int ipv6_exthdrs_options_init(void);
+void ipv6_exthdrs_options_exit(void);
+
bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,
const struct inet6_skb_parm *opt);
struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
diff --git a/include/uapi/linux/ipeh.h b/include/uapi/linux/ipeh.h
new file mode 100644
index 000000000000..c4302b7edd3e
--- /dev/null
+++ b/include/uapi/linux/ipeh.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* ipeh.h - IP extension header TLV management */
+
+#ifndef _UAPI_LINUX_IPEH_H
+#define _UAPI_LINUX_IPEH_H
+
+/* Flags for EH type that can use a TLV option */
+#define IPEH_TLV_CLASS_FLAG_HOPOPT BIT(0)
+#define IPEH_TLV_CLASS_FLAG_RTRDSTOPT BIT(1)
+#define IPEH_TLV_CLASS_FLAG_DSTOPT BIT(2)
+
+#define IPEH_TLV_CLASS_FLAG_MASK (IPEH_TLV_CLASS_FLAG_HOPOPT | \
+ IPEH_TLV_CLASS_FLAG_RTRDSTOPT | \
+ IPEH_TLV_CLASS_FLAG_DSTOPT)
+
+#endif /* _UAPI_LINUX_IPEH_H */
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 939d27c1d059..0847d494eefe 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -47,6 +47,7 @@
#ifdef CONFIG_IPV6_SEG6_HMAC
#include <net/seg6_hmac.h>
#endif
+#include <uapi/linux/ipeh.h>
#include <linux/uaccess.h>
@@ -131,7 +132,8 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)
dstbuf = opt->dst1;
#endif
- if (ipeh_parse_tlv(tlvprocdestopt_lst, skb,
+ if (ipeh_parse_tlv(IPEH_TLV_CLASS_FLAG_DSTOPT,
+ &ipv6_tlv_param_table, skb,
init_net.ipv6.sysctl.max_dst_opts_cnt,
2, extlen - 2, ipv6_parse_error)) {
skb->transport_header += extlen;
@@ -514,8 +516,13 @@ int __init ipv6_exthdrs_init(void)
if (ret)
goto out_destopt;
+ ret = ipv6_exthdrs_options_init();
+ if (ret)
+ goto out_nodata;
out:
return ret;
+out_nodata:
+ inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
out_destopt:
inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
out_rthdr:
@@ -525,6 +532,7 @@ int __init ipv6_exthdrs_init(void)
void ipv6_exthdrs_exit(void)
{
+ ipv6_exthdrs_options_exit();
inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
@@ -555,8 +563,8 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
goto fail_and_free;
opt->flags |= IP6SKB_HOPBYHOP;
- if (ipeh_parse_tlv(tlvprochopopt_lst, skb,
- init_net.ipv6.sysctl.max_hbh_opts_cnt,
+ if (ipeh_parse_tlv(IPEH_TLV_CLASS_FLAG_HOPOPT, &ipv6_tlv_param_table,
+ skb, init_net.ipv6.sysctl.max_hbh_opts_cnt,
2, extlen - 2, ipv6_parse_error)) {
skb->transport_header += extlen;
opt = IP6CB(skb);
diff --git a/net/ipv6/exthdrs_common.c b/net/ipv6/exthdrs_common.c
index 99a0911d8315..cc8db9e43ec2 100644
--- a/net/ipv6/exthdrs_common.c
+++ b/net/ipv6/exthdrs_common.c
@@ -150,13 +150,14 @@ EXPORT_SYMBOL_GPL(ipeh_fixup_options);
* - off is offset from skb_transport_header where first TLV is
* - len is length of TLV block
*/
-bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,
- int max_count, int off, int len,
+bool ipeh_parse_tlv(unsigned int class,
+ struct tlv_param_table *tlv_param_table,
+ struct sk_buff *skb, int max_count, int off, int len,
bool (*parse_error)(struct sk_buff *skb,
int off, enum ipeh_parse_errors error))
{
const unsigned char *nh = skb_network_header(skb);
- const struct tlvtype_proc *curr;
+ const struct tlv_proc *curr;
bool disallow_unknowns = false;
int tlv_count = 0;
int padlen = 0;
@@ -168,8 +169,10 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,
if (skb_transport_offset(skb) + off + len > skb_headlen(skb)) {
if (!parse_error(skb, skb_transport_offset(skb),
- IPEH_PARSE_ERR_EH_TOOBIG))
- goto bad;
+ IPEH_PARSE_ERR_EH_TOOBIG)) {
+ kfree_skb(skb);
+ return false;
+ }
len = skb_headlen(skb) - skb_transport_offset(skb) - off;
}
@@ -177,6 +180,8 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,
/* ops function based offset on network header */
off += skb_network_header_len(skb);
+ rcu_read_lock();
+
while (len > 0) {
int optlen = nh[off + 1] + 2;
int i;
@@ -221,26 +226,22 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,
tlv_count++;
if (tlv_count > max_count &&
- parse_error(skb, off, IPEH_PARSE_ERR_OPT_TOOMANY))
+ !parse_error(skb, off, IPEH_PARSE_ERR_OPT_TOOMANY))
goto bad;
- for (curr = procs; curr->type >= 0; curr++) {
- if (curr->type == nh[off]) {
- /* type specific length/alignment
- * checks will be performed in the
- * func().
- */
- if (curr->func(skb, off) == false)
- return false;
- break;
- }
- }
- if (curr->type < 0 &&
- !parse_error(skb, off,
+ curr = ipeh_tlv_get_proc(tlv_param_table, &nh[off]);
+ if ((curr->params.r.class & class) && curr->ops.func) {
+ /* Handler will apply additional checks to
+ * the TLV
+ */
+ if (!curr->ops.func(class, skb, off))
+ return false;
+ } else if (!parse_error(skb, off,
disallow_unknowns ?
IPEH_PARSE_ERR_OPT_UNK_DISALW :
- IPEH_PARSE_ERR_OPT_UNK))
+ IPEH_PARSE_ERR_OPT_UNK)) {
goto bad;
+ }
padlen = 0;
break;
@@ -249,10 +250,238 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,
len -= optlen;
}
- if (len == 0)
+ if (len == 0) {
+ rcu_read_unlock();
return true;
+ }
bad:
+ rcu_read_unlock();
kfree_skb(skb);
return false;
}
EXPORT_SYMBOL(ipeh_parse_tlv);
+
+/* TLV parameter table functions and structures */
+
+/* Default (unset) values for TLV parameters */
+static const struct tlv_proc tlv_default_proc = {
+};
+
+static DEFINE_MUTEX(tlv_mutex);
+
+static size_t tlv_param_table_size(unsigned char count)
+{
+ return sizeof(struct tlv_param_table_data) +
+ (count * sizeof(struct tlv_type));
+}
+
+static void tlv_param_table_release(struct rcu_head *rcu)
+{
+ struct tlv_param_table_data *tpt =
+ container_of(rcu, struct tlv_param_table_data, rcu);
+
+ kvfree(tpt);
+}
+
+/* mutex held */
+static int __tlv_set_one(struct tlv_param_table *tlv_param_table,
+ unsigned char type, const struct tlv_params *params,
+ const struct tlv_ops *ops)
+{
+ struct tlv_param_table_data *tpt, *told;
+ struct tlv_type *ttype;
+
+ told = rcu_dereference_protected(tlv_param_table->data,
+ lockdep_is_held(&tlv_mutex));
+
+ /* Create new TLV table. If there is no exsiting entry then we are
+ * adding a new one to the table, else we're modifying an entry.
+ */
+ tpt = kvmalloc(tlv_param_table_size(told->count + !told->entries[type]),
+ GFP_KERNEL);
+ if (!tpt)
+ return -ENOMEM;
+
+ memcpy(tpt, told, tlv_param_table_size(told->count));
+
+ if (!told->entries[type]) {
+ memset(&tpt->types[told->count], 0, sizeof(struct tlv_type));
+ tpt->entries[type] = told->count;
+ tpt->count = told->count + 1;
+ }
+
+ ttype = &tpt->types[tpt->entries[type]];
+
+ ttype->proc.params = *params;
+ ttype->proc.ops = ops ? *ops : tlv_default_proc.ops;
+
+ rcu_assign_pointer(tlv_param_table->data, tpt);
+ call_rcu(&told->rcu, tlv_param_table_release);
+
+ return 0;
+}
+
+int __ipeh_tlv_set(struct tlv_param_table *tlv_param_table, unsigned char type,
+ const struct tlv_params *params, const struct tlv_ops *ops)
+{
+ int retv;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+ retv = __tlv_set_one(tlv_param_table, type, params, ops);
+ mutex_unlock(&tlv_mutex);
+
+ return retv;
+}
+EXPORT_SYMBOL(__ipeh_tlv_set);
+
+/* mutex held */
+static int __tlv_unset_one(struct tlv_param_table *tlv_param_table,
+ unsigned char type)
+{
+ struct tlv_param_table_data *tpt, *told;
+ unsigned int i, pos;
+
+ told = rcu_dereference_protected(tlv_param_table->data,
+ lockdep_is_held(&tlv_mutex));
+
+ if (!told->entries[type])
+ return 0;
+
+ tpt = kvmalloc(tlv_param_table_size(told->count - 1),
+ GFP_KERNEL);
+ if (!tpt)
+ return -ENOMEM;
+
+ pos = told->entries[type];
+
+ memcpy(tpt->types, told->types, pos * sizeof(struct tlv_type));
+ memcpy(&tpt->types[pos], &told->types[pos + 1],
+ (told->count - pos - 1) * sizeof(struct tlv_type));
+
+ for (i = 0; i < 256; i++) {
+ if (told->entries[i] > pos)
+ tpt->entries[i] = told->entries[i] - 1;
+ else
+ tpt->entries[i] = told->entries[i];
+ }
+
+ /* Clear entry for type being unset (point to default params) */
+ tpt->entries[type] = 0;
+
+ tpt->count = told->count - 1;
+
+ rcu_assign_pointer(tlv_param_table->data, tpt);
+ call_rcu(&told->rcu, tlv_param_table_release);
+
+ return 0;
+}
+
+/* tlv_internal_proc_type is used to check it the TLV proc was set
+ * internally. This is deduced by checking if any operations are defined.
+ */
+static bool tlv_internal_proc_type(struct tlv_proc *proc)
+{
+ return !!proc->ops.func;
+}
+
+int __ipeh_tlv_unset(struct tlv_param_table *tlv_param_table,
+ unsigned char type, bool params_only)
+{
+ struct tlv_proc *tproc;
+ int retv;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+
+ tproc = ipeh_tlv_get_proc_by_type(tlv_param_table, type);
+
+ if (params_only && tlv_internal_proc_type(tproc)) {
+ /* TLV was set by internal source, so maintain the
+ * non-parameter fields (i.e. the operations).
+ */
+ retv = __tlv_set_one(tlv_param_table, type,
+ &tlv_default_proc.params,
+ &tproc->ops);
+ } else {
+ retv = __tlv_unset_one(tlv_param_table, type);
+ }
+
+ mutex_unlock(&tlv_mutex);
+
+ return retv;
+}
+EXPORT_SYMBOL(__ipeh_tlv_unset);
+
+int ipeh_exthdrs_init(struct tlv_param_table *tlv_param_table,
+ const struct tlv_proc_init *tlv_init_params,
+ int num_init_params)
+{
+ struct tlv_param_table_data *tpt;
+ int pos = 0, i;
+ size_t tsize;
+
+ tsize = tlv_param_table_size(num_init_params + 1);
+
+ tpt = kvmalloc(tsize, GFP_KERNEL);
+ if (!tpt)
+ return -ENOMEM;
+
+ memset(tpt, 0, tsize);
+
+ /* Zeroth TLV proc entry is default */
+ tpt->types[pos++].proc = tlv_default_proc;
+
+ for (i = 0; i < num_init_params; i++, pos++) {
+ const struct tlv_proc_init *tpi = &tlv_init_params[i];
+
+ if (WARN_ON(tpi->type < 2)) {
+ /* Padding TLV initialized? */
+ goto err_inval;
+ }
+ if (WARN_ON(tpt->entries[tpi->type])) {
+ /* TLV type already set */
+ goto err_inval;
+ }
+
+ tpt->types[pos].proc = tpi->proc;
+ tpt->entries[tpi->type] = pos;
+ }
+
+ tpt->count = pos;
+
+ RCU_INIT_POINTER(tlv_param_table->data, tpt);
+
+ return 0;
+
+err_inval:
+ kvfree(tpt);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ipeh_exthdrs_init);
+
+static void tlv_destroy_param_table(struct tlv_param_table *tlv_param_table)
+{
+ struct tlv_param_table_data *tpt;
+
+ mutex_lock(&tlv_mutex);
+
+ tpt = rcu_dereference_protected(tlv_param_table->data,
+ lockdep_is_held(&tlv_mutex));
+ if (tpt) {
+ rcu_assign_pointer(tlv_param_table->data, NULL);
+ call_rcu(&tpt->rcu, tlv_param_table_release);
+ }
+
+ mutex_unlock(&tlv_mutex);
+}
+
+void ipeh_exthdrs_fini(struct tlv_param_table *tlv_param_table)
+{
+ tlv_destroy_param_table(tlv_param_table);
+}
+EXPORT_SYMBOL(ipeh_exthdrs_fini);
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index 032e0725846c..d4b373e1df96 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -11,11 +11,12 @@
#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/xfrm.h>
#endif
+#include <uapi/linux/ipeh.h>
/* Destination options header */
#if IS_ENABLED(CONFIG_IPV6_MIP6)
-static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
+static bool ipv6_dest_hao(unsigned int class, struct sk_buff *skb, int optoff)
{
struct ipv6_destopt_hao *hao;
struct inet6_skb_parm *opt = IP6CB(skb);
@@ -74,16 +75,6 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
}
#endif
-const struct tlvtype_proc tlvprocdestopt_lst[] = {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
- {
- .type = IPV6_TLV_HAO,
- .func = ipv6_dest_hao,
- },
-#endif
- {-1, NULL}
-};
-
/* Hop-by-hop options */
/* Note: we cannot rely on skb_dst(skb) before we assign it in
@@ -102,7 +93,7 @@ static inline struct net *ipv6_skb_net(struct sk_buff *skb)
/* Router Alert as of RFC 2711 */
-static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_ra(unsigned int class, struct sk_buff *skb, int optoff)
{
const unsigned char *nh = skb_network_header(skb);
@@ -120,7 +111,7 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
/* Jumbo payload */
-static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_jumbo(unsigned int class, struct sk_buff *skb, int optoff)
{
const unsigned char *nh = skb_network_header(skb);
struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
@@ -164,7 +155,8 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
/* CALIPSO RFC 5570 */
-static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_calipso(unsigned int class, struct sk_buff *skb,
+ int optoff)
{
const unsigned char *nh = skb_network_header(skb);
@@ -184,18 +176,45 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
return false;
}
-const struct tlvtype_proc tlvprochopopt_lst[] = {
+static const struct tlv_proc_init tlv_ipv6_init_params[] __initconst = {
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
{
- .type = IPV6_TLV_ROUTERALERT,
- .func = ipv6_hop_ra,
+ .type = IPV6_TLV_HAO,
+
+ .proc.ops.func = ipv6_dest_hao,
+ .proc.params.r.class = IPEH_TLV_CLASS_FLAG_DSTOPT,
},
+#endif
{
- .type = IPV6_TLV_JUMBO,
- .func = ipv6_hop_jumbo,
+ .type = IPV6_TLV_ROUTERALERT,
+
+ .proc.ops.func = ipv6_hop_ra,
+ .proc.params.r.class = IPEH_TLV_CLASS_FLAG_HOPOPT,
},
{
- .type = IPV6_TLV_CALIPSO,
- .func = ipv6_hop_calipso,
+ .type = IPV6_TLV_JUMBO,
+
+ .proc.ops.func = ipv6_hop_jumbo,
+ .proc.params.r.class = IPEH_TLV_CLASS_FLAG_HOPOPT,
+ },
+ {
+ .type = IPV6_TLV_CALIPSO,
+
+ .proc.ops.func = ipv6_hop_calipso,
+ .proc.params.r.class = IPEH_TLV_CLASS_FLAG_HOPOPT,
},
- { -1, }
};
+
+struct tlv_param_table __rcu ipv6_tlv_param_table;
+EXPORT_SYMBOL(ipv6_tlv_param_table);
+
+int __init ipv6_exthdrs_options_init(void)
+{
+ return ipeh_exthdrs_init(&ipv6_tlv_param_table, tlv_ipv6_init_params,
+ ARRAY_SIZE(tlv_ipv6_init_params));
+}
+
+void ipv6_exthdrs_options_exit(void)
+{
+ ipeh_exthdrs_fini(&ipv6_tlv_param_table);
+}
--
2.21.0
^ permalink raw reply related
* [net-next 1/7] ipeh: Create exthdrs_options.c and ipeh.h
From: Jeff Kirsher @ 2019-08-22 22:59 UTC (permalink / raw)
To: davem; +Cc: Tom Herbert, netdev, nhorman, sassmann, Andrew Bowers,
Jeff Kirsher
In-Reply-To: <20190822225940.14235-1-jeffrey.t.kirsher@intel.com>
From: Tom Herbert <tom@herbertland.com>
Create exthdrs_options.c to hold code related to specific Hop-by-Hop
and Destination extension header options. Move related functions in
exthdrs.c to the new file.
Create include net/ipeh.h to contain common definitions for IP extension
headers.
Signed-off-by: Tom Herbert <tom@herbertland.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
include/net/ipeh.h | 22 ++++
include/net/ipv6.h | 1 +
net/ipv6/Makefile | 2 +-
net/ipv6/exthdrs.c | 204 -------------------------------------
net/ipv6/exthdrs_options.c | 201 ++++++++++++++++++++++++++++++++++++
5 files changed, 225 insertions(+), 205 deletions(-)
create mode 100644 include/net/ipeh.h
create mode 100644 net/ipv6/exthdrs_options.c
diff --git a/include/net/ipeh.h b/include/net/ipeh.h
new file mode 100644
index 000000000000..ec2d18609e0f
--- /dev/null
+++ b/include/net/ipeh.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _NET_IPEH_H
+#define _NET_IPEH_H
+
+#include <linux/skbuff.h>
+
+/*
+ * Parsing tlv encoded headers.
+ *
+ * Parsing function "func" returns true, if parsing succeed
+ * and false, if it failed.
+ * It MUST NOT touch skb->h.
+ */
+struct tlvtype_proc {
+ int type;
+ bool (*func)(struct sk_buff *skb, int offset);
+};
+
+extern const struct tlvtype_proc tlvprocdestopt_lst[];
+extern const struct tlvtype_proc tlvprochopopt_lst[];
+
+#endif /* _NET_IPEH_H */
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 8dfc65639aa4..ec10fcab3f3d 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -20,6 +20,7 @@
#include <net/flow_dissector.h>
#include <net/snmp.h>
#include <net/netns/hash.h>
+#include <net/ipeh.h>
#define SIN6_LEN_RFC2133 24
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 8ccf35514015..df3919b44d93 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,7 +10,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
- udp_offload.o seg6.o fib6_notifier.o
+ udp_offload.o seg6.o fib6_notifier.o exthdrs_options.o
ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index ab5add0fe6b4..664491e8115f 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -39,7 +39,6 @@
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
-#include <net/calipso.h>
#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/xfrm.h>
#endif
@@ -51,19 +50,6 @@
#include <linux/uaccess.h>
-/*
- * Parsing tlv encoded headers.
- *
- * Parsing function "func" returns true, if parsing succeed
- * and false, if it failed.
- * It MUST NOT touch skb->h.
- */
-
-struct tlvtype_proc {
- int type;
- bool (*func)(struct sk_buff *skb, int offset);
-};
-
/*********************
Generic functions
*********************/
@@ -200,80 +186,6 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
return false;
}
-/*****************************
- Destination options header.
- *****************************/
-
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
-{
- struct ipv6_destopt_hao *hao;
- struct inet6_skb_parm *opt = IP6CB(skb);
- struct ipv6hdr *ipv6h = ipv6_hdr(skb);
- int ret;
-
- if (opt->dsthao) {
- net_dbg_ratelimited("hao duplicated\n");
- goto discard;
- }
- opt->dsthao = opt->dst1;
- opt->dst1 = 0;
-
- hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
-
- if (hao->length != 16) {
- net_dbg_ratelimited("hao invalid option length = %d\n",
- hao->length);
- goto discard;
- }
-
- if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
- net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
- &hao->addr);
- goto discard;
- }
-
- ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
- (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
- if (unlikely(ret < 0))
- goto discard;
-
- if (skb_cloned(skb)) {
- if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
- goto discard;
-
- /* update all variable using below by copied skbuff */
- hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
- optoff);
- ipv6h = ipv6_hdr(skb);
- }
-
- if (skb->ip_summed == CHECKSUM_COMPLETE)
- skb->ip_summed = CHECKSUM_NONE;
-
- swap(ipv6h->saddr, hao->addr);
-
- if (skb->tstamp == 0)
- __net_timestamp(skb);
-
- return true;
-
- discard:
- kfree_skb(skb);
- return false;
-}
-#endif
-
-static const struct tlvtype_proc tlvprocdestopt_lst[] = {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
- {
- .type = IPV6_TLV_HAO,
- .func = ipv6_dest_hao,
- },
-#endif
- {-1, NULL}
-};
-
static int ipv6_destopt_rcv(struct sk_buff *skb)
{
struct inet6_dev *idev = __in6_dev_get(skb->dev);
@@ -702,122 +614,6 @@ void ipv6_exthdrs_exit(void)
inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
}
-/**********************************
- Hop-by-hop options.
- **********************************/
-
-/*
- * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
- */
-static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
-{
- return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev);
-}
-
-static inline struct net *ipv6_skb_net(struct sk_buff *skb)
-{
- return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
-}
-
-/* Router Alert as of RFC 2711 */
-
-static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
-{
- const unsigned char *nh = skb_network_header(skb);
-
- if (nh[optoff + 1] == 2) {
- IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
- memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra));
- return true;
- }
- net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n",
- nh[optoff + 1]);
- kfree_skb(skb);
- return false;
-}
-
-/* Jumbo payload */
-
-static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
-{
- const unsigned char *nh = skb_network_header(skb);
- struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
- struct net *net = ipv6_skb_net(skb);
- u32 pkt_len;
-
- if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
- net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
- nh[optoff+1]);
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
- goto drop;
- }
-
- pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
- if (pkt_len <= IPV6_MAXPLEN) {
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
- return false;
- }
- if (ipv6_hdr(skb)->payload_len) {
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
- return false;
- }
-
- if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS);
- goto drop;
- }
-
- if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
- goto drop;
-
- IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
- return true;
-
-drop:
- kfree_skb(skb);
- return false;
-}
-
-/* CALIPSO RFC 5570 */
-
-static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
-{
- const unsigned char *nh = skb_network_header(skb);
-
- if (nh[optoff + 1] < 8)
- goto drop;
-
- if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
- goto drop;
-
- if (!calipso_validate(skb, nh + optoff))
- goto drop;
-
- return true;
-
-drop:
- kfree_skb(skb);
- return false;
-}
-
-static const struct tlvtype_proc tlvprochopopt_lst[] = {
- {
- .type = IPV6_TLV_ROUTERALERT,
- .func = ipv6_hop_ra,
- },
- {
- .type = IPV6_TLV_JUMBO,
- .func = ipv6_hop_jumbo,
- },
- {
- .type = IPV6_TLV_CALIPSO,
- .func = ipv6_hop_calipso,
- },
- { -1, }
-};
-
int ipv6_parse_hopopts(struct sk_buff *skb)
{
struct inet6_skb_parm *opt = IP6CB(skb);
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
new file mode 100644
index 000000000000..032e0725846c
--- /dev/null
+++ b/net/ipv6/exthdrs_options.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/errno.h>
+#include <linux/in6.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+#include <net/calipso.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+#include <net/xfrm.h>
+#endif
+
+/* Destination options header */
+
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
+{
+ struct ipv6_destopt_hao *hao;
+ struct inet6_skb_parm *opt = IP6CB(skb);
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ int ret;
+
+ if (opt->dsthao) {
+ net_dbg_ratelimited("hao duplicated\n");
+ goto discard;
+ }
+ opt->dsthao = opt->dst1;
+ opt->dst1 = 0;
+
+ hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
+
+ if (hao->length != 16) {
+ net_dbg_ratelimited("hao invalid option length = %d\n",
+ hao->length);
+ goto discard;
+ }
+
+ if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
+ net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
+ &hao->addr);
+ goto discard;
+ }
+
+ ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
+ (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
+ if (unlikely(ret < 0))
+ goto discard;
+
+ if (skb_cloned(skb)) {
+ if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ goto discard;
+
+ /* update all variable using below by copied skbuff */
+ hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
+ optoff);
+ ipv6h = ipv6_hdr(skb);
+ }
+
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->ip_summed = CHECKSUM_NONE;
+
+ swap(ipv6h->saddr, hao->addr);
+
+ if (skb->tstamp == 0)
+ __net_timestamp(skb);
+
+ return true;
+
+ discard:
+ kfree_skb(skb);
+ return false;
+}
+#endif
+
+const struct tlvtype_proc tlvprocdestopt_lst[] = {
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+ {
+ .type = IPV6_TLV_HAO,
+ .func = ipv6_dest_hao,
+ },
+#endif
+ {-1, NULL}
+};
+
+/* Hop-by-hop options */
+
+/* Note: we cannot rely on skb_dst(skb) before we assign it in
+ * ip6_route_input().
+ */
+static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
+{
+ return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) :
+ __in6_dev_get(skb->dev);
+}
+
+static inline struct net *ipv6_skb_net(struct sk_buff *skb)
+{
+ return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
+}
+
+/* Router Alert as of RFC 2711 */
+
+static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
+{
+ const unsigned char *nh = skb_network_header(skb);
+
+ if (nh[optoff + 1] == 2) {
+ IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
+ memcpy(&IP6CB(skb)->ra, nh + optoff + 2,
+ sizeof(IP6CB(skb)->ra));
+ return true;
+ }
+ net_dbg_ratelimited("%s: wrong RA length %d\n",
+ __func__, nh[optoff + 1]);
+ kfree_skb(skb);
+ return false;
+}
+
+/* Jumbo payload */
+
+static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
+{
+ const unsigned char *nh = skb_network_header(skb);
+ struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
+ struct net *net = ipv6_skb_net(skb);
+ u32 pkt_len;
+
+ if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
+ net_dbg_ratelimited("%s: wrong jumbo opt length/alignment %d\n",
+ __func__, nh[optoff + 1]);
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+ goto drop;
+ }
+
+ pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
+ if (pkt_len <= IPV6_MAXPLEN) {
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff + 2);
+ return false;
+ }
+ if (ipv6_hdr(skb)->payload_len) {
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
+ return false;
+ }
+
+ if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS);
+ goto drop;
+ }
+
+ if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
+ goto drop;
+
+ IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
+ return true;
+
+drop:
+ kfree_skb(skb);
+ return false;
+}
+
+/* CALIPSO RFC 5570 */
+
+static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
+{
+ const unsigned char *nh = skb_network_header(skb);
+
+ if (nh[optoff + 1] < 8)
+ goto drop;
+
+ if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
+ goto drop;
+
+ if (!calipso_validate(skb, nh + optoff))
+ goto drop;
+
+ return true;
+
+drop:
+ kfree_skb(skb);
+ return false;
+}
+
+const struct tlvtype_proc tlvprochopopt_lst[] = {
+ {
+ .type = IPV6_TLV_ROUTERALERT,
+ .func = ipv6_hop_ra,
+ },
+ {
+ .type = IPV6_TLV_JUMBO,
+ .func = ipv6_hop_jumbo,
+ },
+ {
+ .type = IPV6_TLV_CALIPSO,
+ .func = ipv6_hop_calipso,
+ },
+ { -1, }
+};
--
2.21.0
^ permalink raw reply related
* [net-next 0/7][pull request] ipv6: Extension header infrastructure
From: Jeff Kirsher @ 2019-08-22 22:59 UTC (permalink / raw)
To: davem; +Cc: Jeff Kirsher, netdev, nhorman, sassmann
This patchset improves the IPv6 extension header infrastructure
to make extension headers more usable and scalable.
- Reorganize extension header files to separate out common
API components
- Create common TLV handler that will can be used in other use
cases (e.g. segment routing TLVs, UDP options)
- Allow registration of TLV handlers
- Elaborate on the TLV tables to include more characteristics
- Add a netlink interface to set TLV parameters (such as
alignment requirements, authorization to send, etc.)
- Enhance validation of TLVs being sent. Validation is strict
(unless overridden by admin) following that sending clause
of the robustness principle
- Allow non-privileged users to set Hop-by-Hop and Destination
Options if authorized by the admin
The following are changes since commit c76c992525245ec1c7b6738bf887c42099abab02:
nexthops: remove redundant assignment to variable err
and are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue 1GbE
Tom Herbert (7):
ipeh: Create exthdrs_options.c and ipeh.h
ipeh: Move generic EH functions to exthdrs_common.c
ipeh: Generic TLV parser
ip6tlvs: Registration of TLV handlers and parameters
ip6tlvs: Add TX parameters
ip6tlvs: Add netlink interface
ip6tlvs: Validation of TX Destination and Hop-by-Hop options
include/net/ipeh.h | 208 +++++++
include/net/ipv6.h | 12 +-
include/uapi/linux/in6.h | 6 +
include/uapi/linux/ipeh.h | 53 ++
net/dccp/ipv6.c | 2 +-
net/ipv6/Kconfig | 4 +
net/ipv6/Makefile | 3 +-
net/ipv6/calipso.c | 6 +-
net/ipv6/datagram.c | 51 +-
net/ipv6/exthdrs.c | 505 ++--------------
net/ipv6/exthdrs_common.c | 1158 ++++++++++++++++++++++++++++++++++++
net/ipv6/exthdrs_options.c | 342 +++++++++++
net/ipv6/ipv6_sockglue.c | 39 +-
net/ipv6/raw.c | 2 +-
net/ipv6/tcp_ipv6.c | 2 +-
net/ipv6/udp.c | 2 +-
net/l2tp/l2tp_ip6.c | 2 +-
net/sctp/ipv6.c | 2 +-
18 files changed, 1881 insertions(+), 518 deletions(-)
create mode 100644 include/net/ipeh.h
create mode 100644 include/uapi/linux/ipeh.h
create mode 100644 net/ipv6/exthdrs_common.c
create mode 100644 net/ipv6/exthdrs_options.c
--
2.21.0
^ permalink raw reply
* Re: [PATCH net-next 04/10] net: sched: notify classifier on successful offload add/delete
From: Jakub Kicinski @ 2019-08-22 22:58 UTC (permalink / raw)
To: Vlad Buslov; +Cc: netdev, jhs, xiyou.wangcong, jiri, davem, pablo, Jiri Pirko
In-Reply-To: <20190822124353.16902-5-vladbu@mellanox.com>
On Thu, 22 Aug 2019 15:43:47 +0300, Vlad Buslov wrote:
> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> index 4215c849f4a3..d8ef7a9e6906 100644
> --- a/net/sched/cls_api.c
> +++ b/net/sched/cls_api.c
> @@ -3099,9 +3099,13 @@ int tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp,
> }
>
> ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
> - if (ok_count > 0)
> - tc_cls_offload_cnt_update(block, tp, in_hw_count, flags,
> - ok_count, true);
> + if (ok_count >= 0) {
> + if (tp->ops->hw_add)
> + tp->ops->hw_add(tp, type_data);
> + if (ok_count > 0)
> + tc_cls_offload_cnt_update(block, tp, in_hw_count, flags,
> + ok_count, true);
> + }
nit:
if (ok_count < 0)
goto err_unlock;
if (tp->ops->hw_add)
tp->ops->hw_add(tp, type_data);
if (ok_count > 0)
tc_cls_offload_cnt_update(block, tp, in_hw_count, flags,
ok_count, true);
IOW try to keep the "success flow" unindented.
> errout:
> up_read(&block->cb_lock);
> return ok_count;
^ permalink raw reply
* Re: [PATCH] net: Add the same IP detection for duplicate address.
From: David Miller @ 2019-08-22 22:57 UTC (permalink / raw)
To: liudongxu3; +Cc: kuznet, yoshfuji, netdev, linux-kernel
In-Reply-To: <20190821032000.10540-1-liudongxu3@huawei.com>
From: Dongxu Liu <liudongxu3@huawei.com>
Date: Wed, 21 Aug 2019 11:20:00 +0800
> The network sends an ARP REQUEST packet to determine
> whether there is a host with the same IP.
> Windows and some other hosts may send the source IP
> address instead of 0.
> When IN_DEV_ORCONF(in_dev, DROP_GRATUITOUS_ARP) is enable,
> the REQUEST will be dropped.
> When IN_DEV_ORCONF(in_dev, DROP_GRATUITOUS_ARP) is disable,
> The case should be added to the IP conflict handling process.
>
> Signed-off-by: Dongxu Liu <liudongxu3@huawei.com>
Even documents like RFC 5227 talk about there being a zero source
protocol address here (read the last two paragraphis of section
1.2. "relationship to 826"):
====================
An ARP Probe with an all-zero 'sender IP address' may ostensibly be
merely asking an innocent question ("Is anyone using this
address?"), but an intelligent implementation that knows how IPv4
Address Conflict Detection works should be able to recognize this
question as the precursor to claiming the address.
====================
I do not understand why we have to add a special case for an
implementation that has decided, after so many decades of our existing
behavior, to put something of than zero in the source protocol
address.
I'm not applying this, I do not see a legitimate justification for
this change at all.
Sorry.
^ permalink raw reply
* Re: [PATCH net-next 03/10] net: sched: refactor block offloads counter usage
From: Jakub Kicinski @ 2019-08-22 22:53 UTC (permalink / raw)
To: Vlad Buslov; +Cc: netdev, jhs, xiyou.wangcong, jiri, davem, pablo, Jiri Pirko
In-Reply-To: <20190822124353.16902-4-vladbu@mellanox.com>
On Thu, 22 Aug 2019 15:43:46 +0300, Vlad Buslov wrote:
> Without rtnl lock protection filters can no longer safely manage block
> offloads counter themselves. Refactor cls API to protect block offloadcnt
> with tcf_block->cb_lock that is already used to protect driver callback
> list and nooffloaddevcnt counter. The counter can be modified by concurrent
> tasks by new functions that execute block callbacks (which is safe with
> previous patch that changed its type to atomic_t), however, block
> bind/unbind code that checks the counter value takes cb_lock in write mode
> to exclude any concurrent modifications. This approach prevents race
> conditions between bind/unbind and callback execution code but allows for
> concurrency for tc rule update path.
>
> Move block offload counter, filter in hardware counter and filter flags
> management from classifiers into cls hardware offloads API. Make functions
> tcf_block_offload_inc() and tcf_block_offload_dec() to be cls API private.
> Implement following new cls API to be used instead:
>
> tc_setup_cb_add() - non-destructive filter add. If filter that wasn't
> already in hardware is successfully offloaded, increment block offloads
> counter, set filter in hardware counter and flag. On failure, previously
> offloaded filter is considered to be intact and offloads counter is not
> decremented.
>
> tc_setup_cb_replace() - destructive filter replace. Release existing
> filter block offload counter and reset its in hardware counter and flag.
> Set new filter in hardware counter and flag. On failure, previously
> offloaded filter is considered to be destroyed and offload counter is
> decremented.
>
> tc_setup_cb_destroy() - filter destroy. Unconditionally decrement block
> offloads counter.
>
> Refactor all offload-capable classifiers to atomically offload filters to
> hardware, change block offload counter, and set filter in hardware counter
> and flag by means of the new cls API functions.
>
> Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
> Acked-by: Jiri Pirko <jiri@mellanox.com>
Looks good, minor nits
> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> index 8502bd006b37..4215c849f4a3 100644
> --- a/net/sched/cls_api.c
> +++ b/net/sched/cls_api.c
> @@ -3000,13 +3000,97 @@ int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
> }
> EXPORT_SYMBOL(tcf_exts_dump_stats);
>
> -int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
> - void *type_data, bool err_stop)
> +static void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
> +{
> + if (*flags & TCA_CLS_FLAGS_IN_HW)
> + return;
> + *flags |= TCA_CLS_FLAGS_IN_HW;
> + atomic_inc(&block->offloadcnt);
> +}
> +
> +static void tcf_block_offload_dec(struct tcf_block *block, u32 *flags)
> +{
> + if (!(*flags & TCA_CLS_FLAGS_IN_HW))
> + return;
> + *flags &= ~TCA_CLS_FLAGS_IN_HW;
> + atomic_dec(&block->offloadcnt);
> +}
> +
> +void tc_cls_offload_cnt_update(struct tcf_block *block, struct tcf_proto *tp,
> + u32 *cnt, u32 *flags, u32 diff, bool add)
> +{
> + lockdep_assert_held(&block->cb_lock);
> +
> + spin_lock(&tp->lock);
> + if (add) {
> + if (!*cnt)
> + tcf_block_offload_inc(block, flags);
> + (*cnt) += diff;
brackets unnecessary
> + } else {
> + (*cnt) -= diff;
> + if (!*cnt)
> + tcf_block_offload_dec(block, flags);
> + }
> + spin_unlock(&tp->lock);
> +}
> +EXPORT_SYMBOL(tc_cls_offload_cnt_update);
> +
> +static void
> +tc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp,
> + u32 *cnt, u32 *flags)
> +{
> + lockdep_assert_held(&block->cb_lock);
> +
> + spin_lock(&tp->lock);
> + tcf_block_offload_dec(block, flags);
> + (*cnt) = 0;
ditto
> + spin_unlock(&tp->lock);
> +}
> +
> +static int
> +__tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
> + void *type_data, bool err_stop)
> {
> struct flow_block_cb *block_cb;
> int ok_count = 0;
> int err;
>
> + list_for_each_entry(block_cb, &block->flow_block.cb_list, list) {
> + err = block_cb->cb(type, type_data, block_cb->cb_priv);
> + if (err) {
> + if (err_stop)
> + return err;
> + } else {
> + ok_count++;
> + }
> + }
> + return ok_count;
> +}
> +
> +int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
> + void *type_data, bool err_stop, bool rtnl_held)
> +{
> + int ok_count;
> +
> + down_read(&block->cb_lock);
> + ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
> + up_read(&block->cb_lock);
> + return ok_count;
> +}
> +EXPORT_SYMBOL(tc_setup_cb_call);
> +
> +/* Non-destructive filter add. If filter that wasn't already in hardware is
> + * successfully offloaded, increment block offloads counter. On failure,
> + * previously offloaded filter is considered to be intact and offloads counter
> + * is not decremented.
> + */
> +
Spurious new line here?
> +int tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp,
> + enum tc_setup_type type, void *type_data, bool err_stop,
> + u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
> +{
> + int ok_count;
> +
> down_read(&block->cb_lock);
> /* Make sure all netdevs sharing this block are offload-capable. */
> if (block->nooffloaddevcnt && err_stop) {
> @@ -3014,22 +3098,67 @@ int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
> goto errout;
> }
>
> - list_for_each_entry(block_cb, &block->flow_block.cb_list, list) {
> - err = block_cb->cb(type, type_data, block_cb->cb_priv);
> - if (err) {
> - if (err_stop) {
> - ok_count = err;
> - goto errout;
> - }
> - } else {
> - ok_count++;
> - }
> + ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
> + if (ok_count > 0)
> + tc_cls_offload_cnt_update(block, tp, in_hw_count, flags,
> + ok_count, true);
> +errout:
and the labels again
> + up_read(&block->cb_lock);
> + return ok_count;
> +}
> +EXPORT_SYMBOL(tc_setup_cb_add);
> +
> +/* Destructive filter replace. If filter that wasn't already in hardware is
> + * successfully offloaded, increment block offload counter. On failure,
> + * previously offloaded filter is considered to be destroyed and offload counter
> + * is decremented.
> + */
> +
spurious new line?
> +int tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp,
> + enum tc_setup_type type, void *type_data, bool err_stop,
> + u32 *old_flags, unsigned int *old_in_hw_count,
> + u32 *new_flags, unsigned int *new_in_hw_count,
> + bool rtnl_held)
> +{
> + int ok_count;
> +
> + down_read(&block->cb_lock);
> + /* Make sure all netdevs sharing this block are offload-capable. */
> + if (block->nooffloaddevcnt && err_stop) {
> + ok_count = -EOPNOTSUPP;
> + goto errout;
> }
> +
> + tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags);
> +
> + ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
> + if (ok_count > 0)
> + tc_cls_offload_cnt_update(block, tp, new_in_hw_count, new_flags,
> + ok_count, true);
> errout:
> up_read(&block->cb_lock);
> return ok_count;
> }
> -EXPORT_SYMBOL(tc_setup_cb_call);
> +EXPORT_SYMBOL(tc_setup_cb_replace);
> +
> +/* Destroy filter and decrement block offload counter, if filter was previously
> + * offloaded.
> + */
> +
hm.. is this gap between comment and function it pertains to
intentional?
> +int tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp,
> + enum tc_setup_type type, void *type_data, bool err_stop,
> + u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
> +{
> + int ok_count;
> +
> + down_read(&block->cb_lock);
> + ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
> +
> + tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags);
> + up_read(&block->cb_lock);
> + return ok_count;
> +}
> +EXPORT_SYMBOL(tc_setup_cb_destroy);
>
> int tc_setup_flow_action(struct flow_action *flow_action,
> const struct tcf_exts *exts)
> diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
> index 3f7a9c02b70c..7f304db7e697 100644
> --- a/net/sched/cls_bpf.c
> +++ b/net/sched/cls_bpf.c
> @@ -162,17 +162,21 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
> cls_bpf.name = obj->bpf_name;
> cls_bpf.exts_integrated = obj->exts_integrated;
>
> - if (oldprog)
> - tcf_block_offload_dec(block, &oldprog->gen_flags);
> + if (cls_bpf.oldprog)
why the change from oldprog to cls_bpf.oldprog?
> + err = tc_setup_cb_replace(block, tp, TC_SETUP_CLSBPF, &cls_bpf,
> + skip_sw, &oldprog->gen_flags,
> + &oldprog->in_hw_count,
> + &prog->gen_flags, &prog->in_hw_count,
> + true);
> + else
> + err = tc_setup_cb_add(block, tp, TC_SETUP_CLSBPF, &cls_bpf,
> + skip_sw, &prog->gen_flags,
> + &prog->in_hw_count, true);
>
> - err = tc_setup_cb_call(block, TC_SETUP_CLSBPF, &cls_bpf, skip_sw);
> if (prog) {
> if (err < 0) {
> cls_bpf_offload_cmd(tp, oldprog, prog, extack);
> return err;
> - } else if (err > 0) {
> - prog->in_hw_count = err;
> - tcf_block_offload_inc(block, &prog->gen_flags);
> }
> }
>
> @@ -230,7 +234,7 @@ static void cls_bpf_offload_update_stats(struct tcf_proto *tp,
> cls_bpf.name = prog->bpf_name;
> cls_bpf.exts_integrated = prog->exts_integrated;
>
> - tc_setup_cb_call(block, TC_SETUP_CLSBPF, &cls_bpf, false);
> + tc_setup_cb_call(block, TC_SETUP_CLSBPF, &cls_bpf, false, true);
> }
>
> static int cls_bpf_init(struct tcf_proto *tp)
> @@ -680,8 +684,8 @@ static int cls_bpf_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb
> continue;
> }
>
> - tc_cls_offload_cnt_update(block, &prog->in_hw_count,
> - &prog->gen_flags, add);
> + tc_cls_offload_cnt_update(block, tp, &prog->in_hw_count,
> + &prog->gen_flags, 1, add);
Since we're adding those higher level add/replace/destroy helpers,
would it also be possible to have a helper which takes care of
reoffload? tc_cls_offload_cnt_update() is kind of low level now, it'd
be cool to also hide it in the core.
> }
>
> return 0;
> diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
> index 054123742e32..0001a933d48b 100644
> --- a/net/sched/cls_flower.c
> +++ b/net/sched/cls_flower.c
> @@ -419,10 +419,10 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
> cls_flower.command = FLOW_CLS_DESTROY;
> cls_flower.cookie = (unsigned long) f;
>
> - tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
> + tc_setup_cb_destroy(block, tp, TC_SETUP_CLSFLOWER, &cls_flower, false,
> + &f->flags, &f->in_hw_count, true);
> spin_lock(&tp->lock);
> list_del_init(&f->hw_list);
> - tcf_block_offload_dec(block, &f->flags);
> spin_unlock(&tp->lock);
>
> if (!rtnl_held)
> @@ -466,18 +466,15 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
> goto errout;
> }
>
> - err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw);
> + err = tc_setup_cb_add(block, tp, TC_SETUP_CLSFLOWER, &cls_flower,
> + skip_sw, &f->flags, &f->in_hw_count, true);
> kfree(cls_flower.rule);
>
> if (err < 0) {
> fl_hw_destroy_filter(tp, f, true, NULL);
> goto errout;
> } else if (err > 0) {
> - f->in_hw_count = err;
> err = 0;
Why does the tc_setup_cb* API still return the positive values, the
callers should no longer care, right?
> - spin_lock(&tp->lock);
> - tcf_block_offload_inc(block, &f->flags);
> - spin_unlock(&tp->lock);
> }
>
> if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW)) {
^ permalink raw reply
* Re: r8169: regression on MIPS/Loongson
From: Heiner Kallweit @ 2019-08-22 22:52 UTC (permalink / raw)
To: Aaro Koskinen; +Cc: netdev, linux-mips
In-Reply-To: <20190822222549.GF30291@darkstar.musicnaut.iki.fi>
On 23.08.2019 00:25, Aaro Koskinen wrote:
> Hi,
>
> After upgrading from v5.2 to v5.3-rc5 on MIPS/Loongson board, copying
> large files from network with scp started to fail with "Integrity error".
> Bisected to:
>
> f072218cca5b076dd99f3dfa3aaafedfd0023a51 is the first bad commit
> commit f072218cca5b076dd99f3dfa3aaafedfd0023a51
> Author: Heiner Kallweit <hkallweit1@gmail.com>
> Date: Thu Jun 27 23:19:09 2019 +0200
>
> r8169: remove not needed call to dma_sync_single_for_device
>
> Any idea what goes wrong? Should this change be reverted?
>
Typically the Realtek chips are used on Intel platforms and I haven't
seen any such report yet, so it seems to be platform-specific.
Which board (DT config) is it, and can you provide a full dmesg?
> A.
>
Heiner
^ permalink raw reply
* Re: [PATCH v2 bpf-next 1/4] bpf: unprivileged BPF access via /dev/bpf
From: Alexei Starovoitov @ 2019-08-22 22:48 UTC (permalink / raw)
To: Daniel Borkmann
Cc: Andy Lutomirski, Song Liu, Kees Cook, Networking, bpf,
Alexei Starovoitov, Kernel Team, Lorenz Bauer, Jann Horn, Greg KH,
Linux API, LSM List, Chenbo Feng
In-Reply-To: <98fee747-795a-ff10-fa98-10ddb5afcc03@iogearbox.net>
On Thu, Aug 22, 2019 at 04:17:43PM +0200, Daniel Borkmann wrote:
>
> > > Hence unprivileged bpf is actually something that can be deprecated.
>
> There is actually a publicly known use-case on unprivileged bpf wrt
> socket filters, see the SO_ATTACH_BPF on sockets section as an example:
>
> https://blog.cloudflare.com/cloudflare-architecture-and-how-bpf-eats-the-world/
>
> If I'd have to take a good guess, I'd think it's major use-case is in
> SO_ATTACH_REUSEPORT_EBPF in the wild, I don't think the sysctl can be
> outright flipped or deprecated w/o breaking existing applications unless
> it's cleanly modeled into some sort of customizable CAP_BPF* type policy
> (more below) where this would be the lowest common denominator.
The cloudflare use case is the perfect example that a lot of program types
are used together.
Do people use SO_ATTACH_BPF ? Of course.
All program types are used by somebody. Before accepting them we had long
conversations with authors to understand that the use cases are real.
Some progs are probably used less than others by now.
Like cls_bpf without exts_integrated is probably not used at all.
We still have to support it, of course.
That cloudflare example demonstrates that kernel.unprivileged_bpf_disabled=1
is a reality. Companies that care about security already switched it on.
Different bits in cloudflare setup need different level of capabilities.
Some (like SO_ATTACH_BPF) need unpriv. Another need CAP_NET_ADMIN.
But common demoninator for them all is still CAP_SYS_ADMIN.
And that's why the system as a whole is not as safe as it could have
been with CAP_BPF. The system needs root in many places.
Folks going out of the way to reduce that SYS_ADMIN to something less.
The example with systemd and NET_ADMIN is just one of them.
^ permalink raw reply
* Re: [v4] ocelot_ace: fix action of trap
From: David Miller @ 2019-08-22 22:44 UTC (permalink / raw)
To: yangbo.lu
Cc: netdev, allan.nielsen, alexandre.belloni, UNGLinuxDriver, andrew
In-Reply-To: <20190821015912.43151-1-yangbo.lu@nxp.com>
From: Yangbo Lu <yangbo.lu@nxp.com>
Date: Wed, 21 Aug 2019 09:59:12 +0800
> The trap action should be copying the frame to CPU and
> dropping it for forwarding, but current setting was just
> copying frame to CPU.
>
> Fixes: b596229448dd ("net: mscc: ocelot: Add support for tcam")
> Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
> Acked-by: Allan W. Nielsen <allan.nielsen@microchip.com>
> ---
> Changes for v4:
> - Added ACK and Fixes info in commit message.
> Changes for v3:
> - Set MASK_MODE to 1 for dropping forwarding.
> - Dropped other patches of patch-set.
> Changes for v2:
> - None.
Applied.
^ permalink raw reply
* Re: [PATCH v2] sock: fix potential memory leak in proto_register()
From: David Miller @ 2019-08-22 22:43 UTC (permalink / raw)
To: zhang.lin16
Cc: ast, daniel, kafai, songliubraving, yhs, willemb, edumazet,
deepa.kernel, arnd, dh.herrmann, gnault, netdev, linux-kernel,
bpf, xue.zhihong, wang.yi59, jiang.xuexin
In-Reply-To: <1566348158-43942-1-git-send-email-zhang.lin16@zte.com.cn>
From: zhanglin <zhang.lin16@zte.com.cn>
Date: Wed, 21 Aug 2019 08:42:38 +0800
> @@ -3243,18 +3245,24 @@ int proto_register(struct proto *prot, int alloc_slab)
> }
>
> mutex_lock(&proto_list_mutex);
> + if (assign_proto_idx(prot)) {
> + mutex_unlock(&proto_list_mutex);
> + goto out_free_timewait_sock_slab_name;
> + }
Propagate the error code properly please.
^ permalink raw reply
* RE: [PATCH net-next,v5, 0/6] Add software backchannel and mlx5e HV VHCA stats
From: Haiyang Zhang @ 2019-08-22 22:43 UTC (permalink / raw)
To: David Miller, eranbe@mellanox.com
Cc: sashal@kernel.org, saeedm@mellanox.com, leon@kernel.org,
lorenzo.pieralisi@arm.com, bhelgaas@google.com,
linux-pci@vger.kernel.org, linux-hyperv@vger.kernel.org,
netdev@vger.kernel.org, KY Srinivasan, Stephen Hemminger,
linux-kernel@vger.kernel.org
In-Reply-To: <20190822.153912.2269276523787180347.davem@davemloft.net>
> -----Original Message-----
> From: David Miller <davem@davemloft.net>
> Sent: Thursday, August 22, 2019 3:39 PM
> To: Haiyang Zhang <haiyangz@microsoft.com>
> Cc: sashal@kernel.org; saeedm@mellanox.com; leon@kernel.org;
> eranbe@mellanox.com; lorenzo.pieralisi@arm.com; bhelgaas@google.com;
> linux-pci@vger.kernel.org; linux-hyperv@vger.kernel.org;
> netdev@vger.kernel.org; KY Srinivasan <kys@microsoft.com>; Stephen
> Hemminger <sthemmin@microsoft.com>; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH net-next,v5, 0/6] Add software backchannel and mlx5e
> HV VHCA stats
>
> From: Haiyang Zhang <haiyangz@microsoft.com>
> Date: Thu, 22 Aug 2019 22:37:13 +0000
>
> > The v5 is pretty much the same as v4, except Eran had a fix to patch #3 in
> response to
> > Leon Romanovsky <leon@kernel.org>.
>
> Well you now have to send me a patch relative to v4 in order to fix that.
>
> When I say "applied", the series is in my tree and is therefore permanent.
> It is therefore never appropriate to then post a new version of the series.
Thanks.
Eran, could you submit another patch for the fix to patch #3?
- Haiyang
^ permalink raw reply
* Re: [PATCH net-next v4 0/2] dt-bindings: net: meson-dwmac: convert to yaml
From: David Miller @ 2019-08-22 22:42 UTC (permalink / raw)
To: narmstrong
Cc: robh+dt, martin.blumenstingl, devicetree, netdev, linux-amlogic,
linux-arm-kernel, linux-kernel
In-Reply-To: <20190820075742.14857-1-narmstrong@baylibre.com>
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Tue, 20 Aug 2019 09:57:40 +0200
> This patchsets converts the Amlogic Meson DWMAC glue bindings over to
> YAML schemas using the already converted dwmac bindings.
>
> The first patch is needed because the Amlogic glue needs a supplementary
> reg cell to access the DWMAC glue registers.
>
> Changes since v3:
> - Specified net-next target tree
>
> Changes since v2:
> - Added review tags
> - Updated allwinner,sun7i-a20-gmac.yaml reg maxItems
Series applied, thanks.
^ permalink raw reply
* Re: [PATCH net-next,v5, 0/6] Add software backchannel and mlx5e HV VHCA stats
From: David Miller @ 2019-08-22 22:39 UTC (permalink / raw)
To: haiyangz
Cc: sashal, saeedm, leon, eranbe, lorenzo.pieralisi, bhelgaas,
linux-pci, linux-hyperv, netdev, kys, sthemmin, linux-kernel
In-Reply-To: <DM6PR21MB133743FB2006A28AE10A170CCAA50@DM6PR21MB1337.namprd21.prod.outlook.com>
From: Haiyang Zhang <haiyangz@microsoft.com>
Date: Thu, 22 Aug 2019 22:37:13 +0000
> The v5 is pretty much the same as v4, except Eran had a fix to patch #3 in response to
> Leon Romanovsky <leon@kernel.org>.
Well you now have to send me a patch relative to v4 in order to fix that.
When I say "applied", the series is in my tree and is therefore permanent.
It is therefore never appropriate to then post a new version of the series.
^ permalink raw reply
* RE: [PATCH net-next,v5, 0/6] Add software backchannel and mlx5e HV VHCA stats
From: Haiyang Zhang @ 2019-08-22 22:37 UTC (permalink / raw)
To: David Miller
Cc: sashal@kernel.org, saeedm@mellanox.com, leon@kernel.org,
eranbe@mellanox.com, lorenzo.pieralisi@arm.com,
bhelgaas@google.com, linux-pci@vger.kernel.org,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org,
KY Srinivasan, Stephen Hemminger, linux-kernel@vger.kernel.org
In-Reply-To: <20190822.153315.1245817410062415025.davem@davemloft.net>
> -----Original Message-----
> From: David Miller <davem@davemloft.net>
> Sent: Thursday, August 22, 2019 3:33 PM
> To: Haiyang Zhang <haiyangz@microsoft.com>
> Cc: sashal@kernel.org; saeedm@mellanox.com; leon@kernel.org;
> eranbe@mellanox.com; lorenzo.pieralisi@arm.com; bhelgaas@google.com;
> linux-pci@vger.kernel.org; linux-hyperv@vger.kernel.org;
> netdev@vger.kernel.org; KY Srinivasan <kys@microsoft.com>; Stephen
> Hemminger <sthemmin@microsoft.com>; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH net-next,v5, 0/6] Add software backchannel and mlx5e
> HV VHCA stats
>
>
> I applied this patch series already to net-next, what are you doing?
The v5 is pretty much the same as v4, except Eran had a fix to patch #3 in response to
Leon Romanovsky <leon@kernel.org>.
Thanks,
- Haiyang
^ permalink raw reply
* Re: New skb extension for use by LSMs (skb "security blob")?
From: David Miller @ 2019-08-22 22:36 UTC (permalink / raw)
To: casey; +Cc: fw, paul, netdev, linux-security-module, selinux
In-Reply-To: <5d524e45-0d80-3a1c-4fd2-7610d2197bf8@schaufler-ca.com>
From: Casey Schaufler <casey@schaufler-ca.com>
Date: Thu, 22 Aug 2019 15:34:44 -0700
> On 8/22/2019 3:28 PM, David Miller wrote:
>> From: Casey Schaufler <casey@schaufler-ca.com>
>> Date: Thu, 22 Aug 2019 14:59:37 -0700
>>
>>> Sure, you *can* do that, but it would be insane to do so.
>> We look up the neighbour table entries on every single packet we
>> transmit from the kernel in the same exact way.
>>
>> And it was exactly to get rid of a pointer in a data structure.
>
> I very much expect that the lifecycle management issues would
> be completely different, but I'll admit to having little understanding
> of the details of the neighbour table.
Neighbour table entries can live anywhere from essentially forever down
to several microseconds.
If your hash is good, and you use RCU locking on the read side, it's a
single pointer dereference in cost.
^ permalink raw reply
* Re: New skb extension for use by LSMs (skb "security blob")?
From: Casey Schaufler @ 2019-08-22 22:34 UTC (permalink / raw)
To: David Miller; +Cc: fw, paul, netdev, linux-security-module, selinux, casey
In-Reply-To: <20190822.152857.1388207414767202364.davem@davemloft.net>
On 8/22/2019 3:28 PM, David Miller wrote:
> From: Casey Schaufler <casey@schaufler-ca.com>
> Date: Thu, 22 Aug 2019 14:59:37 -0700
>
>> Sure, you *can* do that, but it would be insane to do so.
> We look up the neighbour table entries on every single packet we
> transmit from the kernel in the same exact way.
>
> And it was exactly to get rid of a pointer in a data structure.
I very much expect that the lifecycle management issues would
be completely different, but I'll admit to having little understanding
of the details of the neighbour table.
^ permalink raw reply
* Re: [PATCH net-next,v5, 0/6] Add software backchannel and mlx5e HV VHCA stats
From: David Miller @ 2019-08-22 22:33 UTC (permalink / raw)
To: haiyangz
Cc: sashal, saeedm, leon, eranbe, lorenzo.pieralisi, bhelgaas,
linux-pci, linux-hyperv, netdev, kys, sthemmin, linux-kernel
In-Reply-To: <1566512708-13785-1-git-send-email-haiyangz@microsoft.com>
I applied this patch series already to net-next, what are you doing?
^ permalink raw reply
* r8169: regression on MIPS/Loongson
From: Aaro Koskinen @ 2019-08-22 22:25 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: netdev, linux-mips
Hi,
After upgrading from v5.2 to v5.3-rc5 on MIPS/Loongson board, copying
large files from network with scp started to fail with "Integrity error".
Bisected to:
f072218cca5b076dd99f3dfa3aaafedfd0023a51 is the first bad commit
commit f072218cca5b076dd99f3dfa3aaafedfd0023a51
Author: Heiner Kallweit <hkallweit1@gmail.com>
Date: Thu Jun 27 23:19:09 2019 +0200
r8169: remove not needed call to dma_sync_single_for_device
Any idea what goes wrong? Should this change be reverted?
A.
^ permalink raw reply
* Re: [PATCH 0/3] Add NETIF_F_HW_BRIDGE feature
From: David Miller @ 2019-08-22 22:32 UTC (permalink / raw)
To: horatiu.vultur
Cc: roopa, nikolay, UNGLinuxDriver, alexandre.belloni, allan.nielsen,
netdev, linux-kernel, bridge
In-Reply-To: <1566500850-6247-1-git-send-email-horatiu.vultur@microchip.com>
From: Horatiu Vultur <horatiu.vultur@microchip.com>
Date: Thu, 22 Aug 2019 21:07:27 +0200
> Current implementation of the SW bridge is setting the interfaces in
> promisc mode when they are added to bridge if learning of the frames is
> enabled.
> In case of Ocelot which has HW capabilities to switch frames, it is not
> needed to set the ports in promisc mode because the HW already capable of
> doing that. Therefore add NETIF_F_HW_BRIDGE feature to indicate that the
> HW has bridge capabilities. Therefore the SW bridge doesn't need to set
> the ports in promisc mode to do the switching.
> This optimization takes places only if all the interfaces that are part
> of the bridge have this flag and have the same network driver.
>
> If the bridge interfaces is added in promisc mode then also the ports part
> of the bridge are set in promisc mode.
This doesn't look right at all.
The Linux bridge provides a software bridge.
By default, all hardware must provide a hardware implementation of
that software bridge behavior.
Anything that deviates from that behavior has to be explicitly asked
for by the user by explicit config commands.
^ permalink raw reply
* Re: [net-next v2 00/13][pull request] 40GbE Intel Wired LAN Driver Updates 2019-08-22
From: David Miller @ 2019-08-22 22:29 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, nhorman, sassmann
In-Reply-To: <20190822203039.15668-1-jeffrey.t.kirsher@intel.com>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Thu, 22 Aug 2019 13:30:26 -0700
> This series contains updates to i40e driver only.
Pulled, thanks Jeff.
^ permalink raw reply
* Re: New skb extension for use by LSMs (skb "security blob")?
From: David Miller @ 2019-08-22 22:28 UTC (permalink / raw)
To: casey; +Cc: fw, paul, netdev, linux-security-module, selinux
In-Reply-To: <e2e22b41-2aa1-6a52-107d-e4efd9dcacf4@schaufler-ca.com>
From: Casey Schaufler <casey@schaufler-ca.com>
Date: Thu, 22 Aug 2019 14:59:37 -0700
> Sure, you *can* do that, but it would be insane to do so.
We look up the neighbour table entries on every single packet we
transmit from the kernel in the same exact way.
And it was exactly to get rid of a pointer in a data structure.
^ permalink raw reply
* [PATCH net-next,v5, 1/6] PCI: hv: Add a paravirtual backchannel in software
From: Haiyang Zhang @ 2019-08-22 22:26 UTC (permalink / raw)
To: sashal@kernel.org, davem@davemloft.net, saeedm@mellanox.com,
leon@kernel.org, eranbe@mellanox.com, lorenzo.pieralisi@arm.com,
bhelgaas@google.com, linux-pci@vger.kernel.org,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org
Cc: Haiyang Zhang, KY Srinivasan, Stephen Hemminger,
linux-kernel@vger.kernel.org, Dexuan Cui, Jake Oshins
In-Reply-To: <1566512708-13785-1-git-send-email-haiyangz@microsoft.com>
From: Dexuan Cui <decui@microsoft.com>
Windows SR-IOV provides a backchannel mechanism in software for communication
between a VF driver and a PF driver. These "configuration blocks" are
similar in concept to PCI configuration space, but instead of doing reads and
writes in 32-bit chunks through a very slow path, packets of up to 128 bytes
can be sent or received asynchronously.
Nearly every SR-IOV device contains just such a communications channel in
hardware, so using this one in software is usually optional. Using the
software channel, however, allows driver implementers to leverage software
tools that fuzz the communications channel looking for vulnerabilities.
The usage model for these packets puts the responsibility for reading or
writing on the VF driver. The VF driver sends a read or a write packet,
indicating which "block" is being referred to by number.
If the PF driver wishes to initiate communication, it can "invalidate" one or
more of the first 64 blocks. This invalidation is delivered via a callback
supplied by the VF driver by this driver.
No protocol is implied, except that supplied by the PF and VF drivers.
Signed-off-by: Jake Oshins <jakeo@microsoft.com>
Signed-off-by: Dexuan Cui <decui@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: K. Y. Srinivasan <kys@microsoft.com>
Cc: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
---
drivers/pci/controller/pci-hyperv.c | 302 ++++++++++++++++++++++++++++++++++++
include/linux/hyperv.h | 15 ++
2 files changed, 317 insertions(+)
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 40b6254..57adeca 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -365,6 +365,39 @@ struct pci_delete_interrupt {
struct tran_int_desc int_desc;
} __packed;
+/*
+ * Note: the VM must pass a valid block id, wslot and bytes_requested.
+ */
+struct pci_read_block {
+ struct pci_message message_type;
+ u32 block_id;
+ union win_slot_encoding wslot;
+ u32 bytes_requested;
+} __packed;
+
+struct pci_read_block_response {
+ struct vmpacket_descriptor hdr;
+ u32 status;
+ u8 bytes[HV_CONFIG_BLOCK_SIZE_MAX];
+} __packed;
+
+/*
+ * Note: the VM must pass a valid block id, wslot and byte_count.
+ */
+struct pci_write_block {
+ struct pci_message message_type;
+ u32 block_id;
+ union win_slot_encoding wslot;
+ u32 byte_count;
+ u8 bytes[HV_CONFIG_BLOCK_SIZE_MAX];
+} __packed;
+
+struct pci_dev_inval_block {
+ struct pci_incoming_message incoming;
+ union win_slot_encoding wslot;
+ u64 block_mask;
+} __packed;
+
struct pci_dev_incoming {
struct pci_incoming_message incoming;
union win_slot_encoding wslot;
@@ -499,6 +532,9 @@ struct hv_pci_dev {
struct hv_pcibus_device *hbus;
struct work_struct wrk;
+ void (*block_invalidate)(void *context, u64 block_mask);
+ void *invalidate_context;
+
/*
* What would be observed if one wrote 0xFFFFFFFF to a BAR and then
* read it back, for each of the BAR offsets within config space.
@@ -817,6 +853,256 @@ static int hv_pcifront_write_config(struct pci_bus *bus, unsigned int devfn,
.write = hv_pcifront_write_config,
};
+/*
+ * Paravirtual backchannel
+ *
+ * Hyper-V SR-IOV provides a backchannel mechanism in software for
+ * communication between a VF driver and a PF driver. These
+ * "configuration blocks" are similar in concept to PCI configuration space,
+ * but instead of doing reads and writes in 32-bit chunks through a very slow
+ * path, packets of up to 128 bytes can be sent or received asynchronously.
+ *
+ * Nearly every SR-IOV device contains just such a communications channel in
+ * hardware, so using this one in software is usually optional. Using the
+ * software channel, however, allows driver implementers to leverage software
+ * tools that fuzz the communications channel looking for vulnerabilities.
+ *
+ * The usage model for these packets puts the responsibility for reading or
+ * writing on the VF driver. The VF driver sends a read or a write packet,
+ * indicating which "block" is being referred to by number.
+ *
+ * If the PF driver wishes to initiate communication, it can "invalidate" one or
+ * more of the first 64 blocks. This invalidation is delivered via a callback
+ * supplied by the VF driver by this driver.
+ *
+ * No protocol is implied, except that supplied by the PF and VF drivers.
+ */
+
+struct hv_read_config_compl {
+ struct hv_pci_compl comp_pkt;
+ void *buf;
+ unsigned int len;
+ unsigned int bytes_returned;
+};
+
+/**
+ * hv_pci_read_config_compl() - Invoked when a response packet
+ * for a read config block operation arrives.
+ * @context: Identifies the read config operation
+ * @resp: The response packet itself
+ * @resp_packet_size: Size in bytes of the response packet
+ */
+static void hv_pci_read_config_compl(void *context, struct pci_response *resp,
+ int resp_packet_size)
+{
+ struct hv_read_config_compl *comp = context;
+ struct pci_read_block_response *read_resp =
+ (struct pci_read_block_response *)resp;
+ unsigned int data_len, hdr_len;
+
+ hdr_len = offsetof(struct pci_read_block_response, bytes);
+ if (resp_packet_size < hdr_len) {
+ comp->comp_pkt.completion_status = -1;
+ goto out;
+ }
+
+ data_len = resp_packet_size - hdr_len;
+ if (data_len > 0 && read_resp->status == 0) {
+ comp->bytes_returned = min(comp->len, data_len);
+ memcpy(comp->buf, read_resp->bytes, comp->bytes_returned);
+ } else {
+ comp->bytes_returned = 0;
+ }
+
+ comp->comp_pkt.completion_status = read_resp->status;
+out:
+ complete(&comp->comp_pkt.host_event);
+}
+
+/**
+ * hv_read_config_block() - Sends a read config block request to
+ * the back-end driver running in the Hyper-V parent partition.
+ * @pdev: The PCI driver's representation for this device.
+ * @buf: Buffer into which the config block will be copied.
+ * @len: Size in bytes of buf.
+ * @block_id: Identifies the config block which has been requested.
+ * @bytes_returned: Size which came back from the back-end driver.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int hv_read_config_block(struct pci_dev *pdev, void *buf, unsigned int len,
+ unsigned int block_id, unsigned int *bytes_returned)
+{
+ struct hv_pcibus_device *hbus =
+ container_of(pdev->bus->sysdata, struct hv_pcibus_device,
+ sysdata);
+ struct {
+ struct pci_packet pkt;
+ char buf[sizeof(struct pci_read_block)];
+ } pkt;
+ struct hv_read_config_compl comp_pkt;
+ struct pci_read_block *read_blk;
+ int ret;
+
+ if (len == 0 || len > HV_CONFIG_BLOCK_SIZE_MAX)
+ return -EINVAL;
+
+ init_completion(&comp_pkt.comp_pkt.host_event);
+ comp_pkt.buf = buf;
+ comp_pkt.len = len;
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.pkt.completion_func = hv_pci_read_config_compl;
+ pkt.pkt.compl_ctxt = &comp_pkt;
+ read_blk = (struct pci_read_block *)&pkt.pkt.message;
+ read_blk->message_type.type = PCI_READ_BLOCK;
+ read_blk->wslot.slot = devfn_to_wslot(pdev->devfn);
+ read_blk->block_id = block_id;
+ read_blk->bytes_requested = len;
+
+ ret = vmbus_sendpacket(hbus->hdev->channel, read_blk,
+ sizeof(*read_blk), (unsigned long)&pkt.pkt,
+ VM_PKT_DATA_INBAND,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret)
+ return ret;
+
+ ret = wait_for_response(hbus->hdev, &comp_pkt.comp_pkt.host_event);
+ if (ret)
+ return ret;
+
+ if (comp_pkt.comp_pkt.completion_status != 0 ||
+ comp_pkt.bytes_returned == 0) {
+ dev_err(&hbus->hdev->device,
+ "Read Config Block failed: 0x%x, bytes_returned=%d\n",
+ comp_pkt.comp_pkt.completion_status,
+ comp_pkt.bytes_returned);
+ return -EIO;
+ }
+
+ *bytes_returned = comp_pkt.bytes_returned;
+ return 0;
+}
+EXPORT_SYMBOL(hv_read_config_block);
+
+/**
+ * hv_pci_write_config_compl() - Invoked when a response packet for a write
+ * config block operation arrives.
+ * @context: Identifies the write config operation
+ * @resp: The response packet itself
+ * @resp_packet_size: Size in bytes of the response packet
+ */
+static void hv_pci_write_config_compl(void *context, struct pci_response *resp,
+ int resp_packet_size)
+{
+ struct hv_pci_compl *comp_pkt = context;
+
+ comp_pkt->completion_status = resp->status;
+ complete(&comp_pkt->host_event);
+}
+
+/**
+ * hv_write_config_block() - Sends a write config block request to the
+ * back-end driver running in the Hyper-V parent partition.
+ * @pdev: The PCI driver's representation for this device.
+ * @buf: Buffer from which the config block will be copied.
+ * @len: Size in bytes of buf.
+ * @block_id: Identifies the config block which is being written.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int hv_write_config_block(struct pci_dev *pdev, void *buf, unsigned int len,
+ unsigned int block_id)
+{
+ struct hv_pcibus_device *hbus =
+ container_of(pdev->bus->sysdata, struct hv_pcibus_device,
+ sysdata);
+ struct {
+ struct pci_packet pkt;
+ char buf[sizeof(struct pci_write_block)];
+ u32 reserved;
+ } pkt;
+ struct hv_pci_compl comp_pkt;
+ struct pci_write_block *write_blk;
+ u32 pkt_size;
+ int ret;
+
+ if (len == 0 || len > HV_CONFIG_BLOCK_SIZE_MAX)
+ return -EINVAL;
+
+ init_completion(&comp_pkt.host_event);
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.pkt.completion_func = hv_pci_write_config_compl;
+ pkt.pkt.compl_ctxt = &comp_pkt;
+ write_blk = (struct pci_write_block *)&pkt.pkt.message;
+ write_blk->message_type.type = PCI_WRITE_BLOCK;
+ write_blk->wslot.slot = devfn_to_wslot(pdev->devfn);
+ write_blk->block_id = block_id;
+ write_blk->byte_count = len;
+ memcpy(write_blk->bytes, buf, len);
+ pkt_size = offsetof(struct pci_write_block, bytes) + len;
+ /*
+ * This quirk is required on some hosts shipped around 2018, because
+ * these hosts don't check the pkt_size correctly (new hosts have been
+ * fixed since early 2019). The quirk is also safe on very old hosts
+ * and new hosts, because, on them, what really matters is the length
+ * specified in write_blk->byte_count.
+ */
+ pkt_size += sizeof(pkt.reserved);
+
+ ret = vmbus_sendpacket(hbus->hdev->channel, write_blk, pkt_size,
+ (unsigned long)&pkt.pkt, VM_PKT_DATA_INBAND,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret)
+ return ret;
+
+ ret = wait_for_response(hbus->hdev, &comp_pkt.host_event);
+ if (ret)
+ return ret;
+
+ if (comp_pkt.completion_status != 0) {
+ dev_err(&hbus->hdev->device,
+ "Write Config Block failed: 0x%x\n",
+ comp_pkt.completion_status);
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(hv_write_config_block);
+
+/**
+ * hv_register_block_invalidate() - Invoked when a config block invalidation
+ * arrives from the back-end driver.
+ * @pdev: The PCI driver's representation for this device.
+ * @context: Identifies the device.
+ * @block_invalidate: Identifies all of the blocks being invalidated.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int hv_register_block_invalidate(struct pci_dev *pdev, void *context,
+ void (*block_invalidate)(void *context,
+ u64 block_mask))
+{
+ struct hv_pcibus_device *hbus =
+ container_of(pdev->bus->sysdata, struct hv_pcibus_device,
+ sysdata);
+ struct hv_pci_dev *hpdev;
+
+ hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn));
+ if (!hpdev)
+ return -ENODEV;
+
+ hpdev->block_invalidate = block_invalidate;
+ hpdev->invalidate_context = context;
+
+ put_pcichild(hpdev);
+ return 0;
+
+}
+EXPORT_SYMBOL(hv_register_block_invalidate);
+
/* Interrupt management hooks */
static void hv_int_desc_free(struct hv_pci_dev *hpdev,
struct tran_int_desc *int_desc)
@@ -1968,6 +2254,7 @@ static void hv_pci_onchannelcallback(void *context)
struct pci_response *response;
struct pci_incoming_message *new_message;
struct pci_bus_relations *bus_rel;
+ struct pci_dev_inval_block *inval;
struct pci_dev_incoming *dev_message;
struct hv_pci_dev *hpdev;
@@ -2045,6 +2332,21 @@ static void hv_pci_onchannelcallback(void *context)
}
break;
+ case PCI_INVALIDATE_BLOCK:
+
+ inval = (struct pci_dev_inval_block *)buffer;
+ hpdev = get_pcichild_wslot(hbus,
+ inval->wslot.slot);
+ if (hpdev) {
+ if (hpdev->block_invalidate) {
+ hpdev->block_invalidate(
+ hpdev->invalidate_context,
+ inval->block_mask);
+ }
+ put_pcichild(hpdev);
+ }
+ break;
+
default:
dev_warn(&hbus->hdev->device,
"Unimplemented protocol message %x\n",
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 6256cc3..9d37f8c 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -1578,4 +1578,19 @@ struct vmpacket_descriptor *
for (pkt = hv_pkt_iter_first(channel); pkt; \
pkt = hv_pkt_iter_next(channel, pkt))
+/*
+ * Functions for passing data between SR-IOV PF and VF drivers. The VF driver
+ * sends requests to read and write blocks. Each block must be 128 bytes or
+ * smaller. Optionally, the VF driver can register a callback function which
+ * will be invoked when the host says that one or more of the first 64 block
+ * IDs is "invalid" which means that the VF driver should reread them.
+ */
+#define HV_CONFIG_BLOCK_SIZE_MAX 128
+int hv_read_config_block(struct pci_dev *dev, void *buf, unsigned int buf_len,
+ unsigned int block_id, unsigned int *bytes_returned);
+int hv_write_config_block(struct pci_dev *dev, void *buf, unsigned int len,
+ unsigned int block_id);
+int hv_register_block_invalidate(struct pci_dev *dev, void *context,
+ void (*block_invalidate)(void *context,
+ u64 block_mask));
#endif /* _HYPERV_H */
--
1.8.3.1
^ permalink raw reply related
* [PATCH net-next,v5, 5/6] net/mlx5: Add HV VHCA control agent
From: Haiyang Zhang @ 2019-08-22 22:26 UTC (permalink / raw)
To: sashal@kernel.org, davem@davemloft.net, saeedm@mellanox.com,
leon@kernel.org, eranbe@mellanox.com, lorenzo.pieralisi@arm.com,
bhelgaas@google.com, linux-pci@vger.kernel.org,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org
Cc: Haiyang Zhang, KY Srinivasan, Stephen Hemminger,
linux-kernel@vger.kernel.org
In-Reply-To: <1566512708-13785-1-git-send-email-haiyangz@microsoft.com>
From: Eran Ben Elisha <eranbe@mellanox.com>
Control agent is responsible over of the control block (ID 0). It should
update the PF via this block about every capability change. In addition,
upon block 0 invalidate, it should activate all other supported agents
with data requests from the PF.
Upon agent create/destroy, the invalidate callback of the control agent
is being called in order to update the PF driver about this change.
The control agent is an integral part of HV VHCA and will be created
and destroy as part of the HV VHCA init/cleanup flow.
Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
---
.../net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 122 ++++++++++++++++++++-
.../net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 1 +
2 files changed, 121 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
index 84d1d75..4047629 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
@@ -109,22 +109,131 @@ void mlx5_hv_vhca_invalidate(void *context, u64 block_mask)
queue_work(hv_vhca->work_queue, &work->invalidate_work);
}
+#define AGENT_MASK(type) (type ? BIT(type - 1) : 0 /* control */)
+
+static void mlx5_hv_vhca_agents_control(struct mlx5_hv_vhca *hv_vhca,
+ struct mlx5_hv_vhca_control_block *block)
+{
+ int i;
+
+ for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++) {
+ struct mlx5_hv_vhca_agent *agent = hv_vhca->agents[i];
+
+ if (!agent || !agent->control)
+ continue;
+
+ if (!(AGENT_MASK(agent->type) & block->control))
+ continue;
+
+ agent->control(agent, block);
+ }
+}
+
+static void mlx5_hv_vhca_capabilities(struct mlx5_hv_vhca *hv_vhca,
+ u32 *capabilities)
+{
+ int i;
+
+ for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++) {
+ struct mlx5_hv_vhca_agent *agent = hv_vhca->agents[i];
+
+ if (agent)
+ *capabilities |= AGENT_MASK(agent->type);
+ }
+}
+
+static void
+mlx5_hv_vhca_control_agent_invalidate(struct mlx5_hv_vhca_agent *agent,
+ u64 block_mask)
+{
+ struct mlx5_hv_vhca *hv_vhca = agent->hv_vhca;
+ struct mlx5_core_dev *dev = hv_vhca->dev;
+ struct mlx5_hv_vhca_control_block *block;
+ u32 capabilities = 0;
+ int err;
+
+ block = kzalloc(sizeof(*block), GFP_KERNEL);
+ if (!block)
+ return;
+
+ err = mlx5_hv_read_config(dev, block, sizeof(*block), 0);
+ if (err)
+ goto free_block;
+
+ mlx5_hv_vhca_capabilities(hv_vhca, &capabilities);
+
+ /* In case no capabilities, send empty block in return */
+ if (!capabilities) {
+ memset(block, 0, sizeof(*block));
+ goto write;
+ }
+
+ if (block->capabilities != capabilities)
+ block->capabilities = capabilities;
+
+ if (block->control & ~capabilities)
+ goto free_block;
+
+ mlx5_hv_vhca_agents_control(hv_vhca, block);
+ block->command_ack = block->command;
+
+write:
+ mlx5_hv_write_config(dev, block, sizeof(*block), 0);
+
+free_block:
+ kfree(block);
+}
+
+static struct mlx5_hv_vhca_agent *
+mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca)
+{
+ return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL,
+ NULL,
+ mlx5_hv_vhca_control_agent_invalidate,
+ NULL, NULL);
+}
+
+static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent)
+{
+ mlx5_hv_vhca_agent_destroy(agent);
+}
+
int mlx5_hv_vhca_init(struct mlx5_hv_vhca *hv_vhca)
{
+ struct mlx5_hv_vhca_agent *agent;
+ int err;
+
if (IS_ERR_OR_NULL(hv_vhca))
return IS_ERR_OR_NULL(hv_vhca);
- return mlx5_hv_register_invalidate(hv_vhca->dev, hv_vhca,
- mlx5_hv_vhca_invalidate);
+ err = mlx5_hv_register_invalidate(hv_vhca->dev, hv_vhca,
+ mlx5_hv_vhca_invalidate);
+ if (err)
+ return err;
+
+ agent = mlx5_hv_vhca_control_agent_create(hv_vhca);
+ if (IS_ERR_OR_NULL(agent)) {
+ mlx5_hv_unregister_invalidate(hv_vhca->dev);
+ return IS_ERR_OR_NULL(agent);
+ }
+
+ hv_vhca->agents[MLX5_HV_VHCA_AGENT_CONTROL] = agent;
+
+ return 0;
}
void mlx5_hv_vhca_cleanup(struct mlx5_hv_vhca *hv_vhca)
{
+ struct mlx5_hv_vhca_agent *agent;
int i;
if (IS_ERR_OR_NULL(hv_vhca))
return;
+ agent = hv_vhca->agents[MLX5_HV_VHCA_AGENT_CONTROL];
+ if (agent)
+ mlx5_hv_vhca_control_agent_destroy(agent);
+
mutex_lock(&hv_vhca->agents_lock);
for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++)
WARN_ON(hv_vhca->agents[i]);
@@ -134,6 +243,11 @@ void mlx5_hv_vhca_cleanup(struct mlx5_hv_vhca *hv_vhca)
mlx5_hv_unregister_invalidate(hv_vhca->dev);
}
+static void mlx5_hv_vhca_agents_update(struct mlx5_hv_vhca *hv_vhca)
+{
+ mlx5_hv_vhca_invalidate(hv_vhca, BIT(MLX5_HV_VHCA_AGENT_CONTROL));
+}
+
struct mlx5_hv_vhca_agent *
mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
enum mlx5_hv_vhca_agent_type type,
@@ -174,6 +288,8 @@ struct mlx5_hv_vhca_agent *
hv_vhca->agents[type] = agent;
mutex_unlock(&hv_vhca->agents_lock);
+ mlx5_hv_vhca_agents_update(hv_vhca);
+
return agent;
}
@@ -195,6 +311,8 @@ void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent)
agent->cleanup(agent);
kfree(agent);
+
+ mlx5_hv_vhca_agents_update(hv_vhca);
}
static int mlx5_hv_vhca_data_block_prepare(struct mlx5_hv_vhca_agent *agent,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
index cdf1303..984e7ad 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
@@ -12,6 +12,7 @@
struct mlx5_hv_vhca_control_block;
enum mlx5_hv_vhca_agent_type {
+ MLX5_HV_VHCA_AGENT_CONTROL = 0,
MLX5_HV_VHCA_AGENT_MAX = 32,
};
--
1.8.3.1
^ permalink raw reply related
* [PATCH net-next,v5, 6/6] net/mlx5e: Add mlx5e HV VHCA stats agent
From: Haiyang Zhang @ 2019-08-22 22:26 UTC (permalink / raw)
To: sashal@kernel.org, davem@davemloft.net, saeedm@mellanox.com,
leon@kernel.org, eranbe@mellanox.com, lorenzo.pieralisi@arm.com,
bhelgaas@google.com, linux-pci@vger.kernel.org,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org
Cc: Haiyang Zhang, KY Srinivasan, Stephen Hemminger,
linux-kernel@vger.kernel.org
In-Reply-To: <1566512708-13785-1-git-send-email-haiyangz@microsoft.com>
From: Eran Ben Elisha <eranbe@mellanox.com>
HV VHCA stats agent is responsible on running a preiodic rx/tx
packets/bytes stats update. Currently the supported format is version
MLX5_HV_VHCA_STATS_VERSION. Block ID 1 is dedicated for statistics data
transfer from the VF to the PF.
The reporter fetch the statistics data from all opened channels, fill it
in a buffer and send it to mlx5_hv_vhca_write_agent.
As the stats layer should include some metadata per block (sequence and
offset), the HV VHCA layer shall modify the buffer before actually send it
over block 1.
Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
---
drivers/net/ethernet/mellanox/mlx5/core/Makefile | 1 +
drivers/net/ethernet/mellanox/mlx5/core/en.h | 13 ++
.../ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 162 +++++++++++++++++++++
.../ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h | 25 ++++
drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 3 +
.../net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 1 +
6 files changed, 205 insertions(+)
create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index 8d443fc..f4de9cc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -36,6 +36,7 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o
mlx5_core-$(CONFIG_MLX5_ESWITCH) += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o \
lib/geneve.o en/tc_tun_vxlan.o en/tc_tun_gre.o \
en/tc_tun_geneve.o diag/en_tc_tracepoint.o
+mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o
#
# Core extra
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 7316571..4467927 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -54,6 +54,7 @@
#include "mlx5_core.h"
#include "en_stats.h"
#include "en/fs.h"
+#include "lib/hv_vhca.h"
extern const struct net_device_ops mlx5e_netdev_ops;
struct page_pool;
@@ -782,6 +783,15 @@ struct mlx5e_modify_sq_param {
int rl_index;
};
+#if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE)
+struct mlx5e_hv_vhca_stats_agent {
+ struct mlx5_hv_vhca_agent *agent;
+ struct delayed_work work;
+ u16 delay;
+ void *buf;
+};
+#endif
+
struct mlx5e_xsk {
/* UMEMs are stored separately from channels, because we don't want to
* lose them when channels are recreated. The kernel also stores UMEMs,
@@ -853,6 +863,9 @@ struct mlx5e_priv {
struct devlink_health_reporter *tx_reporter;
struct devlink_health_reporter *rx_reporter;
struct mlx5e_xsk xsk;
+#if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE)
+ struct mlx5e_hv_vhca_stats_agent stats_agent;
+#endif
};
struct mlx5e_profile {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
new file mode 100644
index 0000000..c37b4ac
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+// Copyright (c) 2018 Mellanox Technologies
+
+#include "en.h"
+#include "en/hv_vhca_stats.h"
+#include "lib/hv_vhca.h"
+#include "lib/hv.h"
+
+struct mlx5e_hv_vhca_per_ring_stats {
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_packets;
+ u64 tx_bytes;
+};
+
+static void
+mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
+ struct mlx5e_hv_vhca_per_ring_stats *data)
+{
+ struct mlx5e_channel_stats *stats;
+ int tc;
+
+ stats = &priv->channel_stats[ch];
+ data->rx_packets = stats->rq.packets;
+ data->rx_bytes = stats->rq.bytes;
+
+ for (tc = 0; tc < priv->max_opened_tc; tc++) {
+ data->tx_packets += stats->sq[tc].packets;
+ data->tx_bytes += stats->sq[tc].bytes;
+ }
+}
+
+static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, u64 *data,
+ int buf_len)
+{
+ int ch, i = 0;
+
+ for (ch = 0; ch < priv->max_nch; ch++) {
+ u64 *buf = data + i;
+
+ if (WARN_ON_ONCE(buf +
+ sizeof(struct mlx5e_hv_vhca_per_ring_stats) >
+ data + buf_len))
+ return;
+
+ mlx5e_hv_vhca_fill_ring_stats(priv, ch,
+ (struct mlx5e_hv_vhca_per_ring_stats *)buf);
+ i += sizeof(struct mlx5e_hv_vhca_per_ring_stats) / sizeof(u64);
+ }
+}
+
+static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
+{
+ return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
+ priv->max_nch);
+}
+
+static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
+{
+ struct mlx5e_hv_vhca_stats_agent *sagent;
+ struct mlx5_hv_vhca_agent *agent;
+ struct delayed_work *dwork;
+ struct mlx5e_priv *priv;
+ int buf_len, rc;
+ void *buf;
+
+ dwork = to_delayed_work(work);
+ sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
+ priv = container_of(sagent, struct mlx5e_priv, stats_agent);
+ buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
+ agent = sagent->agent;
+ buf = sagent->buf;
+
+ memset(buf, 0, buf_len);
+ mlx5e_hv_vhca_fill_stats(priv, buf, buf_len);
+
+ rc = mlx5_hv_vhca_agent_write(agent, buf, buf_len);
+ if (rc) {
+ mlx5_core_err(priv->mdev,
+ "%s: Failed to write stats, err = %d\n",
+ __func__, rc);
+ return;
+ }
+
+ if (sagent->delay)
+ queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
+}
+
+enum {
+ MLX5_HV_VHCA_STATS_VERSION = 1,
+ MLX5_HV_VHCA_STATS_UPDATE_ONCE = 0xFFFF,
+};
+
+static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
+ struct mlx5_hv_vhca_control_block *block)
+{
+ struct mlx5e_hv_vhca_stats_agent *sagent;
+ struct mlx5e_priv *priv;
+
+ priv = mlx5_hv_vhca_agent_priv(agent);
+ sagent = &priv->stats_agent;
+
+ block->version = MLX5_HV_VHCA_STATS_VERSION;
+ block->rings = priv->max_nch;
+
+ if (!block->command) {
+ cancel_delayed_work_sync(&priv->stats_agent.work);
+ return;
+ }
+
+ sagent->delay = block->command == MLX5_HV_VHCA_STATS_UPDATE_ONCE ? 0 :
+ msecs_to_jiffies(block->command * 100);
+
+ queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
+}
+
+static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
+{
+ struct mlx5e_priv *priv = mlx5_hv_vhca_agent_priv(agent);
+
+ cancel_delayed_work_sync(&priv->stats_agent.work);
+}
+
+int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
+{
+ int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
+ struct mlx5_hv_vhca_agent *agent;
+
+ priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
+ if (!priv->stats_agent.buf)
+ return -ENOMEM;
+
+ agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
+ MLX5_HV_VHCA_AGENT_STATS,
+ mlx5e_hv_vhca_stats_control, NULL,
+ mlx5e_hv_vhca_stats_cleanup,
+ priv);
+
+ if (IS_ERR_OR_NULL(agent)) {
+ if (IS_ERR(agent))
+ netdev_warn(priv->netdev,
+ "Failed to create hv vhca stats agent, err = %ld\n",
+ PTR_ERR(agent));
+
+ kfree(priv->stats_agent.buf);
+ return IS_ERR_OR_NULL(agent);
+ }
+
+ priv->stats_agent.agent = agent;
+ INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
+
+ return 0;
+}
+
+void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
+{
+ if (IS_ERR_OR_NULL(priv->stats_agent.agent))
+ return;
+
+ mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
+ kfree(priv->stats_agent.buf);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h
new file mode 100644
index 0000000..664463f
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#ifndef __MLX5_EN_STATS_VHCA_H__
+#define __MLX5_EN_STATS_VHCA_H__
+#include "en.h"
+
+#if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE)
+
+int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv);
+void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv);
+
+#else
+
+static inline int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
+{
+ return 0;
+}
+
+static inline void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
+{
+}
+#endif
+
+#endif /* __MLX5_EN_STATS_VHCA_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 7fdea64..fa4bf2d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -62,6 +62,7 @@
#include "en/xsk/setup.h"
#include "en/xsk/rx.h"
#include "en/xsk/tx.h"
+#include "en/hv_vhca_stats.h"
bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev)
@@ -5109,6 +5110,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
if (mlx5e_monitor_counter_supported(priv))
mlx5e_monitor_counter_init(priv);
+ mlx5e_hv_vhca_stats_create(priv);
if (netdev->reg_state != NETREG_REGISTERED)
return;
#ifdef CONFIG_MLX5_CORE_EN_DCB
@@ -5141,6 +5143,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
queue_work(priv->wq, &priv->set_rx_mode_work);
+ mlx5e_hv_vhca_stats_destroy(priv);
if (mlx5e_monitor_counter_supported(priv))
mlx5e_monitor_counter_cleanup(priv);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
index 984e7ad..4bad6a5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
@@ -13,6 +13,7 @@
enum mlx5_hv_vhca_agent_type {
MLX5_HV_VHCA_AGENT_CONTROL = 0,
+ MLX5_HV_VHCA_AGENT_STATS = 1,
MLX5_HV_VHCA_AGENT_MAX = 32,
};
--
1.8.3.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox