* [net-next PATCH v2 04/12] net: flow_table: create a set of common headers and actions
From: John Fastabend @ 2015-01-13 21:36 UTC (permalink / raw)
To: tgraf, simon.horman, sfeldma; +Cc: netdev, gerlitz.or, jhs, andy, davem
In-Reply-To: <20150113212941.13874.48692.stgit@nitbit.x32>
This adds common headers and actions that drivers can use.
I have not yet moved the header graphs into the common header
because I'm not entirely convinced its re-usable. The devices
I have been looking at have different enough header graphs that
they wouldn't be re-usable. However possibly many 40Gbp NICs
for example could share a common header graph. When we get
multiple implementations we can move this into the common file
if it makes sense.
And table structures seem to be unique enough that there is
little value in putting each devices table layout into the
common file so its left for device specific implementation.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
include/linux/if_flow_common.h | 257 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 257 insertions(+)
create mode 100644 include/linux/if_flow_common.h
diff --git a/include/linux/if_flow_common.h b/include/linux/if_flow_common.h
new file mode 100644
index 0000000..d4dd749
--- /dev/null
+++ b/include/linux/if_flow_common.h
@@ -0,0 +1,257 @@
+#ifndef _IF_FLOW_COMMON_H_
+#define _IF_FLOW_COMMON_H_
+
+#include <linux/if_flow.h>
+
+/* Common header definition this section provides a set of common or
+ * standard headers that device driver writers may use to simplify the
+ * driver creation. We do not want vendor or driver specific headers
+ * here though. Driver authors can keep these contained to their driver
+ *
+ * Driver authors may use unique IDs greater than HEADER_MAX_UID it is
+ * guaranteed to be larger than any unique IDs used here.
+ */
+#define HEADER_MAX_UID 100
+
+enum net_flow_headers {
+ HEADER_UNSPEC,
+ HEADER_ETHERNET,
+ HEADER_VLAN,
+ HEADER_IPV4,
+};
+
+enum net_flow_ethernet_fields_ids {
+ HEADER_ETHERNET_UNSPEC,
+ HEADER_ETHERNET_SRC_MAC,
+ HEADER_ETHERNET_DST_MAC,
+ HEADER_ETHERNET_ETHERTYPE,
+};
+
+struct net_flow_field net_flow_ethernet_fields[] = {
+ { .name = "src_mac", .uid = HEADER_ETHERNET_SRC_MAC, .bitwidth = 48},
+ { .name = "dst_mac", .uid = HEADER_ETHERNET_DST_MAC, .bitwidth = 48},
+ { .name = "ethertype",
+ .uid = HEADER_ETHERNET_ETHERTYPE,
+ .bitwidth = 16},
+};
+
+struct net_flow_hdr net_flow_ethernet = {
+ .name = "ethernet",
+ .uid = HEADER_ETHERNET,
+ .field_sz = ARRAY_SIZE(net_flow_ethernet_fields),
+ .fields = net_flow_ethernet_fields,
+};
+
+enum net_flow_vlan_fields_ids {
+ HEADER_VLAN_UNSPEC,
+ HEADER_VLAN_PCP,
+ HEADER_VLAN_CFI,
+ HEADER_VLAN_VID,
+ HEADER_VLAN_ETHERTYPE,
+};
+
+struct net_flow_field net_flow_vlan_fields[] = {
+ { .name = "pcp", .uid = HEADER_VLAN_PCP, .bitwidth = 3,},
+ { .name = "cfi", .uid = HEADER_VLAN_CFI, .bitwidth = 1,},
+ { .name = "vid", .uid = HEADER_VLAN_VID, .bitwidth = 12,},
+ { .name = "ethertype", .uid = HEADER_VLAN_ETHERTYPE, .bitwidth = 16,},
+};
+
+struct net_flow_hdr net_flow_vlan = {
+ .name = "vlan",
+ .uid = HEADER_VLAN,
+ .field_sz = ARRAY_SIZE(net_flow_vlan_fields),
+ .fields = net_flow_vlan_fields,
+};
+
+enum net_flow_ipv4_fields_ids {
+ HEADER_IPV4_UNSPEC,
+ HEADER_IPV4_VERSION,
+ HEADER_IPV4_IHL,
+ HEADER_IPV4_DSCP,
+ HEADER_IPV4_ECN,
+ HEADER_IPV4_LENGTH,
+ HEADER_IPV4_IDENTIFICATION,
+ HEADER_IPV4_FLAGS,
+ HEADER_IPV4_FRAGMENT_OFFSET,
+ HEADER_IPV4_TTL,
+ HEADER_IPV4_PROTOCOL,
+ HEADER_IPV4_CSUM,
+ HEADER_IPV4_SRC_IP,
+ HEADER_IPV4_DST_IP,
+ HEADER_IPV4_OPTIONS,
+};
+
+struct net_flow_field net_flow_ipv4_fields[14] = {
+ { .name = "version",
+ .uid = HEADER_IPV4_VERSION,
+ .bitwidth = 4,},
+ { .name = "ihl",
+ .uid = HEADER_IPV4_IHL,
+ .bitwidth = 4,},
+ { .name = "dscp",
+ .uid = HEADER_IPV4_DSCP,
+ .bitwidth = 6,},
+ { .name = "ecn",
+ .uid = HEADER_IPV4_ECN,
+ .bitwidth = 2,},
+ { .name = "length",
+ .uid = HEADER_IPV4_LENGTH,
+ .bitwidth = 8,},
+ { .name = "identification",
+ .uid = HEADER_IPV4_IDENTIFICATION,
+ .bitwidth = 8,},
+ { .name = "flags",
+ .uid = HEADER_IPV4_FLAGS,
+ .bitwidth = 3,},
+ { .name = "fragment_offset",
+ .uid = HEADER_IPV4_FRAGMENT_OFFSET,
+ .bitwidth = 13,},
+ { .name = "ttl",
+ .uid = HEADER_IPV4_TTL,
+ .bitwidth = 1,},
+ { .name = "protocol",
+ .uid = HEADER_IPV4_PROTOCOL,
+ .bitwidth = 8,},
+ { .name = "csum",
+ .uid = HEADER_IPV4_CSUM,
+ .bitwidth = 8,},
+ { .name = "src_ip",
+ .uid = HEADER_IPV4_SRC_IP,
+ .bitwidth = 32,},
+ { .name = "dst_ip",
+ .uid = HEADER_IPV4_DST_IP,
+ .bitwidth = 32,},
+ { .name = "options",
+ .uid = HEADER_IPV4_OPTIONS,
+ .bitwidth = -1,},
+};
+
+struct net_flow_hdr net_flow_ipv4 = {
+ .name = "ipv4",
+ .uid = HEADER_IPV4,
+ .field_sz = ARRAY_SIZE(net_flow_ipv4_fields),
+ .fields = net_flow_ipv4_fields,
+};
+
+/* Common set of actions. Below are the list of actions that are or we expect
+ * are common enough to be supported by multiple devices.
+ *
+ * Driver authors may use unique IDs greater than ACTION_MAX_UID it is
+ * guaranteed to be larger than any unique IDs used here.
+ */
+#define ACTION_MAX_UID 100
+
+enum net_flow_action_ids {
+ ACTION_SET_UNSPEC,
+ ACTION_SET_VLAN_ID,
+ ACTION_COPY_TO_CPU,
+ ACTION_POP_VLAN,
+ ACTION_SET_ETH_SRC,
+ ACTION_SET_ETH_DST,
+ ACTION_SET_OUT_PORT,
+ ACTION_CHECK_TTL_DROP,
+};
+
+struct net_flow_action_arg net_flow_null_args[1] = {
+ {
+ .name = "",
+ .type = NFL_ACTION_ARG_TYPE_NULL,
+ },
+};
+
+struct net_flow_action net_flow_null_action = {
+ .name = "", .uid = 0, .args = NULL,
+};
+
+struct net_flow_action_arg net_flow_set_vlan_id_args[2] = {
+ {
+ .name = "vlan_id",
+ .type = NFL_ACTION_ARG_TYPE_U16,
+ .value_u16 = 0,
+ },
+ {
+ .name = "",
+ .type = NFL_ACTION_ARG_TYPE_NULL,
+ },
+};
+
+struct net_flow_action net_flow_set_vlan_id = {
+ .name = "set_vlan_id",
+ .uid = ACTION_SET_VLAN_ID,
+ .args = net_flow_set_vlan_id_args,
+};
+
+struct net_flow_action net_flow_copy_to_cpu = {
+ .name = "copy_to_cpu",
+ .uid = ACTION_COPY_TO_CPU,
+ .args = net_flow_null_args,
+};
+
+struct net_flow_action net_flow_pop_vlan = {
+ .name = "pop_vlan",
+ .uid = ACTION_POP_VLAN,
+ .args = net_flow_null_args,
+};
+
+struct net_flow_action_arg net_flow_set_eth_src_args[2] = {
+ {
+ .name = "eth_src",
+ .type = NFL_ACTION_ARG_TYPE_U64,
+ .value_u64 = 0,
+ },
+ {
+ .name = "",
+ .type = NFL_ACTION_ARG_TYPE_NULL,
+ },
+};
+
+struct net_flow_action net_flow_set_eth_src = {
+ .name = "set_eth_src",
+ .uid = ACTION_SET_ETH_SRC,
+ .args = net_flow_set_eth_src_args,
+};
+
+struct net_flow_action_arg net_flow_set_eth_dst_args[2] = {
+ {
+ .name = "eth_dst",
+ .type = NFL_ACTION_ARG_TYPE_U64,
+ .value_u64 = 0,
+ },
+ {
+ .name = "",
+ .type = NFL_ACTION_ARG_TYPE_NULL,
+ },
+};
+
+struct net_flow_action net_flow_set_eth_dst = {
+ .name = "set_eth_dst",
+ .uid = ACTION_SET_ETH_DST,
+ .args = net_flow_set_eth_dst_args,
+};
+
+struct net_flow_action_arg net_flow_set_out_port_args[2] = {
+ {
+ .name = "set_out_port",
+ .type = NFL_ACTION_ARG_TYPE_U32,
+ .value_u32 = 0,
+ },
+ {
+ .name = "",
+ .type = NFL_ACTION_ARG_TYPE_NULL,
+ },
+};
+
+struct net_flow_action net_flow_set_out_port = {
+ .name = "set_out_port",
+ .uid = ACTION_SET_OUT_PORT,
+ .args = net_flow_set_out_port_args,
+};
+
+struct net_flow_action net_flow_check_ttl_drop = {
+ .name = "check_ttl_drop",
+ .uid = ACTION_CHECK_TTL_DROP,
+ .args = net_flow_null_args,
+};
+
+#endif
^ permalink raw reply related
* Re: [PATCH] tcp: Fix RFC reference in comment
From: Yuchung Cheng @ 2015-01-13 21:36 UTC (permalink / raw)
To: Debabrata Banerjee; +Cc: David Miller, netdev, linux-kernel@vger.kernel.org
In-Reply-To: <1421183431-29915-1-git-send-email-dbanerje@akamai.com>
On Tue, Jan 13, 2015 at 1:10 PM, Debabrata Banerjee <dbanerje@akamai.com> wrote:
>
> Comment in tcp_cwnd_restart() was referencing the wrong RFC for the algorithm
> it's implementing.
>
> Signed-off-by: Debabrata Banerjee <dbanerje@akamai.com>
> ---
> net/ipv4/tcp_output.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
> index 65caf8b..0c13f88 100644
> --- a/net/ipv4/tcp_output.c
> +++ b/net/ipv4/tcp_output.c
> @@ -139,7 +139,7 @@ static __u16 tcp_advertise_mss(struct sock *sk)
> return (__u16)mss;
> }
>
> -/* RFC2861. Reset CWND after idle period longer RTO to "restart window".
> +/* RFC2581 4.1. Reset CWND after idle period longer RTO to "restart window".
> * This is the first part of cwnd validation mechanism. */
> static void tcp_cwnd_restart(struct sock *sk, const struct dst_entry *dst)
> {
RFC2861 resets the cwnd like in RFC2581, but the rest of the code
implements RFC2861. So I think the current comment is fine.
>
> --
> 2.2.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [net-next PATCH v2 03/12] net: flow: implement flow cache for get routines
From: John Fastabend @ 2015-01-13 21:36 UTC (permalink / raw)
To: tgraf, simon.horman, sfeldma; +Cc: netdev, gerlitz.or, jhs, andy, davem
In-Reply-To: <20150113212941.13874.48692.stgit@nitbit.x32>
I used rhashtable to implement a flow cache so software can track the
currently programmed rules without requiring every driver to
implement its own cache logic or fetch information from hardware.
I chose rhashtable to get the dynamic resizing. I could use arrays
but I don't want to pre-allocate large cache tables when we may
never use them.
One oddity in the rhashtable implementation is there is no way
AFAICS to do delayed free's so we use rcu_sync heavily. This should be
fine, get operations shouldn't be a used heavily.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
include/linux/if_flow.h | 21 +++++++
net/core/flow_table.c | 146 +++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 160 insertions(+), 7 deletions(-)
diff --git a/include/linux/if_flow.h b/include/linux/if_flow.h
index 23dec9b..dc70e3e 100644
--- a/include/linux/if_flow.h
+++ b/include/linux/if_flow.h
@@ -21,6 +21,7 @@
#define _IF_FLOW_H
#include <uapi/linux/if_flow.h>
+#include <linux/rhashtable.h>
/**
* @struct net_flow_fields
@@ -134,6 +135,7 @@ struct net_flow_field_ref {
* @size max number of entries for table or -1 for unbounded
* @matches null terminated set of supported match types given by match uid
* @actions null terminated set of supported action types given by action uid
+ * @cache software cache of hardware flows
*/
struct net_flow_tbl {
char *name;
@@ -143,6 +145,7 @@ struct net_flow_tbl {
int size;
struct net_flow_field_ref *matches;
int *actions;
+ struct rhashtable cache;
};
/**
@@ -198,10 +201,28 @@ struct net_flow_tbl_node {
* Flows must match all entries in match set.
*/
struct net_flow_rule {
+ struct rhash_head node;
int table_id;
int uid;
int priority;
struct net_flow_field_ref *matches;
struct net_flow_action *actions;
};
+
+#ifdef CONFIG_NET_FLOW_TABLES
+int net_flow_init_cache(struct net_flow_tbl *table);
+void net_flow_destroy_cache(struct net_flow_tbl *table);
+#else
+static inline int
+net_flow_init_cache(struct net_flow_tbl *table)
+{
+ return 0;
+}
+
+static inline void
+net_flow_destroy_cache(struct net_flow_tbl *table)
+{
+ return;
+}
+#endif /* CONFIG_NET_FLOW_TABLES */
#endif /* _IF_FLOW_H_ */
diff --git a/net/core/flow_table.c b/net/core/flow_table.c
index b6b1729..baeae64 100644
--- a/net/core/flow_table.c
+++ b/net/core/flow_table.c
@@ -26,6 +26,8 @@
#include <net/genetlink.h>
#include <net/rtnetlink.h>
#include <linux/module.h>
+#include <linux/rhashtable.h>
+#include <linux/jhash.h>
static DEFINE_MUTEX(net_flow_mutex);
@@ -919,6 +921,27 @@ static int net_flow_cmd_get_table_graph(struct sk_buff *skb,
return genlmsg_reply(msg, info);
}
+static struct net_flow_tbl *net_flow_get_table(struct net_device *dev,
+ int table_id)
+{
+ struct net_flow_tbl **tables;
+ int i;
+
+ if (!dev->netdev_ops->ndo_flow_get_tbls)
+ return NULL;
+
+ tables = dev->netdev_ops->ndo_flow_get_tbls(dev);
+ if (!tables)
+ return NULL;
+
+ for (i = 0; tables[i]; i++) {
+ if (tables[i]->uid == table_id)
+ return tables[i];
+ }
+
+ return NULL;
+}
+
static int net_flow_put_flow_action(struct sk_buff *skb,
struct net_flow_action *a)
{
@@ -1017,11 +1040,39 @@ put_failure:
return err;
}
+static int net_flow_get_rule_cache(struct sk_buff *skb,
+ struct net_flow_tbl *table,
+ int min, int max)
+{
+ const struct bucket_table *tbl;
+ struct net_flow_rule *he;
+ int i, err = 0;
+
+ rcu_read_lock();
+ tbl = rht_dereference_rcu(table->cache.tbl, &table->cache);
+
+ for (i = 0; i < tbl->size; i++) {
+ struct rhash_head *pos;
+
+ rht_for_each_entry_rcu(he, pos, tbl, i, node) {
+ if (he->uid < min || (max > 0 && he->uid > max))
+ continue;
+ err = net_flow_put_rule(skb, he);
+ if (err)
+ goto out;
+ }
+ }
+out:
+ rcu_read_unlock();
+ return err;
+}
+
static struct sk_buff *net_flow_build_flows_msg(struct net_device *dev,
u32 portid, int seq, u8 cmd,
int min, int max, int table)
{
struct genlmsghdr *hdr;
+ struct net_flow_tbl *t;
struct nlattr *flows;
struct sk_buff *skb;
int err = -ENOBUFS;
@@ -1042,15 +1093,23 @@ static struct sk_buff *net_flow_build_flows_msg(struct net_device *dev,
goto out;
}
+ t = net_flow_get_table(dev, table);
+ if (!t) {
+ err = -EINVAL;
+ goto out;
+ }
+
flows = nla_nest_start(skb, NFL_FLOWS);
if (!flows) {
err = -EMSGSIZE;
goto out;
}
- err = -EOPNOTSUPP;
- if (err < 0)
- goto out_cancel;
+ err = net_flow_get_rule_cache(skb, t, min, max);
+ if (err < 0) {
+ nla_nest_cancel(skb, flows);
+ goto out;
+ }
nla_nest_end(skb, flows);
@@ -1059,8 +1118,6 @@ static struct sk_buff *net_flow_build_flows_msg(struct net_device *dev,
goto out;
return skb;
-out_cancel:
- nla_nest_cancel(skb, flows);
out:
nlmsg_free(skb);
return ERR_PTR(err);
@@ -1505,6 +1562,71 @@ static int net_flow_get_rule(struct net_flow_rule *rule, struct nlattr *attr)
return 0;
}
+#define NFL_TABLE_ELEM_HINT 10
+int net_flow_init_cache(struct net_flow_tbl *table)
+{
+ struct rhashtable_params params = {
+ .nelem_hint = NFL_TABLE_ELEM_HINT,
+ .head_offset = offsetof(struct net_flow_rule, node),
+ .key_offset = offsetof(struct net_flow_rule, uid),
+ .key_len = sizeof(__u32),
+ .hashfn = jhash,
+ .grow_decision = rht_grow_above_75,
+ .shrink_decision = rht_shrink_below_30
+ };
+
+ return rhashtable_init(&table->cache, ¶ms);
+}
+EXPORT_SYMBOL(net_flow_init_cache);
+
+void net_flow_destroy_cache(struct net_flow_tbl *table)
+{
+ struct rhashtable *cache = &table->cache;
+ const struct bucket_table *tbl;
+ struct net_flow_rule *he;
+ struct rhash_head *pos, *next;
+ unsigned int i;
+
+ /* Stop an eventual async resizing */
+ cache->being_destroyed = true;
+ mutex_lock(&cache->mutex);
+
+ tbl = rht_dereference(cache->tbl, cache);
+ for (i = 0; i < tbl->size; i++) {
+ rht_for_each_entry_safe(he, pos, next, tbl, i, node) {
+ rhashtable_remove(&table->cache, &he->node);
+ synchronize_rcu();
+ net_flow_rule_free(he);
+ }
+ }
+
+ mutex_unlock(&cache->mutex);
+ rhashtable_destroy(cache);
+}
+EXPORT_SYMBOL(net_flow_destroy_cache);
+
+static void net_flow_add_rule_cache(struct net_flow_tbl *table,
+ struct net_flow_rule *this)
+{
+ rhashtable_insert(&table->cache, &this->node);
+}
+
+static int net_flow_del_rule_cache(struct net_flow_tbl *table,
+ struct net_flow_rule *this)
+{
+ struct net_flow_rule *he;
+
+ he = rhashtable_lookup(&table->cache, &this->uid);
+ if (he) {
+ rhashtable_remove(&table->cache, &he->node);
+ synchronize_rcu();
+ net_flow_rule_free(he);
+ return 0;
+ }
+
+ return -EEXIST;
+}
+
static int net_flow_table_cmd_flows(struct sk_buff *recv_skb,
struct genl_info *info)
{
@@ -1546,6 +1668,8 @@ static int net_flow_table_cmd_flows(struct sk_buff *recv_skb,
net_flow_lock();
nla_for_each_nested(flow, info->attrs[NFL_FLOWS], rem) {
+ struct net_flow_tbl *table;
+
if (nla_type(flow) != NFL_FLOW)
continue;
@@ -1563,12 +1687,22 @@ static int net_flow_table_cmd_flows(struct sk_buff *recv_skb,
if (err)
goto out_locked;
+ table = net_flow_get_table(dev, this->table_id);
+ if (!table) {
+ err = -EINVAL;
+ goto skip;
+ }
+
switch (cmd) {
case NFL_TABLE_CMD_SET_FLOWS:
err = dev->netdev_ops->ndo_flow_set_rule(dev, this);
+ if (!err)
+ net_flow_add_rule_cache(table, this);
break;
case NFL_TABLE_CMD_DEL_FLOWS:
err = dev->netdev_ops->ndo_flow_del_rule(dev, this);
+ if (!err)
+ err = net_flow_del_rule_cache(table, this);
break;
default:
err = -EOPNOTSUPP;
@@ -1597,8 +1731,6 @@ skip:
net_flow_put_rule(skb, this);
}
- net_flow_rule_free(this);
-
if (err && err_handle == NFL_FLOWS_ERROR_ABORT)
goto out_locked;
}
^ permalink raw reply related
* [net-next PATCH v2 02/12] net: flow_table: add flow, delete flow
From: John Fastabend @ 2015-01-13 21:35 UTC (permalink / raw)
To: tgraf, simon.horman, sfeldma; +Cc: netdev, gerlitz.or, jhs, andy, davem
In-Reply-To: <20150113212941.13874.48692.stgit@nitbit.x32>
Now that the device capabilities are exposed we can add support to
add and delete flows from the tables.
The two operations are
table_set_flows :
The set flow operations is used to program a set of flows into a
hardware device table. The message is consumed via netlink encoded
message which is then decoded into a null terminated array of
flow entry structures. A flow entry structure is defined as
struct net_flow_flow {
int table_id;
int uid;
int priority;
struct net_flow_field_ref *matches;
struct net_flow_action *actions;
}
The table id is the _uid_ returned from 'get_tables' operatoins.
Matches is a set of match criteria for packets with a logical AND
operation done on the set so packets match the entire criteria.
Actions provide a set of actions to perform when the flow rule is
hit. Both matches and actions are null terminated arrays.
The flows are configured in hardware using an ndo op. We do not
provide a commit operation at the moment and expect hardware
commits the flows one at a time. Future work may require a commit
operation to tell the hardware we are done loading flow rules. On
some hardware this will help bulk updates.
Its possible for hardware to return an error from a flow set
operation. This can occur for many reasons both transient and
resource constraints. We have different error handling strategies
built in and listed here,
*_ERROR_ABORT abort on first error with errmsg
*_ERROR_CONTINUE continue programming flows no errmsg
*_ERROR_ABORT_LOG abort on first error and return flow that
failed to user space in reply msg
*_ERROR_CONT_LOG continue programming flows and return a list
of flows that failed to user space in a reply
msg.
notably missing is a rollback error strategy. I don't have a
use for this in software yet but the strategy can be added with
*_ERROR_ROLLBACK for example.
table_del_flows
The delete flow operation uses the same structures and error
handling strategies as the table_set_flows operations. Although on
delete messges ommit the matches/actions arrays because they are
not needed to lookup the flow.
Also thanks to Simon Horman for fixes and other help.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
include/linux/if_flow.h | 21 +
include/linux/netdevice.h | 10 +
include/uapi/linux/if_flow.h | 51 +++
net/core/flow_table.c | 760 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 841 insertions(+), 1 deletion(-)
diff --git a/include/linux/if_flow.h b/include/linux/if_flow.h
index 3e1829e..23dec9b 100644
--- a/include/linux/if_flow.h
+++ b/include/linux/if_flow.h
@@ -185,4 +185,23 @@ struct net_flow_tbl_node {
__u32 flags;
struct net_flow_jump_table *jump;
};
-#endif
+
+/**
+ * @struct net_flow_rule
+ * @brief describes the match/action entry
+ *
+ * @uid unique identifier for flow
+ * @priority priority to execute flow match/action in table
+ * @match null terminated set of match uids match criteria
+ * @actoin null terminated set of action uids to apply to match
+ *
+ * Flows must match all entries in match set.
+ */
+struct net_flow_rule {
+ int table_id;
+ int uid;
+ int priority;
+ struct net_flow_field_ref *matches;
+ struct net_flow_action *actions;
+};
+#endif /* _IF_FLOW_H_ */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 74481b9..9d57f8b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1061,6 +1061,12 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* Report a null terminated list of nodes defining the header graph. This
* provides the necessary graph to learn the ordering of headers supported
* by the device.
+ *
+ * int (*ndo_flow_set_rule)(struct net_device *dev, struct net_flow_rule *f)
+ * This is used to program a rule into a device table.
+ *
+ * int (*ndo_flow_del_rule)(struct net_device *dev, struct net_flow_rule *f)
+ * This is used to remove a rule from a device table.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
@@ -1227,6 +1233,10 @@ struct net_device_ops {
struct net_flow_tbl_node **(*ndo_flow_get_tbl_graph)(struct net_device *dev);
struct net_flow_hdr **(*ndo_flow_get_hdrs)(struct net_device *dev);
struct net_flow_hdr_node **(*ndo_flow_get_hdr_graph)(struct net_device *dev);
+ int (*ndo_flow_set_rule)(struct net_device *dev,
+ struct net_flow_rule *f);
+ int (*ndo_flow_del_rule)(struct net_device *dev,
+ struct net_flow_rule *f);
#endif
};
diff --git a/include/uapi/linux/if_flow.h b/include/uapi/linux/if_flow.h
index 4ab2fa0..23ae3e7 100644
--- a/include/uapi/linux/if_flow.h
+++ b/include/uapi/linux/if_flow.h
@@ -149,6 +149,12 @@ enum {
#define NFL_FIELD_MAX (__NFL_FIELD_MAX - 1)
+/* Max length supported by kernel name strings only the first n characters
+ * will be used by the kernel API. This is to prevent arbitrarily long
+ * strings being passed from user space.
+ */
+#define NFL_MAX_NAME 80
+
enum {
NFL_FIELD_ATTR_UNSPEC,
NFL_FIELD_ATTR_NAME,
@@ -354,6 +360,44 @@ enum {
#define NFL_NFL_MAX (__NFL_NFL_MAX - 1)
enum {
+ NFL_TABLE_FLOWS_UNSPEC,
+ NFL_TABLE_FLOWS_TABLE,
+ NFL_TABLE_FLOWS_MINPRIO,
+ NFL_TABLE_FLOWS_MAXPRIO,
+ NFL_TABLE_FLOWS_FLOWS,
+ __NFL_TABLE_FLOWS_MAX,
+};
+
+#define NFL_TABLE_FLOWS_MAX (__NFL_TABLE_FLOWS_MAX - 1)
+
+enum {
+ /* Abort with normal errmsg */
+ NFL_FLOWS_ERROR_ABORT,
+ /* Ignore errors and continue without logging */
+ NFL_FLOWS_ERROR_CONTINUE,
+ /* Abort and reply with invalid flow fields */
+ NFL_FLOWS_ERROR_ABORT_LOG,
+ /* Continue and reply with list of invalid flows */
+ NFL_FLOWS_ERROR_CONT_LOG,
+ __NFLS_FLOWS_ERROR_MAX,
+};
+
+#define NFLS_FLOWS_ERROR_MAX (__NFLS_FLOWS_ERROR_MAX - 1)
+
+enum {
+ NFL_ATTR_UNSPEC,
+ NFL_ATTR_ERROR,
+ NFL_ATTR_TABLE,
+ NFL_ATTR_UID,
+ NFL_ATTR_PRIORITY,
+ NFL_ATTR_MATCHES,
+ NFL_ATTR_ACTIONS,
+ __NFL_ATTR_MAX,
+};
+
+#define NFL_ATTR_MAX (__NFL_ATTR_MAX - 1)
+
+enum {
NFL_IDENTIFIER_UNSPEC,
NFL_IDENTIFIER_IFINDEX, /* net_device ifindex */
};
@@ -369,6 +413,9 @@ enum {
NFL_HEADER_GRAPH,
NFL_TABLE_GRAPH,
+ NFL_FLOWS,
+ NFL_FLOWS_ERROR,
+
__NFL_MAX,
NFL_MAX = (__NFL_MAX - 1),
};
@@ -380,6 +427,10 @@ enum {
NFL_TABLE_CMD_GET_HDR_GRAPH,
NFL_TABLE_CMD_GET_TABLE_GRAPH,
+ NFL_TABLE_CMD_GET_FLOWS,
+ NFL_TABLE_CMD_SET_FLOWS,
+ NFL_TABLE_CMD_DEL_FLOWS,
+
__NFL_CMD_MAX,
NFL_CMD_MAX = (__NFL_CMD_MAX - 1),
};
diff --git a/net/core/flow_table.c b/net/core/flow_table.c
index ce673df..b6b1729 100644
--- a/net/core/flow_table.c
+++ b/net/core/flow_table.c
@@ -27,6 +27,18 @@
#include <net/rtnetlink.h>
#include <linux/module.h>
+static DEFINE_MUTEX(net_flow_mutex);
+
+void net_flow_lock(void)
+{
+ mutex_lock(&net_flow_mutex);
+}
+
+void net_flow_unlock(void)
+{
+ mutex_unlock(&net_flow_mutex);
+}
+
static struct genl_family net_flow_nl_family = {
.id = GENL_ID_GENERATE,
.name = NFL_GENL_NAME,
@@ -78,6 +90,34 @@ static int net_flow_put_act_types(struct sk_buff *skb,
if (err)
goto out;
+ switch (args[i].type) {
+ case NFL_ACTION_ARG_TYPE_NULL:
+ err = 0;
+ break;
+ case NFL_ACTION_ARG_TYPE_U8:
+ err = nla_put_u8(skb, NFL_ACTION_ARG_VALUE,
+ args[i].value_u8);
+ break;
+ case NFL_ACTION_ARG_TYPE_U16:
+ err = nla_put_u16(skb, NFL_ACTION_ARG_VALUE,
+ args[i].value_u16);
+ break;
+ case NFL_ACTION_ARG_TYPE_U32:
+ err = nla_put_u32(skb, NFL_ACTION_ARG_VALUE,
+ args[i].value_u32);
+ break;
+ case NFL_ACTION_ARG_TYPE_U64:
+ err = nla_put_u64(skb, NFL_ACTION_ARG_VALUE,
+ args[i].value_u64);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err)
+ goto out;
+
nla_nest_end(skb, arg);
}
return 0;
@@ -879,6 +919,708 @@ static int net_flow_cmd_get_table_graph(struct sk_buff *skb,
return genlmsg_reply(msg, info);
}
+static int net_flow_put_flow_action(struct sk_buff *skb,
+ struct net_flow_action *a)
+{
+ struct nlattr *action, *sigs;
+ int err = 0;
+
+ action = nla_nest_start(skb, NFL_ACTION);
+ if (!action)
+ return -EMSGSIZE;
+
+ if (nla_put_u32(skb, NFL_ACTION_ATTR_UID, a->uid))
+ return -EMSGSIZE;
+
+ if (!a->args)
+ goto done;
+
+ sigs = nla_nest_start(skb, NFL_ACTION_ATTR_SIGNATURE);
+ if (!sigs) {
+ nla_nest_cancel(skb, action);
+ return -EMSGSIZE;
+ }
+
+ err = net_flow_put_act_types(skb, a->args);
+ if (err) {
+ nla_nest_cancel(skb, action);
+ return err;
+ }
+ nla_nest_end(skb, sigs);
+done:
+ nla_nest_end(skb, action);
+ return 0;
+}
+
+static int net_flow_put_rule(struct sk_buff *skb, struct net_flow_rule *rule)
+{
+ struct nlattr *flows, *actions, *matches;
+ int j, i = 0;
+ int err = -EMSGSIZE;
+
+ flows = nla_nest_start(skb, NFL_FLOW);
+ if (!flows)
+ goto put_failure;
+
+ if (nla_put_u32(skb, NFL_ATTR_TABLE, rule->table_id) ||
+ nla_put_u32(skb, NFL_ATTR_UID, rule->uid) ||
+ nla_put_u32(skb, NFL_ATTR_PRIORITY, rule->priority))
+ goto flows_put_failure;
+
+ if (rule->matches) {
+ matches = nla_nest_start(skb, NFL_ATTR_MATCHES);
+ if (!matches)
+ goto flows_put_failure;
+
+ for (j = 0; rule->matches && rule->matches[j].header; j++) {
+ struct net_flow_field_ref *f = &rule->matches[j];
+ struct nlattr *field;
+
+ field = nla_nest_start(skb, NFL_FIELD_REF);
+ if (!field) {
+ err = -EMSGSIZE;
+ goto flows_put_failure;
+ }
+
+ err = net_flow_put_field_ref(skb, f);
+ if (err)
+ goto flows_put_failure;
+
+ err = net_flow_put_field_value(skb, f);
+ if (err)
+ goto flows_put_failure;
+
+ nla_nest_end(skb, field);
+ }
+ nla_nest_end(skb, matches);
+ }
+
+ if (rule->actions) {
+ actions = nla_nest_start(skb, NFL_ATTR_ACTIONS);
+ if (!actions)
+ goto flows_put_failure;
+
+ for (i = 0; rule->actions && rule->actions[i].uid; i++) {
+ err = net_flow_put_flow_action(skb, &rule->actions[i]);
+ if (err)
+ goto flows_put_failure;
+ }
+ nla_nest_end(skb, actions);
+ }
+
+ nla_nest_end(skb, flows);
+ return 0;
+
+flows_put_failure:
+ nla_nest_cancel(skb, flows);
+put_failure:
+ return err;
+}
+
+static struct sk_buff *net_flow_build_flows_msg(struct net_device *dev,
+ u32 portid, int seq, u8 cmd,
+ int min, int max, int table)
+{
+ struct genlmsghdr *hdr;
+ struct nlattr *flows;
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOBUFS);
+
+ hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(skb,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(skb, NFL_IDENTIFIER, dev->ifindex)) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ flows = nla_nest_start(skb, NFL_FLOWS);
+ if (!flows) {
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ err = -EOPNOTSUPP;
+ if (err < 0)
+ goto out_cancel;
+
+ nla_nest_end(skb, flows);
+
+ err = genlmsg_end(skb, hdr);
+ if (err < 0)
+ goto out;
+
+ return skb;
+out_cancel:
+ nla_nest_cancel(skb, flows);
+out:
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+}
+
+static const
+struct nla_policy net_flow_table_flows_policy[NFL_TABLE_FLOWS_MAX + 1] = {
+ [NFL_TABLE_FLOWS_TABLE] = { .type = NLA_U32,},
+ [NFL_TABLE_FLOWS_MINPRIO] = { .type = NLA_U32,},
+ [NFL_TABLE_FLOWS_MAXPRIO] = { .type = NLA_U32,},
+ [NFL_TABLE_FLOWS_FLOWS] = { .type = NLA_NESTED,},
+};
+
+static int net_flow_table_cmd_get_flows(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct nlattr *tb[NFL_TABLE_FLOWS_MAX+1];
+ int table, min = -1, max = -1;
+ struct net_device *dev;
+ struct sk_buff *msg;
+ int err = -EINVAL;
+
+ dev = net_flow_get_dev(info);
+ if (!dev)
+ return -EINVAL;
+
+ if (!info->attrs[NFL_IDENTIFIER_TYPE] ||
+ !info->attrs[NFL_IDENTIFIER] ||
+ !info->attrs[NFL_FLOWS])
+ goto out;
+
+ err = nla_parse_nested(tb, NFL_TABLE_FLOWS_MAX,
+ info->attrs[NFL_FLOWS],
+ net_flow_table_flows_policy);
+ if (err)
+ goto out;
+
+ if (!tb[NFL_TABLE_FLOWS_TABLE])
+ goto out;
+
+ table = nla_get_u32(tb[NFL_TABLE_FLOWS_TABLE]);
+
+ if (tb[NFL_TABLE_FLOWS_MINPRIO])
+ min = nla_get_u32(tb[NFL_TABLE_FLOWS_MINPRIO]);
+ if (tb[NFL_TABLE_FLOWS_MAXPRIO])
+ max = nla_get_u32(tb[NFL_TABLE_FLOWS_MAXPRIO]);
+
+ msg = net_flow_build_flows_msg(dev,
+ info->snd_portid,
+ info->snd_seq,
+ NFL_TABLE_CMD_GET_FLOWS,
+ min, max, table);
+ dev_put(dev);
+
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ return genlmsg_reply(msg, info);
+out:
+ dev_put(dev);
+ return err;
+}
+
+static struct sk_buff *net_flow_start_errmsg(struct net_device *dev,
+ struct genlmsghdr **hdr,
+ u32 portid, int seq, u8 cmd)
+{
+ struct genlmsghdr *h;
+ struct sk_buff *skb;
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-EMSGSIZE);
+
+ h = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
+ if (!h)
+ return ERR_PTR(-EMSGSIZE);
+
+ if (nla_put_u32(skb,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(skb, NFL_IDENTIFIER, dev->ifindex))
+ return ERR_PTR(-EMSGSIZE);
+
+ *hdr = h;
+ return skb;
+}
+
+static struct sk_buff *net_flow_end_flow_errmsg(struct sk_buff *skb,
+ struct genlmsghdr *hdr)
+{
+ int err;
+
+ err = genlmsg_end(skb, hdr);
+ if (err < 0) {
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+ }
+
+ return skb;
+}
+
+const struct nla_policy net_flow_field_policy[NFL_FIELD_REF_MAX + 1] = {
+ [NFL_FIELD_REF_NEXT_NODE] = { .type = NLA_U32,},
+ [NFL_FIELD_REF_INSTANCE] = { .type = NLA_U32,},
+ [NFL_FIELD_REF_HEADER] = { .type = NLA_U32,},
+ [NFL_FIELD_REF_FIELD] = { .type = NLA_U32,},
+ [NFL_FIELD_REF_MASK_TYPE] = { .type = NLA_U32,},
+ [NFL_FIELD_REF_TYPE] = { .type = NLA_U32,},
+ [NFL_FIELD_REF_VALUE] = { .type = NLA_BINARY,
+ .len = sizeof(u64)},
+ [NFL_FIELD_REF_MASK] = { .type = NLA_BINARY,
+ .len = sizeof(u64)},
+};
+
+static int net_flow_get_field(struct net_flow_field_ref *field,
+ struct nlattr *nla)
+{
+ struct nlattr *ref[NFL_FIELD_REF_MAX+1];
+ int err;
+
+ err = nla_parse_nested(ref, NFL_FIELD_REF_MAX,
+ nla, net_flow_field_policy);
+ if (err)
+ return err;
+
+ if (!ref[NFL_FIELD_REF_INSTANCE] ||
+ !ref[NFL_FIELD_REF_HEADER] ||
+ !ref[NFL_FIELD_REF_FIELD] ||
+ !ref[NFL_FIELD_REF_MASK_TYPE] ||
+ !ref[NFL_FIELD_REF_TYPE])
+ return -EINVAL;
+
+ field->instance = nla_get_u32(ref[NFL_FIELD_REF_INSTANCE]);
+ field->header = nla_get_u32(ref[NFL_FIELD_REF_HEADER]);
+ field->field = nla_get_u32(ref[NFL_FIELD_REF_FIELD]);
+ field->mask_type = nla_get_u32(ref[NFL_FIELD_REF_MASK_TYPE]);
+ field->type = nla_get_u32(ref[NFL_FIELD_REF_TYPE]);
+
+ if (!ref[NFL_FIELD_REF_VALUE])
+ return 0;
+
+ switch (field->type) {
+ case NFL_FIELD_REF_ATTR_TYPE_U8:
+ if (nla_len(ref[NFL_FIELD_REF_VALUE]) < sizeof(u8)) {
+ err = -EINVAL;
+ break;
+ }
+ field->value_u8 = nla_get_u8(ref[NFL_FIELD_REF_VALUE]);
+
+ if (!ref[NFL_FIELD_REF_MASK])
+ break;
+
+ if (nla_len(ref[NFL_FIELD_REF_MASK]) < sizeof(u8)) {
+ err = -EINVAL;
+ break;
+ }
+ field->mask_u8 = nla_get_u8(ref[NFL_FIELD_REF_MASK]);
+ break;
+ case NFL_FIELD_REF_ATTR_TYPE_U16:
+ if (nla_len(ref[NFL_FIELD_REF_VALUE]) < sizeof(u16)) {
+ err = -EINVAL;
+ break;
+ }
+ field->value_u16 = nla_get_u16(ref[NFL_FIELD_REF_VALUE]);
+
+ if (!ref[NFL_FIELD_REF_MASK])
+ break;
+
+ if (nla_len(ref[NFL_FIELD_REF_MASK]) < sizeof(u16)) {
+ err = -EINVAL;
+ break;
+ }
+ field->mask_u16 = nla_get_u16(ref[NFL_FIELD_REF_MASK]);
+ break;
+ case NFL_FIELD_REF_ATTR_TYPE_U32:
+ if (nla_len(ref[NFL_FIELD_REF_VALUE]) < sizeof(u32)) {
+ err = -EINVAL;
+ break;
+ }
+ field->value_u32 = nla_get_u32(ref[NFL_FIELD_REF_VALUE]);
+
+ if (!ref[NFL_FIELD_REF_MASK])
+ break;
+
+ if (nla_len(ref[NFL_FIELD_REF_MASK]) < sizeof(u32)) {
+ err = -EINVAL;
+ break;
+ }
+ field->mask_u32 = nla_get_u32(ref[NFL_FIELD_REF_MASK]);
+ break;
+ case NFL_FIELD_REF_ATTR_TYPE_U64:
+ if (nla_len(ref[NFL_FIELD_REF_VALUE]) < sizeof(u64)) {
+ err = -EINVAL;
+ break;
+ }
+ field->value_u64 = nla_get_u64(ref[NFL_FIELD_REF_VALUE]);
+
+ if (!ref[NFL_FIELD_REF_MASK])
+ break;
+
+ if (nla_len(ref[NFL_FIELD_REF_MASK]) < sizeof(u64)) {
+ err = -EINVAL;
+ break;
+ }
+ field->mask_u64 = nla_get_u64(ref[NFL_FIELD_REF_MASK]);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static void net_flow_free_actions(struct net_flow_action *actions)
+{
+ int i;
+
+ if (!actions)
+ return;
+
+ for (i = 0; actions[i].args; i++) {
+ kfree(actions[i].args->name);
+ kfree(actions[i].args);
+ }
+ kfree(actions);
+}
+
+static void net_flow_rule_free(struct net_flow_rule *rule)
+{
+ if (!rule)
+ return;
+
+ kfree(rule->matches);
+ net_flow_free_actions(rule->actions);
+ kfree(rule);
+}
+
+static const
+struct nla_policy net_flow_actarg_policy[NFL_ACTION_ARG_MAX + 1] = {
+ [NFL_ACTION_ARG_NAME] = { .type = NLA_STRING },
+ [NFL_ACTION_ARG_TYPE] = { .type = NLA_U32 },
+ [NFL_ACTION_ARG_VALUE] = { .type = NLA_BINARY, .len = sizeof(u64)},
+};
+
+static int net_flow_get_actarg(struct net_flow_action_arg *arg,
+ struct nlattr *attr)
+{
+ struct nlattr *r[NFL_ACTION_ARG_MAX+1];
+ int err;
+
+ err = nla_parse_nested(r, NFL_ACTION_ARG_MAX,
+ attr, net_flow_actarg_policy);
+ if (err)
+ return err;
+
+ if (!r[NFL_ACTION_ARG_TYPE] ||
+ !r[NFL_ACTION_ARG_VALUE])
+ return -EINVAL;
+
+ arg->type = nla_get_u32(r[NFL_ACTION_ARG_TYPE]);
+ switch (arg->type) {
+ case NFL_ACTION_ARG_TYPE_U8:
+ if (nla_len(r[NFL_ACTION_ARG_VALUE]) < sizeof(u8))
+ return -EINVAL;
+ arg->value_u8 = nla_get_u8(r[NFL_ACTION_ARG_VALUE]);
+ break;
+ case NFL_ACTION_ARG_TYPE_U16:
+ if (nla_len(r[NFL_ACTION_ARG_VALUE]) < sizeof(u16))
+ return -EINVAL;
+ arg->value_u16 = nla_get_u16(r[NFL_ACTION_ARG_VALUE]);
+ break;
+ case NFL_ACTION_ARG_TYPE_U32:
+ if (nla_len(r[NFL_ACTION_ARG_VALUE]) < sizeof(u32))
+ return -EINVAL;
+ arg->value_u32 = nla_get_u32(r[NFL_ACTION_ARG_VALUE]);
+ break;
+ case NFL_ACTION_ARG_TYPE_U64:
+ if (nla_len(r[NFL_ACTION_ARG_VALUE]) < sizeof(u64))
+ return -EINVAL;
+ arg->value_u64 = nla_get_u64(r[NFL_ACTION_ARG_VALUE]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (r[NFL_ACTION_ARG_NAME]) {
+ int max = nla_len(r[NFL_ACTION_ARG_NAME]);
+
+ if (max > NFL_MAX_NAME)
+ max = NFL_MAX_NAME;
+
+ arg->name = kzalloc(max, GFP_KERNEL);
+ if (!arg->name)
+ return -ENOMEM;
+ nla_strlcpy(arg->name, r[NFL_ACTION_ARG_NAME], max);
+ }
+
+ return 0;
+}
+
+static int net_flow_get_action(struct net_flow_action *a, struct nlattr *attr)
+{
+ struct nlattr *act[NFL_ACTION_ATTR_MAX+1];
+ struct nlattr *args;
+ int rem;
+ int err, count = 0;
+
+ if (nla_type(attr) != NFL_ACTION) {
+ pr_warn("%s: expected NFL_ACTION\n", __func__);
+ return 0;
+ }
+
+ err = nla_parse_nested(act, NFL_ACTION_ATTR_MAX,
+ attr, net_flow_action_policy);
+ if (err < 0)
+ return err;
+
+ if (!act[NFL_ACTION_ATTR_UID])
+ return -EINVAL;
+
+ a->uid = nla_get_u32(act[NFL_ACTION_ATTR_UID]);
+
+ /* Only need to parse signature if it is provided otherwise assume
+ * action does not need any arguments
+ */
+ if (!act[NFL_ACTION_ATTR_SIGNATURE])
+ return 0;
+
+ nla_for_each_nested(args, act[NFL_ACTION_ATTR_SIGNATURE], rem)
+ count++;
+
+ a->args = kcalloc(count + 1,
+ sizeof(struct net_flow_action_arg),
+ GFP_KERNEL);
+ count = 0;
+
+ nla_for_each_nested(args, act[NFL_ACTION_ATTR_SIGNATURE], rem) {
+ if (nla_type(args) != NFL_ACTION_ARG)
+ continue;
+
+ err = net_flow_get_actarg(&a->args[count], args);
+ if (err) {
+ kfree(a->args);
+ a->args = NULL;
+ return err;
+ }
+ count++;
+ }
+ return 0;
+}
+
+static const
+struct nla_policy net_flow_rule_policy[NFL_ATTR_MAX + 1] = {
+ [NFL_ATTR_TABLE] = { .type = NLA_U32 },
+ [NFL_ATTR_UID] = { .type = NLA_U32 },
+ [NFL_ATTR_PRIORITY] = { .type = NLA_U32 },
+ [NFL_ATTR_MATCHES] = { .type = NLA_NESTED },
+ [NFL_ATTR_ACTIONS] = { .type = NLA_NESTED },
+};
+
+static int net_flow_get_rule(struct net_flow_rule *rule, struct nlattr *attr)
+{
+ struct nlattr *f[NFL_ATTR_MAX+1];
+ struct nlattr *match, *act;
+ int rem, err;
+ int count = 0;
+
+ err = nla_parse_nested(f, NFL_ATTR_MAX,
+ attr, net_flow_rule_policy);
+ if (err < 0)
+ return -EINVAL;
+
+ if (!f[NFL_ATTR_TABLE] || !f[NFL_ATTR_UID] ||
+ !f[NFL_ATTR_PRIORITY])
+ return -EINVAL;
+
+ rule->table_id = nla_get_u32(f[NFL_ATTR_TABLE]);
+ rule->uid = nla_get_u32(f[NFL_ATTR_UID]);
+ rule->priority = nla_get_u32(f[NFL_ATTR_PRIORITY]);
+
+ rule->matches = NULL;
+ rule->actions = NULL;
+
+ if (f[NFL_ATTR_MATCHES]) {
+ nla_for_each_nested(match, f[NFL_ATTR_MATCHES], rem) {
+ if (nla_type(match) == NFL_FIELD_REF)
+ count++;
+ }
+
+ /* Null terminated list of matches */
+ rule->matches = kcalloc(count + 1,
+ sizeof(struct net_flow_field_ref),
+ GFP_KERNEL);
+ if (!rule->matches)
+ return -ENOMEM;
+
+ count = 0;
+ nla_for_each_nested(match, f[NFL_ATTR_MATCHES], rem) {
+ err = net_flow_get_field(&rule->matches[count], match);
+ if (err) {
+ kfree(rule->matches);
+ rule->matches = NULL;
+ return err;
+ }
+ count++;
+ }
+ }
+
+ if (f[NFL_ATTR_ACTIONS]) {
+ count = 0;
+ nla_for_each_nested(act, f[NFL_ATTR_ACTIONS], rem) {
+ if (nla_type(act) == NFL_ACTION)
+ count++;
+ }
+
+ /* Null terminated list of actions */
+ rule->actions = kcalloc(count + 1,
+ sizeof(struct net_flow_action),
+ GFP_KERNEL);
+ if (!rule->actions) {
+ kfree(rule->matches);
+ rule->matches = NULL;
+ return -ENOMEM;
+ }
+
+ count = 0;
+ nla_for_each_nested(act, f[NFL_ATTR_ACTIONS], rem) {
+ err = net_flow_get_action(&rule->actions[count], act);
+ if (err) {
+ kfree(rule->matches);
+ rule->matches = NULL;
+ net_flow_free_actions(rule->actions);
+ rule->actions = NULL;
+ return err;
+ }
+ count++;
+ }
+ }
+
+ return 0;
+}
+
+static int net_flow_table_cmd_flows(struct sk_buff *recv_skb,
+ struct genl_info *info)
+{
+ int rem, err_handle = NFL_FLOWS_ERROR_ABORT;
+ struct net_flow_rule *this = NULL;
+ struct sk_buff *skb = NULL;
+ struct genlmsghdr *hdr;
+ struct net_device *dev;
+ struct nlattr *flow, *flows;
+ int cmd = info->genlhdr->cmd;
+ int err = -EOPNOTSUPP;
+
+ dev = net_flow_get_dev(info);
+ if (!dev)
+ return -EINVAL;
+
+ switch (cmd) {
+ case NFL_TABLE_CMD_SET_FLOWS:
+ if (!dev->netdev_ops->ndo_flow_set_rule)
+ goto out;
+ break;
+ case NFL_TABLE_CMD_DEL_FLOWS:
+ if (!dev->netdev_ops->ndo_flow_del_rule)
+ goto out;
+ break;
+ default:
+ goto out;
+ }
+
+ if (!info->attrs[NFL_IDENTIFIER_TYPE] ||
+ !info->attrs[NFL_IDENTIFIER] ||
+ !info->attrs[NFL_FLOWS]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (info->attrs[NFL_FLOWS_ERROR])
+ err_handle = nla_get_u32(info->attrs[NFL_FLOWS_ERROR]);
+
+ net_flow_lock();
+ nla_for_each_nested(flow, info->attrs[NFL_FLOWS], rem) {
+ if (nla_type(flow) != NFL_FLOW)
+ continue;
+
+ this = kzalloc(sizeof(*this), GFP_KERNEL);
+ if (!this) {
+ err = -ENOMEM;
+ goto skip;
+ }
+
+ /* If userspace is passing invalid messages so that we can not
+ * even build correct flow structures abort with an error. And
+ * do not try to proceed regardless of error structure.
+ */
+ err = net_flow_get_rule(this, flow);
+ if (err)
+ goto out_locked;
+
+ switch (cmd) {
+ case NFL_TABLE_CMD_SET_FLOWS:
+ err = dev->netdev_ops->ndo_flow_set_rule(dev, this);
+ break;
+ case NFL_TABLE_CMD_DEL_FLOWS:
+ err = dev->netdev_ops->ndo_flow_del_rule(dev, this);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+skip:
+ if (err && err_handle != NFL_FLOWS_ERROR_CONTINUE) {
+ if (!skb) {
+ skb = net_flow_start_errmsg(dev, &hdr,
+ info->snd_portid,
+ info->snd_seq,
+ cmd);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ goto out_locked;
+ }
+
+ flows = nla_nest_start(skb, NFL_FLOWS);
+ if (!flows) {
+ err = -EMSGSIZE;
+ goto out_locked;
+ }
+ }
+
+ net_flow_put_rule(skb, this);
+ }
+
+ net_flow_rule_free(this);
+
+ if (err && err_handle == NFL_FLOWS_ERROR_ABORT)
+ goto out_locked;
+ }
+ net_flow_unlock();
+ dev_put(dev);
+
+ if (skb) {
+ nla_nest_end(skb, flows);
+ net_flow_end_flow_errmsg(skb, hdr);
+ return genlmsg_reply(skb, info);
+ }
+ return 0;
+
+out_locked:
+ net_flow_unlock();
+out:
+ net_flow_rule_free(this);
+ nlmsg_free(skb);
+ dev_put(dev);
+ return err;
+}
+
static const struct nla_policy net_flow_cmd_policy[NFL_MAX + 1] = {
[NFL_IDENTIFIER_TYPE] = {.type = NLA_U32, },
[NFL_IDENTIFIER] = {.type = NLA_U32, },
@@ -920,6 +1662,24 @@ static const struct genl_ops net_flow_table_nl_ops[] = {
.policy = net_flow_cmd_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NFL_TABLE_CMD_GET_FLOWS,
+ .doit = net_flow_table_cmd_get_flows,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NFL_TABLE_CMD_SET_FLOWS,
+ .doit = net_flow_table_cmd_flows,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NFL_TABLE_CMD_DEL_FLOWS,
+ .doit = net_flow_table_cmd_flows,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
static int __init net_flow_nl_module_init(void)
^ permalink raw reply related
* [net-next PATCH v2 01/12] net: flow_table: create interface for hw match/action tables
From: John Fastabend @ 2015-01-13 21:35 UTC (permalink / raw)
To: tgraf, simon.horman, sfeldma; +Cc: netdev, gerlitz.or, jhs, andy, davem
In-Reply-To: <20150113212941.13874.48692.stgit@nitbit.x32>
Currently, we do not have an interface to query hardware and learn
the capabilities of the device. This makes it very difficult to use
hardware flow tables.
At the moment the only interface we have to work with hardware flow
tables is ethtool. This has many deficiencies, first its ioctl based
making it difficult to use in systems that need to monitor interfaces
because there is no support for multicast, notifiers, etc.
The next big gap is it doesn't support querying devices for
capabilities. The only way to learn hardware entries is by doing a
"try and see" operation. An error perhaps indicating the device can
not support your request but could be possibly for other reasons.
Maybe a table is full for example. The existing flow interface only
supports a single ingress table which is sufficient for some of the
existing NIC host interfaces but limiting for more advanced NIC
interfaces and switch devices.
Also it is not extensible without recompiling both drivers and core
interfaces. It may be possible to reprogram a device with additional
header types, new protocols, whatever and it would be great if the
flow table infrastructure can handle this.
So this patch scraps the ethtool flow classifier interface and
creates a new flow table interface. It is expected that device that
support the existing ethtool interface today can support both
interfaces without too much difficulty. I did a proof point on the
ixgbe driver. Only choosing ixgbe because I have a 82599 10Gbps
device in my development system. A more thorough implementation
was done for the rocker switch showing how to use the interface.
In this patch we create interfaces to get the headers a device
supports, the actions it supports, a header graph showing the
relationship between headers the device supports, the tables
supported by the device and how they are connected.
This patch _only_ provides the get routines in an attempt to
make the patch sequence manageable.
get_headers :
report a set of headers/fields the device supports. These
are specified as length/offsets so we can support standard
protocols or vendor specific headers. This is more flexible
then bitmasks of pre-defined packet types. In 'tc' for example
I may use u32 to match on proprietary or vendor specific fields.
A bitmask approach does not allow for this, but defining the
fields as a set of offsets and lengths allows for this.
A device that supports Openflow version 1.x for example could
provide the set of field/offsets that are equivelent to the
specification.
One property of this type of interface is I don't have to
rebuild my kernel/driver header interfaces, etc to support the
latest and greatest trendy protocol foo.
For some types of metadata the device understands we also
use header fields to represent these. One example of this is
we may have an ingress_port metadata field to report the
port a packet was received on. At the moment we expect the
metadata fields to be defined outside the interface. We can
standardize on common ones such "ingress_port" across devices.
Some examples of outside definitions specifying metadata
might be OVS, internal definitions like skb->mark, or some
FoRCES definitions.
get_header_graph :
Simply providing a header/field offset I support is not sufficient
to learn how many nested 802.1Q tags I can support and other
similar cases where the ordering of headers matters.
So we use this operation to query the device for a header
graph showing how the headers need to be related.
With this operation and the 'get_headers' operation you can
interrogate the driver with questions like "do you support
Q'in'Q?", "how many VLAN tags can I nest before the parser
breaks?", "Do you support MPLS?", "How about Foo Header in
a VXLAN tunnel?".
get_actions :
Report a list of actions supported by the device along with the
arguments they take. So "drop_packet" action takes no arguments
and "set_field" action takes two arguments a field and value.
This suffers again from being slightly opaque. Meaning if a device
reports back action "foo_bar" with three arguments how do I as a
consumer of this "know" what that action is? The easy thing to do
is punt on it and say it should be described outside the driver
somewhere. OVS for example defines a set of actions. If my FoRCeS
quick read is correct they define actions using text in the
messaging interface. A follow up patch series could use a
description language to describe actions. Possibly using something
from eBPF or nftables for example. This patch will not try to
solve the isuse now and expect actions are defined outside the API
or are well known.
get_tables :
Hardware may support one or more tables. Each table supports a set
of matches and a set of actions. The match fields supported are
defined above by the 'get_headers' operations. Similarly the actions
supported are defined by the 'get_actions' operation.
This allows the hardware to report several tables all with distinct
capabilities. Tables also have table attributes used to describe
features of the table. Because netlink messages are TLV based we
can easily add new table attribues as needed.
Currently a table has two attributes size and source. The size
indicates how many "slots" are in the table for flow entries. One
caveat here is a rule in the flow table may consume multiple slots
in the table. We deal with this in a subsequent patch.
The source field is used to indicate table boundaries where actions
are applied. A table with the same source value will not "see"
actions from tables with the same source. An example where this is
relavent would be to have an action to re-write the destiniation
IP address of a packet. If you have a match rule in a table with
the same source that matches on the new IP address it will not be
hit. However if it is in a table with a different source value
_and_ in another table that gets applied the rule will be hit. See
the next operatoin for querying table ordering.
Some basic hardware may only support a single table which simplifies
some things. But even the simple 10/40Gbps NICs support multiple
tables and different tables depending on ingress/egress.
get_table_graph :
When a device supports multiple tables we need to identify how the
tables are connected when each table is executed.
To do this we provide a table graph which gives the pipeline of the
device. The graph gives nodes representing each table and the edges
indicate the criteria to progress to the next flow table. There are
examples of this type of thing in both FoRCES and OVS. OVS
prescribes a set of tables reachable with goto actions and FoRCES a
slightly more flexible arrangement. In software tc's u32 classifier
allows "linking" hash tables together. The OVS dataplane with the
support of 'goto' action is completely connected. Without the
'goto' action the tables are progressed linearly.
By querying the graph from hardware we can "learn" what table flows
are supported and map them into software.
We also provide a bit to indicate if the node is a root node of the
ingress pipeline or egress pipeline. This is used on devices that
have different pipelines for ingres and egress. This appears to be
fairly common for devices. The realtek chip presented at LPC in
Dusseldorf for example appeared to have a separate ingress/egress
pipeline.
With these five operations software can learn what types of fields
the hardware flow table supports and how they are arranged. Subsequent
patches will address programming the flow tables.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
include/linux/if_flow.h | 188 ++++++++
include/linux/netdevice.h | 38 ++
include/uapi/linux/if_flow.h | 389 +++++++++++++++++
net/Kconfig | 7
net/core/Makefile | 1
net/core/flow_table.c | 942 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 1565 insertions(+)
create mode 100644 include/linux/if_flow.h
create mode 100644 include/uapi/linux/if_flow.h
create mode 100644 net/core/flow_table.c
diff --git a/include/linux/if_flow.h b/include/linux/if_flow.h
new file mode 100644
index 0000000..3e1829e
--- /dev/null
+++ b/include/linux/if_flow.h
@@ -0,0 +1,188 @@
+/*
+ * include/linux/net/if_flow.h - Flow table interface for Switch devices
+ * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Author: John Fastabend <john.r.fastabend@intel.com>
+ */
+
+#ifndef _IF_FLOW_H
+#define _IF_FLOW_H
+
+#include <uapi/linux/if_flow.h>
+
+/**
+ * @struct net_flow_fields
+ * @brief defines a field in a header
+ *
+ * @name string identifier for pretty printing
+ * @uid unique identifier for field
+ * @bitwidth length of field in bits
+ */
+struct net_flow_field {
+ char *name;
+ int uid;
+ int bitwidth;
+};
+
+/**
+ * @struct net_flow_hdr
+ * @brief defines a match (header/field) an endpoint can use
+ *
+ * @name string identifier for pretty printing
+ * @uid unique identifier for header
+ * @field_sz number of fields are in the set
+ * @fields the set of fields in the net_flow_hdr
+ */
+struct net_flow_hdr {
+ char *name;
+ int uid;
+ int field_sz;
+ struct net_flow_field *fields;
+};
+
+/**
+ * @struct net_flow_action_arg
+ * @brief encodes action arguments in structures one per argument
+ *
+ * @name string identifier for pretty printing
+ * @type type of argument either u8, u16, u32, u64
+ * @value_# indicate value/mask value type on of u8, u16, u32, or u64
+ */
+struct net_flow_action_arg {
+ char *name;
+ enum net_flow_action_arg_type type;
+ union {
+ __u8 value_u8;
+ __u16 value_u16;
+ __u32 value_u32;
+ __u64 value_u64;
+ };
+};
+
+/**
+ * @struct net_flow_action
+ * @brief a description of a endpoint defined action
+ *
+ * @name printable name
+ * @uid unique action identifier
+ * @args null terminated list of action arguments
+ */
+struct net_flow_action {
+ char *name;
+ int uid;
+ struct net_flow_action_arg *args;
+};
+
+/**
+ * @struct net_flow_field_ref
+ * @brief uniquely identify field as instance:header:field tuple
+ *
+ * @instance identify unique instance of field reference
+ * @header identify unique header reference
+ * @field identify unique field in above header reference
+ * @mask_type indicate mask type
+ * @type indicate value/mask value type on of u8, u16, u32, or u64
+ * @value_u# value of field reference
+ * @mask_u# mask value of field reference
+ */
+struct net_flow_field_ref {
+ int instance;
+ int header;
+ int field;
+ int mask_type;
+ int type;
+ union {
+ struct {
+ __u8 value_u8;
+ __u8 mask_u8;
+ };
+ struct {
+ __u16 value_u16;
+ __u16 mask_u16;
+ };
+ struct {
+ __u32 value_u32;
+ __u32 mask_u32;
+ };
+ struct {
+ __u64 value_u64;
+ __u64 mask_u64;
+ };
+ };
+};
+
+/**
+ * @struct net_flow_tbl
+ * @brief define flow table with supported match/actions
+ *
+ * @name string identifier for pretty printing
+ * @uid unique identifier for table
+ * @source uid of parent table
+ * @apply_action actions in the same apply group are applied in one step
+ * @size max number of entries for table or -1 for unbounded
+ * @matches null terminated set of supported match types given by match uid
+ * @actions null terminated set of supported action types given by action uid
+ */
+struct net_flow_tbl {
+ char *name;
+ int uid;
+ int source;
+ int apply_action;
+ int size;
+ struct net_flow_field_ref *matches;
+ int *actions;
+};
+
+/**
+ * @struct net_flow_jump_table
+ * @brief encodes an edge of the table graph or header graph
+ *
+ * @field field reference must be true to follow edge
+ * @node node identifier to connect edge to
+ */
+
+struct net_flow_jump_table {
+ struct net_flow_field_ref field;
+ int node; /* <0 is a parser error */
+};
+
+/* @struct net_flow_hdr_node
+ * @brief node in a header graph of header fields.
+ *
+ * @name string identifier for pretty printing
+ * @uid unique id of the graph node
+ * @hdrs null terminated list of hdrs identified by this node
+ * @jump encoding of graph structure as a case jump statement
+ */
+struct net_flow_hdr_node {
+ char *name;
+ int uid;
+ int *hdrs;
+ struct net_flow_jump_table *jump;
+};
+
+/* @struct net_flow_tbl_node
+ * @brief
+ *
+ * @uid unique id of the table node
+ * @flags bitmask of table attributes
+ * @jump encoding of graph structure as a case jump statement
+ */
+struct net_flow_tbl_node {
+ int uid;
+ __u32 flags;
+ struct net_flow_jump_table *jump;
+};
+#endif
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 679e6e9..74481b9 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -52,6 +52,10 @@
#include <linux/neighbour.h>
#include <uapi/linux/netdevice.h>
+#ifdef CONFIG_NET_FLOW_TABLES
+#include <linux/if_flow.h>
+#endif
+
struct netpoll_info;
struct device;
struct phy_device;
@@ -1030,6 +1034,33 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* int (*ndo_switch_port_stp_update)(struct net_device *dev, u8 state);
* Called to notify switch device port of bridge port STP
* state change.
+ *
+ * struct net_flow_action **(*ndo_flow_get_actions)(struct net_device *dev)
+ * Report a null terminated list of actions supported by the device along
+ * with the arguments they take.
+ *
+ * struct net_flow_tbl **(*ndo_flow_get_tbls)(struct net_device *dev)
+ * Report a null terminated list of tables supported by the device.
+ * Including the match fields and actions supported. The match fields
+ * are defined by the 'ndo_flow_get_hdrs' op and the actions are defined
+ * by 'ndo_flow_get_actions' op.
+ *
+ * struct net_flow_tbl_node **(*ndo_flow_get_tbl_graph)(struct net_device *dev)
+ * Report a null terminated list of nodes defining the table graph. When
+ * a device supports multiple tables we need to identify how the tables
+ * are connected and in what order are the tables traversed. The table
+ * nodes returned here provide the graph required to learn this.
+ *
+ * struct net_flow_hdr **(*ndo_flow_get_hdrs)(struct net_device *dev)
+ * Report a null terminated list of headers+fields supported by the
+ * device. See net_flow_hdr struct for details on header/field layout
+ * the basic logic is by giving the byte/length/offset of each field
+ * the device can define the protocols it supports.
+ *
+ * struct net_flow_hdr_node **(*ndo_flow_get_hdr_graph)(struct net_device *dev)
+ * Report a null terminated list of nodes defining the header graph. This
+ * provides the necessary graph to learn the ordering of headers supported
+ * by the device.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
@@ -1190,6 +1221,13 @@ struct net_device_ops {
int (*ndo_switch_port_stp_update)(struct net_device *dev,
u8 state);
#endif
+#ifdef CONFIG_NET_FLOW_TABLES
+ struct net_flow_action **(*ndo_flow_get_actions)(struct net_device *dev);
+ struct net_flow_tbl **(*ndo_flow_get_tbls)(struct net_device *dev);
+ struct net_flow_tbl_node **(*ndo_flow_get_tbl_graph)(struct net_device *dev);
+ struct net_flow_hdr **(*ndo_flow_get_hdrs)(struct net_device *dev);
+ struct net_flow_hdr_node **(*ndo_flow_get_hdr_graph)(struct net_device *dev);
+#endif
};
/**
diff --git a/include/uapi/linux/if_flow.h b/include/uapi/linux/if_flow.h
new file mode 100644
index 0000000..4ab2fa0
--- /dev/null
+++ b/include/uapi/linux/if_flow.h
@@ -0,0 +1,389 @@
+/*
+ * include/uapi/linux/if_flow.h - Flow table interface for Switch devices
+ * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Author: John Fastabend <john.r.fastabend@intel.com>
+ */
+
+/* Netlink description:
+ *
+ * Table definition used to describe running tables. The following
+ * describes the netlink format used by the flow API.
+ *
+ * Flow table definitions used to define tables.
+ *
+ * [NFL_TABLE_IDENTIFIER_TYPE]
+ * [NFL_TABLE_IDENTIFIER]
+ * [NFL_TABLE_TABLES]
+ * [NFL_TABLE]
+ * [NFL_TABLE_ATTR_NAME]
+ * [NFL_TABLE_ATTR_UID]
+ * [NFL_TABLE_ATTR_SOURCE]
+ * [NFL_TABLE_ATTR_APPLY]
+ * [NFL_TABLE_ATTR_SIZE]
+ * [NFL_TABLE_ATTR_MATCHES]
+ * [NFL_FIELD_REF]
+ * [NFL_FIELD_REF_INSTANCE]
+ * [NFL_FIELD_REF_HEADER]
+ * [NFL_FIELD_REF_FIELD]
+ * [NFL_FIELD_REF_MASK]
+ * [NFL_FIELD_REF_TYPE]
+ * [...]
+ * [NFL_TABLE_ATTR_ACTIONS]
+ * [NFL_ACTION_ATTR_UID]
+ * [...]
+ * [NFL_TABLE]
+ * [...]
+ *
+ * Header definitions used to define headers with user friendly
+ * names.
+ *
+ * [NFL_TABLE_HEADERS]
+ * [NFL_HEADER]
+ * [NFL_HEADER_ATTR_NAME]
+ * [NFL_HEADER_ATTR_UID]
+ * [NFL_HEADER_ATTR_FIELDS]
+ * [NFL_HEADER_ATTR_FIELD]
+ * [NFL_FIELD_ATTR_NAME]
+ * [NFL_FIELD_ATTR_UID]
+ * [NFL_FIELD_ATTR_BITWIDTH]
+ * [NFL_HEADER_ATTR_FIELD]
+ * [...]
+ * [...]
+ * [NFL_HEADER]
+ * [...]
+ * [...]
+ *
+ * Action definitions supported by tables
+ *
+ * [NFL_TABLE_ACTIONS]
+ * [NFL_TABLE_ATTR_ACTIONS]
+ * [NFL_ACTION]
+ * [NFL_ACTION_ATTR_NAME]
+ * [NFL_ACTION_ATTR_UID]
+ * [NFL_ACTION_ATTR_SIGNATURE]
+ * [NFL_ACTION_ARG]
+ * [NFL_ACTION_ARG_NAME]
+ * [NFL_ACTION_ARG_TYPE]
+ * [...]
+ * [NFL_ACTION]
+ * [...]
+ *
+ * Then two get definitions for the headers graph and the table graph
+ * The header graph gives an encoded graph to describe how the device
+ * parses the headers. Use this to learn if a specific protocol is
+ * supported in the current device configuration. The table graph
+ * reports how tables are traversed by packets.
+ *
+ * Get Headers Graph <Request> only requires msg preamble.
+ *
+ * Get Headers Graph <Reply> description
+ *
+ * [NFL_HEADER_GRAPH]
+ * [NFL_HEADER_GRAPH_NODE]
+ * [NFL_HEADER_NODE_NAME]
+ * [NFL_HEADER_NODE_HDRS]
+ * [NFL_HEADER_NODE_HDRS_VALUE]
+ * [...]
+ * [NFL_HEADER_NODE_JUMP]]
+ * [NFL_JUMP_ENTRY]
+ * [NFL_FIELD_REF_NEXT_NODE]
+ * [NFL_FIELD_REF_INSTANCE]
+ * [NFL_FIELD_REF_HEADER]
+ * [NFL_FIELD_REF_FIELD]
+ * [NFL_FIELD_REF_MASK]
+ * [NFL_FIELD_REF_TYPE]
+ * [NFL_FIELD_REF_VALUE]
+ * [NFL_FIELD_REF_MASK]
+ * [...]
+ * [NFL_HEADER_GRAPH_NODE]
+ * [
+ *
+ * Get Table Graph <Request> only requires msg preamble.
+ *
+ * Get Table Graph <Reply> description
+ *
+ * [NFL_TABLE_GRAPH]
+ * [NFL_TABLE_GRAPH_NODE]
+ * [NFL_TABLE_GRAPH_NODE_UID]
+ * [NFL_TABLE_GRAPH_NODE_JUMP]
+ * [NFL_JUMP_ENTRY]
+ * [NFL_FIELD_REF_NEXT_NODE]
+ * [NFL_FIELD_REF_INSTANCE]
+ * [NFL_FIELD_REF_HEADER]
+ * [NFL_FIELD_REF_FIELD]
+ * [NFL_FIELD_REF_MASK]
+ * [NFL_FIELD_REF_TYPE]
+ * [NFL_FIELD_REF_VALUE]
+ * [NFL_FIELD_REF_MASK]
+ * [...]
+ * [NFL_TABLE_GRAPH_NODE]
+ * [..]
+ */
+
+#ifndef _UAPI_LINUX_IF_FLOW
+#define _UAPI_LINUX_IF_FLOW
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/if.h>
+
+enum {
+ NFL_FIELD_UNSPEC,
+ NFL_FIELD,
+ __NFL_FIELD_MAX,
+};
+
+#define NFL_FIELD_MAX (__NFL_FIELD_MAX - 1)
+
+enum {
+ NFL_FIELD_ATTR_UNSPEC,
+ NFL_FIELD_ATTR_NAME,
+ NFL_FIELD_ATTR_UID,
+ NFL_FIELD_ATTR_BITWIDTH,
+ __NFL_FIELD_ATTR_MAX,
+};
+
+#define NFL_FIELD_ATTR_MAX (__NFL_FIELD_ATTR_MAX - 1)
+
+enum {
+ NFL_HEADER_UNSPEC,
+ NFL_HEADER,
+ __NFL_HEADER_MAX,
+};
+
+#define NFL_HEADER_MAX (__NFL_HEADER_MAX - 1)
+
+enum {
+ NFL_HEADER_ATTR_UNSPEC,
+ NFL_HEADER_ATTR_NAME,
+ NFL_HEADER_ATTR_UID,
+ NFL_HEADER_ATTR_FIELDS,
+ __NFL_HEADER_ATTR_MAX,
+};
+
+#define NFL_HEADER_ATTR_MAX (__NFL_HEADER_ATTR_MAX - 1)
+
+enum {
+ NFL_MASK_TYPE_UNSPEC,
+ NFL_MASK_TYPE_EXACT,
+ NFL_MASK_TYPE_LPM,
+ NFL_MASK_TYPE_MASK,
+};
+
+enum {
+ NFL_FIELD_REF_UNSPEC,
+ NFL_FIELD_REF_NEXT_NODE,
+ NFL_FIELD_REF_INSTANCE,
+ NFL_FIELD_REF_HEADER,
+ NFL_FIELD_REF_FIELD,
+ NFL_FIELD_REF_MASK_TYPE,
+ NFL_FIELD_REF_TYPE,
+ NFL_FIELD_REF_VALUE,
+ NFL_FIELD_REF_MASK,
+ __NFL_FIELD_REF_MAX,
+};
+
+#define NFL_FIELD_REF_MAX (__NFL_FIELD_REF_MAX - 1)
+
+enum {
+ NFL_FIELD_REFS_UNSPEC,
+ NFL_FIELD_REF,
+ __NFL_FIELD_REFS_MAX,
+};
+
+#define NFL_FIELD_REFS_MAX (__NFL_FIELD_REFS_MAX - 1)
+
+enum {
+ NFL_FIELD_REF_ATTR_TYPE_UNSPEC,
+ NFL_FIELD_REF_ATTR_TYPE_U8,
+ NFL_FIELD_REF_ATTR_TYPE_U16,
+ NFL_FIELD_REF_ATTR_TYPE_U32,
+ NFL_FIELD_REF_ATTR_TYPE_U64,
+};
+
+enum net_flow_action_arg_type {
+ NFL_ACTION_ARG_TYPE_NULL,
+ NFL_ACTION_ARG_TYPE_U8,
+ NFL_ACTION_ARG_TYPE_U16,
+ NFL_ACTION_ARG_TYPE_U32,
+ NFL_ACTION_ARG_TYPE_U64,
+ __NFL_ACTION_ARG_TYPE_VAL_MAX,
+};
+
+enum {
+ NFL_ACTION_ARG_UNSPEC,
+ NFL_ACTION_ARG_NAME,
+ NFL_ACTION_ARG_TYPE,
+ NFL_ACTION_ARG_VALUE,
+ __NFL_ACTION_ARG_MAX,
+};
+
+#define NFL_ACTION_ARG_MAX (__NFL_ACTION_ARG_MAX - 1)
+
+enum {
+ NFL_ACTION_ARGS_UNSPEC,
+ NFL_ACTION_ARG,
+ __NFL_ACTION_ARGS_MAX,
+};
+
+#define NFL_ACTION_ARGS_MAX (__NFL_ACTION_ARGS_MAX - 1)
+
+enum {
+ NFL_ACTION_UNSPEC,
+ NFL_ACTION,
+ __NFL_ACTION_MAX,
+};
+
+#define NFL_ACTION_MAX (__NFL_ACTION_MAX - 1)
+
+enum {
+ NFL_ACTION_ATTR_UNSPEC,
+ NFL_ACTION_ATTR_NAME,
+ NFL_ACTION_ATTR_UID,
+ NFL_ACTION_ATTR_SIGNATURE,
+ __NFL_ACTION_ATTR_MAX,
+};
+
+#define NFL_ACTION_ATTR_MAX (__NFL_ACTION_ATTR_MAX - 1)
+
+enum {
+ NFL_ACTION_SET_UNSPEC,
+ NFL_ACTION_SET_ACTIONS,
+ __NFL_ACTION_SET_MAX,
+};
+
+#define NFL_ACTION_SET_MAX (__NFL_ACTION_SET_MAX - 1)
+
+enum {
+ NFL_TABLE_UNSPEC,
+ NFL_TABLE,
+ __NFL_TABLE_MAX,
+};
+
+#define NFL_TABLE_MAX (__NFL_TABLE_MAX - 1)
+
+enum {
+ NFL_TABLE_ATTR_UNSPEC,
+ NFL_TABLE_ATTR_NAME,
+ NFL_TABLE_ATTR_UID,
+ NFL_TABLE_ATTR_SOURCE,
+ NFL_TABLE_ATTR_APPLY,
+ NFL_TABLE_ATTR_SIZE,
+ NFL_TABLE_ATTR_MATCHES,
+ NFL_TABLE_ATTR_ACTIONS,
+ __NFL_TABLE_ATTR_MAX,
+};
+
+#define NFL_TABLE_ATTR_MAX (__NFL_TABLE_ATTR_MAX - 1)
+
+#define NFL_JUMP_TABLE_DONE -1
+enum {
+ NFL_JUMP_ENTRY_UNSPEC,
+ NFL_JUMP_ENTRY,
+ __NFL_JUMP_ENTRY_MAX,
+};
+
+enum {
+ NFL_HEADER_NODE_HDRS_UNSPEC,
+ NFL_HEADER_NODE_HDRS_VALUE,
+ __NFL_HEADER_NODE_HDRS_MAX,
+};
+
+#define NFL_HEADER_NODE_HDRS_MAX (__NFL_HEADER_NODE_HDRS_MAX - 1)
+
+enum {
+ NFL_HEADER_NODE_UNSPEC,
+ NFL_HEADER_NODE_NAME,
+ NFL_HEADER_NODE_UID,
+ NFL_HEADER_NODE_HDRS,
+ NFL_HEADER_NODE_JUMP,
+ __NFL_HEADER_NODE_MAX,
+};
+
+#define NFL_HEADER_NODE_MAX (__NFL_HEADER_NODE_MAX - 1)
+
+enum {
+ NFL_HEADER_GRAPH_UNSPEC,
+ NFL_HEADER_GRAPH_NODE,
+ __NFL_HEADER_GRAPH_MAX,
+};
+
+#define NFL_HEADER_GRAPH_MAX (__NFL_HEADER_GRAPH_MAX - 1)
+
+#define NFL_TABLE_EGRESS_ROOT 1
+#define NFL_TABLE_INGRESS_ROOT 2
+
+enum {
+ NFL_TABLE_GRAPH_NODE_UNSPEC,
+ NFL_TABLE_GRAPH_NODE_UID,
+ NFL_TABLE_GRAPH_NODE_FLAGS,
+ NFL_TABLE_GRAPH_NODE_JUMP,
+ __NFL_TABLE_GRAPH_NODE_MAX,
+};
+
+#define NFL_TABLE_GRAPH_NODE_MAX (__NFL_TABLE_GRAPH_NODE_MAX - 1)
+
+enum {
+ NFL_TABLE_GRAPH_UNSPEC,
+ NFL_TABLE_GRAPH_NODE,
+ __NFL_TABLE_GRAPH_MAX,
+};
+
+#define NFL_TABLE_GRAPH_MAX (__NFL_TABLE_GRAPH_MAX - 1)
+
+enum {
+ NFL_NFL_UNSPEC,
+ NFL_FLOW,
+ __NFL_NFL_MAX,
+};
+
+#define NFL_NFL_MAX (__NFL_NFL_MAX - 1)
+
+enum {
+ NFL_IDENTIFIER_UNSPEC,
+ NFL_IDENTIFIER_IFINDEX, /* net_device ifindex */
+};
+
+enum {
+ NFL_UNSPEC,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER,
+
+ NFL_TABLES,
+ NFL_HEADERS,
+ NFL_ACTIONS,
+ NFL_HEADER_GRAPH,
+ NFL_TABLE_GRAPH,
+
+ __NFL_MAX,
+ NFL_MAX = (__NFL_MAX - 1),
+};
+
+enum {
+ NFL_TABLE_CMD_GET_TABLES,
+ NFL_TABLE_CMD_GET_HEADERS,
+ NFL_TABLE_CMD_GET_ACTIONS,
+ NFL_TABLE_CMD_GET_HDR_GRAPH,
+ NFL_TABLE_CMD_GET_TABLE_GRAPH,
+
+ __NFL_CMD_MAX,
+ NFL_CMD_MAX = (__NFL_CMD_MAX - 1),
+};
+
+#define NFL_GENL_NAME "net_flow_nl"
+#define NFL_GENL_VERSION 0x1
+#endif /* _UAPI_LINUX_IF_FLOW */
diff --git a/net/Kconfig b/net/Kconfig
index ff9ffc1..8380bfe 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -293,6 +293,13 @@ config NET_FLOW_LIMIT
with many clients some protection against DoS by a single (spoofed)
flow that greatly exceeds average workload.
+config NET_FLOW_TABLES
+ boolean "Support network flow tables"
+ ---help---
+ This feature provides an interface for device drivers to report
+ flow tables and supported matches and actions. If you do not
+ want to support hardware offloads for flow tables, say N here.
+
menu "Network testing"
config NET_PKTGEN
diff --git a/net/core/Makefile b/net/core/Makefile
index 235e6c5..1eea785 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o
obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
+obj-$(CONFIG_NET_FLOW_TABLES) += flow_table.o
diff --git a/net/core/flow_table.c b/net/core/flow_table.c
new file mode 100644
index 0000000..ce673df
--- /dev/null
+++ b/net/core/flow_table.c
@@ -0,0 +1,942 @@
+/*
+ * include/uapi/linux/if_flow.h - Flow table interface for Switch devices
+ * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Author: John Fastabend <john.r.fastabend@intel.com>
+ */
+
+#include <uapi/linux/if_flow.h>
+#include <linux/if_flow.h>
+#include <linux/if_bridge.h>
+#include <linux/types.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <net/rtnetlink.h>
+#include <linux/module.h>
+
+static struct genl_family net_flow_nl_family = {
+ .id = GENL_ID_GENERATE,
+ .name = NFL_GENL_NAME,
+ .version = NFL_GENL_VERSION,
+ .maxattr = NFL_MAX,
+ .netnsok = true,
+};
+
+static struct net_device *net_flow_get_dev(struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ int type, ifindex;
+
+ if (!info->attrs[NFL_IDENTIFIER_TYPE] ||
+ !info->attrs[NFL_IDENTIFIER])
+ return NULL;
+
+ type = nla_get_u32(info->attrs[NFL_IDENTIFIER_TYPE]);
+ switch (type) {
+ case NFL_IDENTIFIER_IFINDEX:
+ ifindex = nla_get_u32(info->attrs[NFL_IDENTIFIER]);
+ break;
+ default:
+ return NULL;
+ }
+
+ return dev_get_by_index(net, ifindex);
+}
+
+static int net_flow_put_act_types(struct sk_buff *skb,
+ struct net_flow_action_arg *args)
+{
+ struct nlattr *arg;
+ int i, err;
+
+ for (i = 0; args[i].type; i++) {
+ arg = nla_nest_start(skb, NFL_ACTION_ARG);
+ if (!arg)
+ return -EMSGSIZE;
+
+ if (args[i].name) {
+ err = nla_put_string(skb, NFL_ACTION_ARG_NAME,
+ args[i].name);
+ if (err)
+ goto out;
+ }
+
+ err = nla_put_u32(skb, NFL_ACTION_ARG_TYPE, args[i].type);
+ if (err)
+ goto out;
+
+ nla_nest_end(skb, arg);
+ }
+ return 0;
+out:
+ nla_nest_cancel(skb, arg);
+ return err;
+}
+
+static const
+struct nla_policy net_flow_action_policy[NFL_ACTION_ATTR_MAX + 1] = {
+ [NFL_ACTION_ATTR_NAME] = {.type = NLA_STRING },
+ [NFL_ACTION_ATTR_UID] = {.type = NLA_U32 },
+ [NFL_ACTION_ATTR_SIGNATURE] = {.type = NLA_NESTED },
+};
+
+static int net_flow_put_action(struct sk_buff *skb, struct net_flow_action *a)
+{
+ struct nlattr *nest;
+ int err;
+
+ if (a->name && nla_put_string(skb, NFL_ACTION_ATTR_NAME, a->name))
+ return -EMSGSIZE;
+
+ if (nla_put_u32(skb, NFL_ACTION_ATTR_UID, a->uid))
+ return -EMSGSIZE;
+
+ if (a->args) {
+ nest = nla_nest_start(skb, NFL_ACTION_ATTR_SIGNATURE);
+ if (!nest)
+ return -EMSGSIZE;
+
+ err = net_flow_put_act_types(skb, a->args);
+ if (err) {
+ nla_nest_cancel(skb, nest);
+ return err;
+ }
+ nla_nest_end(skb, nest);
+ }
+
+ return 0;
+}
+
+static int net_flow_put_actions(struct sk_buff *skb,
+ struct net_flow_action **acts)
+{
+ struct nlattr *actions;
+ int i, err;
+
+ actions = nla_nest_start(skb, NFL_ACTIONS);
+ if (!actions)
+ return -EMSGSIZE;
+
+ for (i = 0; acts[i]; i++) {
+ struct nlattr *action = nla_nest_start(skb, NFL_ACTION);
+
+ if (!action)
+ goto action_put_failure;
+
+ err = net_flow_put_action(skb, acts[i]);
+ if (err)
+ goto action_put_failure;
+ nla_nest_end(skb, action);
+ }
+ nla_nest_end(skb, actions);
+
+ return 0;
+action_put_failure:
+ nla_nest_cancel(skb, actions);
+ return -EMSGSIZE;
+}
+
+static struct sk_buff *net_flow_build_actions_msg(struct net_flow_action **a,
+ struct net_device *dev,
+ u32 portid, int seq, u8 cmd)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOBUFS);
+
+ hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(skb,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(skb, NFL_IDENTIFIER, dev->ifindex)) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ err = net_flow_put_actions(skb, a);
+ if (err < 0)
+ goto out;
+
+ err = genlmsg_end(skb, hdr);
+ if (err < 0)
+ goto out;
+
+ return skb;
+out:
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+}
+
+static int net_flow_cmd_get_actions(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net_flow_action **a;
+ struct net_device *dev;
+ struct sk_buff *msg;
+
+ dev = net_flow_get_dev(info);
+ if (!dev)
+ return -EINVAL;
+
+ if (!dev->netdev_ops->ndo_flow_get_actions) {
+ dev_put(dev);
+ return -EOPNOTSUPP;
+ }
+
+ a = dev->netdev_ops->ndo_flow_get_actions(dev);
+ if (!a) {
+ dev_put(dev);
+ return -EBUSY;
+ }
+
+ msg = net_flow_build_actions_msg(a, dev,
+ info->snd_portid,
+ info->snd_seq,
+ NFL_TABLE_CMD_GET_ACTIONS);
+ dev_put(dev);
+
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ return genlmsg_reply(msg, info);
+}
+
+static int net_flow_put_field_ref(struct sk_buff *skb,
+ struct net_flow_field_ref *ref)
+{
+ if (nla_put_u32(skb, NFL_FIELD_REF_INSTANCE, ref->instance) ||
+ nla_put_u32(skb, NFL_FIELD_REF_HEADER, ref->header) ||
+ nla_put_u32(skb, NFL_FIELD_REF_FIELD, ref->field) ||
+ nla_put_u32(skb, NFL_FIELD_REF_MASK_TYPE, ref->mask_type) ||
+ nla_put_u32(skb, NFL_FIELD_REF_TYPE, ref->type))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int net_flow_put_field_value(struct sk_buff *skb,
+ struct net_flow_field_ref *r)
+{
+ int err = -EINVAL;
+
+ switch (r->type) {
+ case NFL_FIELD_REF_ATTR_TYPE_UNSPEC:
+ err = 0;
+ break;
+ case NFL_FIELD_REF_ATTR_TYPE_U8:
+ err = nla_put_u8(skb, NFL_FIELD_REF_VALUE, r->value_u8);
+ if (err)
+ break;
+ err = nla_put_u8(skb, NFL_FIELD_REF_MASK, r->mask_u8);
+ break;
+ case NFL_FIELD_REF_ATTR_TYPE_U16:
+ err = nla_put_u16(skb, NFL_FIELD_REF_VALUE, r->value_u16);
+ if (err)
+ break;
+ err = nla_put_u16(skb, NFL_FIELD_REF_MASK, r->mask_u16);
+ break;
+ case NFL_FIELD_REF_ATTR_TYPE_U32:
+ err = nla_put_u32(skb, NFL_FIELD_REF_VALUE, r->value_u32);
+ if (err)
+ break;
+ err = nla_put_u32(skb, NFL_FIELD_REF_MASK, r->mask_u32);
+ break;
+ case NFL_FIELD_REF_ATTR_TYPE_U64:
+ err = nla_put_u64(skb, NFL_FIELD_REF_VALUE, r->value_u64);
+ if (err)
+ break;
+ err = nla_put_u64(skb, NFL_FIELD_REF_MASK, r->mask_u64);
+ break;
+ default:
+ break;
+ }
+ return err;
+}
+
+static int net_flow_put_table(struct net_device *dev,
+ struct sk_buff *skb,
+ struct net_flow_tbl *t)
+{
+ struct nlattr *matches, *actions, *field;
+ int i, err;
+
+ if (nla_put_string(skb, NFL_TABLE_ATTR_NAME, t->name) ||
+ nla_put_u32(skb, NFL_TABLE_ATTR_UID, t->uid) ||
+ nla_put_u32(skb, NFL_TABLE_ATTR_SOURCE, t->source) ||
+ nla_put_u32(skb, NFL_TABLE_ATTR_APPLY, t->apply_action) ||
+ nla_put_u32(skb, NFL_TABLE_ATTR_SIZE, t->size))
+ return -EMSGSIZE;
+
+ matches = nla_nest_start(skb, NFL_TABLE_ATTR_MATCHES);
+ if (!matches)
+ return -EMSGSIZE;
+
+ for (i = 0; t->matches[i].instance; i++) {
+ field = nla_nest_start(skb, NFL_FIELD_REF);
+
+ err = net_flow_put_field_ref(skb, &t->matches[i]);
+ if (err) {
+ nla_nest_cancel(skb, matches);
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, field);
+ }
+ nla_nest_end(skb, matches);
+
+ actions = nla_nest_start(skb, NFL_TABLE_ATTR_ACTIONS);
+ if (!actions)
+ return -EMSGSIZE;
+
+ for (i = 0; t->actions[i]; i++) {
+ if (nla_put_u32(skb,
+ NFL_ACTION_ATTR_UID,
+ t->actions[i])) {
+ nla_nest_cancel(skb, actions);
+ return -EMSGSIZE;
+ }
+ }
+ nla_nest_end(skb, actions);
+
+ return 0;
+}
+
+static int net_flow_put_tables(struct net_device *dev,
+ struct sk_buff *skb,
+ struct net_flow_tbl **tables)
+{
+ struct nlattr *nest, *t;
+ int i, err = 0;
+
+ nest = nla_nest_start(skb, NFL_TABLES);
+ if (!nest)
+ return -EMSGSIZE;
+
+ for (i = 0; tables[i]; i++) {
+ t = nla_nest_start(skb, NFL_TABLE);
+ if (!t) {
+ err = -EMSGSIZE;
+ goto errout;
+ }
+
+ err = net_flow_put_table(dev, skb, tables[i]);
+ if (err) {
+ nla_nest_cancel(skb, t);
+ goto errout;
+ }
+ nla_nest_end(skb, t);
+ }
+ nla_nest_end(skb, nest);
+ return 0;
+errout:
+ nla_nest_cancel(skb, nest);
+ return err;
+}
+
+static struct sk_buff *net_flow_build_tables_msg(struct net_flow_tbl **t,
+ struct net_device *dev,
+ u32 portid, int seq, u8 cmd)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOBUFS);
+
+ hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(skb,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(skb, NFL_IDENTIFIER, dev->ifindex)) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ err = net_flow_put_tables(dev, skb, t);
+ if (err < 0)
+ goto out;
+
+ err = genlmsg_end(skb, hdr);
+ if (err < 0)
+ goto out;
+
+ return skb;
+out:
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+}
+
+static int net_flow_cmd_get_tables(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net_flow_tbl **tables;
+ struct net_device *dev;
+ struct sk_buff *msg;
+
+ dev = net_flow_get_dev(info);
+ if (!dev)
+ return -EINVAL;
+
+ if (!dev->netdev_ops->ndo_flow_get_tbls) {
+ dev_put(dev);
+ return -EOPNOTSUPP;
+ }
+
+ tables = dev->netdev_ops->ndo_flow_get_tbls(dev);
+ if (!tables) {
+ dev_put(dev);
+ return -EBUSY;
+ }
+
+ msg = net_flow_build_tables_msg(tables, dev,
+ info->snd_portid,
+ info->snd_seq,
+ NFL_TABLE_CMD_GET_TABLES);
+ dev_put(dev);
+
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ return genlmsg_reply(msg, info);
+}
+
+static
+int net_flow_put_fields(struct sk_buff *skb, const struct net_flow_hdr *h)
+{
+ struct net_flow_field *f;
+ int count = h->field_sz;
+ struct nlattr *field;
+
+ for (f = h->fields; count; count--, f++) {
+ field = nla_nest_start(skb, NFL_FIELD);
+ if (!field)
+ goto field_put_failure;
+
+ if (nla_put_string(skb, NFL_FIELD_ATTR_NAME, f->name) ||
+ nla_put_u32(skb, NFL_FIELD_ATTR_UID, f->uid) ||
+ nla_put_u32(skb, NFL_FIELD_ATTR_BITWIDTH, f->bitwidth))
+ goto out;
+
+ nla_nest_end(skb, field);
+ }
+
+ return 0;
+out:
+ nla_nest_cancel(skb, field);
+field_put_failure:
+ return -EMSGSIZE;
+}
+
+static int net_flow_put_headers(struct sk_buff *skb,
+ struct net_flow_hdr **headers)
+{
+ struct nlattr *nest, *hdr, *fields;
+ struct net_flow_hdr *h;
+ int i, err;
+
+ nest = nla_nest_start(skb, NFL_HEADERS);
+ if (!nest)
+ return -EMSGSIZE;
+
+ for (i = 0; headers[i]; i++) {
+ err = -EMSGSIZE;
+ h = headers[i];
+
+ hdr = nla_nest_start(skb, NFL_HEADER);
+ if (!hdr)
+ goto put_failure;
+
+ if (nla_put_string(skb, NFL_HEADER_ATTR_NAME, h->name) ||
+ nla_put_u32(skb, NFL_HEADER_ATTR_UID, h->uid))
+ goto put_failure;
+
+ fields = nla_nest_start(skb, NFL_HEADER_ATTR_FIELDS);
+ if (!fields)
+ goto put_failure;
+
+ err = net_flow_put_fields(skb, h);
+ if (err)
+ goto put_failure;
+
+ nla_nest_end(skb, fields);
+
+ nla_nest_end(skb, hdr);
+ }
+ nla_nest_end(skb, nest);
+
+ return 0;
+put_failure:
+ nla_nest_cancel(skb, nest);
+ return err;
+}
+
+static struct sk_buff *net_flow_build_headers_msg(struct net_flow_hdr **h,
+ struct net_device *dev,
+ u32 portid, int seq, u8 cmd)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOBUFS);
+
+ hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(skb,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(skb, NFL_IDENTIFIER, dev->ifindex)) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ err = net_flow_put_headers(skb, h);
+ if (err < 0)
+ goto out;
+
+ err = genlmsg_end(skb, hdr);
+ if (err < 0)
+ goto out;
+
+ return skb;
+out:
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+}
+
+static int net_flow_cmd_get_headers(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net_flow_hdr **h;
+ struct net_device *dev;
+ struct sk_buff *msg;
+
+ dev = net_flow_get_dev(info);
+ if (!dev)
+ return -EINVAL;
+
+ if (!dev->netdev_ops->ndo_flow_get_hdrs) {
+ dev_put(dev);
+ return -EOPNOTSUPP;
+ }
+
+ h = dev->netdev_ops->ndo_flow_get_hdrs(dev);
+ if (!h) {
+ dev_put(dev);
+ return -EBUSY;
+ }
+
+ msg = net_flow_build_headers_msg(h, dev,
+ info->snd_portid,
+ info->snd_seq,
+ NFL_TABLE_CMD_GET_HEADERS);
+ dev_put(dev);
+
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ return genlmsg_reply(msg, info);
+}
+
+static int net_flow_put_header_node(struct sk_buff *skb,
+ struct net_flow_hdr_node *node)
+{
+ struct nlattr *hdrs, *jumps;
+ int i, err;
+
+ if (nla_put_string(skb, NFL_HEADER_NODE_NAME, node->name) ||
+ nla_put_u32(skb, NFL_HEADER_NODE_UID, node->uid))
+ return -EMSGSIZE;
+
+ /* Insert the set of headers that get extracted at this node */
+ hdrs = nla_nest_start(skb, NFL_HEADER_NODE_HDRS);
+ if (!hdrs)
+ return -EMSGSIZE;
+ for (i = 0; node->hdrs[i]; i++) {
+ if (nla_put_u32(skb, NFL_HEADER_NODE_HDRS_VALUE,
+ node->hdrs[i])) {
+ nla_nest_cancel(skb, hdrs);
+ return -EMSGSIZE;
+ }
+ }
+ nla_nest_end(skb, hdrs);
+
+ /* Then give the jump table to find next header node in graph */
+ jumps = nla_nest_start(skb, NFL_HEADER_NODE_JUMP);
+ if (!jumps)
+ return -EMSGSIZE;
+
+ for (i = 0; node->jump[i].node; i++) {
+ struct nlattr *entry;
+
+ entry = nla_nest_start(skb, NFL_JUMP_ENTRY);
+ if (!entry) {
+ nla_nest_cancel(skb, jumps);
+ return -EMSGSIZE;
+ }
+
+ err = nla_put_u32(skb, NFL_FIELD_REF_NEXT_NODE,
+ node->jump[i].node);
+ if (err) {
+ nla_nest_cancel(skb, jumps);
+ return err;
+ }
+
+ err = net_flow_put_field_ref(skb, &node->jump[i].field);
+ if (err) {
+ nla_nest_cancel(skb, jumps);
+ return err;
+ }
+
+ err = net_flow_put_field_value(skb, &node->jump[i].field);
+ if (err) {
+ nla_nest_cancel(skb, jumps);
+ return err;
+ }
+ nla_nest_end(skb, entry);
+ }
+ nla_nest_end(skb, jumps);
+
+ return 0;
+}
+
+static int net_flow_put_header_graph(struct sk_buff *skb,
+ struct net_flow_hdr_node **g)
+{
+ struct nlattr *nodes, *node;
+ int i, err;
+
+ nodes = nla_nest_start(skb, NFL_HEADER_GRAPH);
+ if (!nodes)
+ return -EMSGSIZE;
+
+ for (i = 0; g[i]; i++) {
+ node = nla_nest_start(skb, NFL_HEADER_GRAPH_NODE);
+ if (!node) {
+ err = -EMSGSIZE;
+ goto nodes_put_error;
+ }
+
+ err = net_flow_put_header_node(skb, g[i]);
+ if (err)
+ goto nodes_put_error;
+
+ nla_nest_end(skb, node);
+ }
+
+ nla_nest_end(skb, nodes);
+ return 0;
+nodes_put_error:
+ nla_nest_cancel(skb, nodes);
+ return err;
+}
+
+static
+struct sk_buff *net_flow_build_header_graph_msg(struct net_flow_hdr_node **g,
+ struct net_device *dev,
+ u32 portid, int seq, u8 cmd)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOBUFS);
+
+ hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(skb,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(skb, NFL_IDENTIFIER, dev->ifindex)) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ err = net_flow_put_header_graph(skb, g);
+ if (err < 0)
+ goto out;
+
+ err = genlmsg_end(skb, hdr);
+ if (err < 0)
+ goto out;
+
+ return skb;
+out:
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+}
+
+static int net_flow_cmd_get_header_graph(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net_flow_hdr_node **h;
+ struct net_device *dev;
+ struct sk_buff *msg;
+
+ dev = net_flow_get_dev(info);
+ if (!dev)
+ return -EINVAL;
+
+ if (!dev->netdev_ops->ndo_flow_get_hdr_graph) {
+ dev_put(dev);
+ return -EOPNOTSUPP;
+ }
+
+ h = dev->netdev_ops->ndo_flow_get_hdr_graph(dev);
+ if (!h) {
+ dev_put(dev);
+ return -EBUSY;
+ }
+
+ msg = net_flow_build_header_graph_msg(h, dev,
+ info->snd_portid,
+ info->snd_seq,
+ NFL_TABLE_CMD_GET_HDR_GRAPH);
+ dev_put(dev);
+
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ return genlmsg_reply(msg, info);
+}
+
+static int net_flow_put_table_node(struct sk_buff *skb,
+ struct net_flow_tbl_node *node)
+{
+ struct nlattr *nest, *jump;
+ int i, err = -EMSGSIZE;
+
+ nest = nla_nest_start(skb, NFL_TABLE_GRAPH_NODE);
+ if (!nest)
+ return err;
+
+ if (nla_put_u32(skb, NFL_TABLE_GRAPH_NODE_UID, node->uid) ||
+ nla_put_u32(skb, NFL_TABLE_GRAPH_NODE_FLAGS, node->flags))
+ goto node_put_failure;
+
+ jump = nla_nest_start(skb, NFL_TABLE_GRAPH_NODE_JUMP);
+ if (!jump)
+ goto node_put_failure;
+
+ for (i = 0; node->jump[i].node; i++) {
+ struct nlattr *entry;
+
+ entry = nla_nest_start(skb, NFL_JUMP_ENTRY);
+ if (!entry)
+ goto node_put_failure;
+
+ err = nla_put_u32(skb, NFL_FIELD_REF_NEXT_NODE,
+ node->jump[i].node);
+ if (err) {
+ nla_nest_cancel(skb, jump);
+ return err;
+ }
+
+ err = net_flow_put_field_ref(skb, &node->jump[i].field);
+ if (err)
+ goto node_put_failure;
+
+ err = net_flow_put_field_value(skb, &node->jump[i].field);
+ if (err)
+ goto node_put_failure;
+
+ nla_nest_end(skb, entry);
+ }
+
+ nla_nest_end(skb, jump);
+ nla_nest_end(skb, nest);
+ return 0;
+node_put_failure:
+ nla_nest_cancel(skb, nest);
+ return err;
+}
+
+static int net_flow_put_table_graph(struct sk_buff *skb,
+ struct net_flow_tbl_node **nodes)
+{
+ struct nlattr *graph;
+ int i, err;
+
+ graph = nla_nest_start(skb, NFL_TABLE_GRAPH);
+ if (!graph)
+ return -EMSGSIZE;
+
+ for (i = 0; nodes[i]; i++) {
+ err = net_flow_put_table_node(skb, nodes[i]);
+ if (err) {
+ nla_nest_cancel(skb, graph);
+ return -EMSGSIZE;
+ }
+ }
+
+ nla_nest_end(skb, graph);
+ return 0;
+}
+
+static
+struct sk_buff *net_flow_build_graph_msg(struct net_flow_tbl_node **g,
+ struct net_device *dev,
+ u32 portid, int seq, u8 cmd)
+{
+ struct genlmsghdr *hdr;
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOBUFS);
+
+ hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(skb,
+ NFL_IDENTIFIER_TYPE,
+ NFL_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(skb, NFL_IDENTIFIER, dev->ifindex)) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ err = net_flow_put_table_graph(skb, g);
+ if (err < 0)
+ goto out;
+
+ err = genlmsg_end(skb, hdr);
+ if (err < 0)
+ goto out;
+
+ return skb;
+out:
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+}
+
+static int net_flow_cmd_get_table_graph(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net_flow_tbl_node **g;
+ struct net_device *dev;
+ struct sk_buff *msg;
+
+ dev = net_flow_get_dev(info);
+ if (!dev)
+ return -EINVAL;
+
+ if (!dev->netdev_ops->ndo_flow_get_tbl_graph) {
+ dev_put(dev);
+ return -EOPNOTSUPP;
+ }
+
+ g = dev->netdev_ops->ndo_flow_get_tbl_graph(dev);
+ if (!g) {
+ dev_put(dev);
+ return -EBUSY;
+ }
+
+ msg = net_flow_build_graph_msg(g, dev,
+ info->snd_portid,
+ info->snd_seq,
+ NFL_TABLE_CMD_GET_TABLE_GRAPH);
+ dev_put(dev);
+
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ return genlmsg_reply(msg, info);
+}
+
+static const struct nla_policy net_flow_cmd_policy[NFL_MAX + 1] = {
+ [NFL_IDENTIFIER_TYPE] = {.type = NLA_U32, },
+ [NFL_IDENTIFIER] = {.type = NLA_U32, },
+ [NFL_TABLES] = {.type = NLA_NESTED, },
+ [NFL_HEADERS] = {.type = NLA_NESTED, },
+ [NFL_ACTIONS] = {.type = NLA_NESTED, },
+ [NFL_HEADER_GRAPH] = {.type = NLA_NESTED, },
+ [NFL_TABLE_GRAPH] = {.type = NLA_NESTED, },
+};
+
+static const struct genl_ops net_flow_table_nl_ops[] = {
+ {
+ .cmd = NFL_TABLE_CMD_GET_TABLES,
+ .doit = net_flow_cmd_get_tables,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NFL_TABLE_CMD_GET_HEADERS,
+ .doit = net_flow_cmd_get_headers,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NFL_TABLE_CMD_GET_ACTIONS,
+ .doit = net_flow_cmd_get_actions,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NFL_TABLE_CMD_GET_HDR_GRAPH,
+ .doit = net_flow_cmd_get_header_graph,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NFL_TABLE_CMD_GET_TABLE_GRAPH,
+ .doit = net_flow_cmd_get_table_graph,
+ .policy = net_flow_cmd_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
+
+static int __init net_flow_nl_module_init(void)
+{
+ return genl_register_family_with_ops(&net_flow_nl_family,
+ net_flow_table_nl_ops);
+}
+
+static void net_flow_nl_module_fini(void)
+{
+ genl_unregister_family(&net_flow_nl_family);
+}
+
+module_init(net_flow_nl_module_init);
+module_exit(net_flow_nl_module_fini);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("John Fastabend <john.r.fastabend@intel.com>");
+MODULE_DESCRIPTION("Netlink interface to Flow Tables (Net Flow Netlink)");
+MODULE_ALIAS_GENL_FAMILY(NFL_GENL_NAME);
^ permalink raw reply related
* [net-next PATCH v2 00/12] Flow API
From: John Fastabend @ 2015-01-13 21:35 UTC (permalink / raw)
To: tgraf, simon.horman, sfeldma; +Cc: netdev, gerlitz.or, jhs, andy, davem
I tried to roll in all the feedback from v1 into this series annotated
here,
- Use a software rhashtable to store add/del flows so we can skip
having to interrogate drivers for get_flow requests.
- Removed structures from UAPI this should make it easier to evolve
as needed.
- Added net_flow_lock around set/del rule ops.
- Alexei Starovoitov suggested renaming NET_FLOW -> NFL for
brevity/clarity. Seems reasonable to me so went ahead and changed
the UAPI enums. Also renamed flow types and calls to *_rule. Core
flow_table still using net_flow_* prefix.
- various fixes/suggestion from Simon Horman, Jiri Pirko, Scot
Feldman, Thomas Graf, et. al.
* SimonH: sent patch series of fixes to netdev
* JiriP: some naming issues, some helper funcs added, etc.
* ScottF: use ARRAY_SIZE, let compiler define array sizes, use
ETH_P_* macros. Various fixes.
* ThomasG: various suggestions
- fixed a few cases to catch invalid messages from user space
and dev_put errors.
Thanks for the initial feedback here is a v2 to take a look at I
hope I addressed all the comments so far except for the integrate
with 'tc'. I plan to work on the integration pieces next.
---
This set creates a new netlink family and set of messages to configure
flow tables in hardware. I tried to make the commit messages
reasonably verbose at least in the flow_table patches possibly too
verbose.
What we get at the end of this series is a working API to get device
capabilities and program flows using the rocker switch.
I created a user space tool 'flow' that I use to configure and query
the devices it is posted here,
https://github.com/jrfastab/iprotue2-flow-tool
For now it is a stand-alone tool but once the kernel bits get sorted
out I would like to port it into the iproute2 package. This way we
can keep all of our tooling in one package.
As far as testing, I've tested various combinations of tables and
rules on the rocker switch and it seems to work.
I could use some help reviewing,
(a) error paths and netlink validation code paths
(b) are there any devices that have pipelines that we
can't represent with this API? It would be good to
know about these so we can design it in probably
in a future series.
For some examples and maybe a bit more illustrative description I
posted a quickly typed up set of notes on github io pages.
http://jrfastab.github.io/jekyll/update/2014/12/21/flow-api.html
After this initial work to expose the API is complete the next task
is to integrate with existing subsystems 'tc' and OVS for example.
Thanks! Any comments/feedback always welcome.
And also thanks to everyone who helped with this flow API so
far. All the folks at Dusseldorf LPC, OVS summit Santa Clara, P4
authors for some inspiration, the collection of IETF FoRCES
documents I mulled over, Netfilter workshop where I started
to realize fixing ethtool was most likely not going to work,
etc.
---
John Fastabend (12):
net: flow_table: create interface for hw match/action tables
net: flow_table: add flow, delete flow
net: flow: implement flow cache for get routines
net: flow_table: create a set of common headers and actions
net: flow_table: add validation functions for flows
net: rocker: add pipeline model for rocker switch
net: rocker: add set flow rules
net: rocker: add group_id slices and drop explicit goto
net: rocker: add multicast path to bridging
net: rocker: add cookie to group acls and use flow_id to set cookie
net: rocker: have flow api calls set cookie value
net: rocker: implement delete flow routine
drivers/net/ethernet/rocker/rocker.c | 757 ++++++++++
drivers/net/ethernet/rocker/rocker_pipeline.h | 595 ++++++++
include/linux/if_flow.h | 228 +++
include/linux/if_flow_common.h | 257 +++
include/linux/netdevice.h | 48 +
include/uapi/linux/if_flow.h | 440 ++++++
net/Kconfig | 7
net/core/Makefile | 1
net/core/flow_table.c | 1909 +++++++++++++++++++++++++
9 files changed, 4225 insertions(+), 17 deletions(-)
create mode 100644 drivers/net/ethernet/rocker/rocker_pipeline.h
create mode 100644 include/linux/if_flow.h
create mode 100644 include/linux/if_flow_common.h
create mode 100644 include/uapi/linux/if_flow.h
create mode 100644 net/core/flow_table.c
--
Signature
^ permalink raw reply
* Re: [PATCH] bridge: only provide proxy ARP when CONFIG_INET is enabled
From: Arnd Bergmann @ 2015-01-13 21:33 UTC (permalink / raw)
To: David Ahern; +Cc: David Miller, cwang, netdev, kyeyoonp, bridge, stephen
In-Reply-To: <54B58AAC.7090001@gmail.com>
On Tuesday 13 January 2015 14:14:20 David Ahern wrote:
>
> Rather than connect CONFIG_BRIDGE to CONFIG_INET, why not make
> br_do_proxy_arp (and setting BR_PROXYARP flag) a no-op if CONFIG_INET is
> not set?
>
> #ifdef CONFIG_INET
> #else
> static inline void br_do_proxy_arp(...args...)
> {
> }
> #endif
>
> That covers both arp_tbl and arp_send.
The effect is very similar to my patch (probably same object code), the
only difference should be that it would add an ugly #ifdef instead of
the preferred IS_ENABLED() check, so you don't get any compile-time
coverage of the function. It's not really important because everybody
has CONFIG_INET enabled in practice and it does get more than enough
compile-time coverage.
Arnd
^ permalink raw reply
* Re: [PATCH net-next] ipv6: directly include libc-compat.h in ipv6.h
From: David Miller @ 2015-01-13 21:33 UTC (permalink / raw)
To: willemb; +Cc: netdev, xiyou.wangcong
In-Reply-To: <1421090974-30018-1-git-send-email-willemb@google.com>
From: Willem de Bruijn <willemb@google.com>
Date: Mon, 12 Jan 2015 14:29:34 -0500
> From: Willem de Bruijn <willemb@google.com>
>
> Patch 3b50d9029809 ("ipv6: fix redefinition of in6_pktinfo ...")
> fixed a libc compatibility issue in ipv6 structure definitions
> as described in include/uapi/linux/libc-compat.h.
>
> It relies on including linux/in6.h to include libc-compat.h itself.
> Include that file directly to clearly communicate the dependency
> (libc-compat.h: "This include must be as early as possible").
>
> Signed-off-by: Willem de Bruijn <willemb@google.com>
Applied.
^ permalink raw reply
* Re: [PATCH net] cxgb4vf: Initialize mdio_addr before using it
From: David Miller @ 2015-01-13 21:32 UTC (permalink / raw)
To: hariprasad; +Cc: netdev, leedom, nirranjan, kumaras
In-Reply-To: <1421080576-30885-1-git-send-email-hariprasad@chelsio.com>
From: Hariprasad Shenai <hariprasad@chelsio.com>
Date: Mon, 12 Jan 2015 22:06:16 +0530
> In commit 5ad24def21b205a8 ("cxgb4vf: Fix ethtool get_settings for VF driver")
> mdio_addr of port_info structure was used unininitialzed. Fixing it.
>
> Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
Applied.
^ permalink raw reply
* Re: [PATCH] bridge: only provide proxy ARP when CONFIG_INET is enabled
From: David Ahern @ 2015-01-13 21:14 UTC (permalink / raw)
To: David Miller, cwang, arnd; +Cc: netdev, kyeyoonp, bridge, stephen
In-Reply-To: <20150113.155740.1237959632603319909.davem@davemloft.net>
On 1/13/15 1:57 PM, David Miller wrote:
> From: Cong Wang <cwang@twopensource.com>
> Date: Tue, 13 Jan 2015 11:25:45 -0800
>
>> On Tue, Jan 13, 2015 at 6:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>>> When IPV4 support is disabled, we cannot call arp_send from
>>> the bridge code, which would result in a kernel link error:
>>>
>>> net/built-in.o: In function `br_handle_frame_finish':
>>> :(.text+0x59914): undefined reference to `arp_send'
>>> :(.text+0x59a50): undefined reference to `arp_tbl'
>>>
>>> This makes the newly added proxy ARP support in the bridge
>>> code depend on the CONFIG_INET symbol and lets the compiler
>>> optimize the code out to avoid the link error.
>>>
>>
>> Not sure how much sense to make CONFIG_BRIDGE depend
>> on CONFIG_INET, at least CONFIG_BONDING does.
>
> It depends upon whether we want to provide and consider
> as a valid configuration bridging without INET. Probably
> we do.
Rather than connect CONFIG_BRIDGE to CONFIG_INET, why not make
br_do_proxy_arp (and setting BR_PROXYARP flag) a no-op if CONFIG_INET is
not set?
#ifdef CONFIG_INET
#else
static inline void br_do_proxy_arp(...args...)
{
}
#endif
That covers both arp_tbl and arp_send.
David
^ permalink raw reply
* [PATCH] tcp: Fix RFC reference in comment
From: Debabrata Banerjee @ 2015-01-13 21:10 UTC (permalink / raw)
To: davem, netdev; +Cc: linux-kernel, Debabrata Banerjee
Comment in tcp_cwnd_restart() was referencing the wrong RFC for the algorithm
it's implementing.
Signed-off-by: Debabrata Banerjee <dbanerje@akamai.com>
---
net/ipv4/tcp_output.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 65caf8b..0c13f88 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -139,7 +139,7 @@ static __u16 tcp_advertise_mss(struct sock *sk)
return (__u16)mss;
}
-/* RFC2861. Reset CWND after idle period longer RTO to "restart window".
+/* RFC2581 4.1. Reset CWND after idle period longer RTO to "restart window".
* This is the first part of cwnd validation mechanism. */
static void tcp_cwnd_restart(struct sock *sk, const struct dst_entry *dst)
{
--
2.2.1
^ permalink raw reply related
* Re: [net-next v2 00/15][pull request] Intel Wired LAN Driver Updates 2015-01-13
From: David Miller @ 2015-01-13 21:06 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, nhorman, sassmann, jogreene
In-Reply-To: <1421179093-10932-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Tue, 13 Jan 2015 11:57:58 -0800
> This series contains updates to i40e and i40evf.
...
> v2: remove un-needed {} in patch #3 of the series based on feedback from
> Sergei Shtylyov
>
> The following are changes since commit 52e3ad9f011fe72620b2f7050227cd48fd295ad5:
> Merge branch 'rhashtable-next'
> and are available in the git repository at:
> git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next master
Pulled, thanks Jeff.
^ permalink raw reply
* Re: [PATCH 3/6] net: davinci_emac: Free clock after checking the frequency
From: David Miller @ 2015-01-13 21:05 UTC (permalink / raw)
To: tony; +Cc: thomas.lendacky, netdev, linux-omap, b.hutchman, balbi
In-Reply-To: <20150113195415.GG2419@atomide.com>
From: Tony Lindgren <tony@atomide.com>
Date: Tue, 13 Jan 2015 11:54:16 -0800
> * Tom Lendacky <thomas.lendacky@amd.com> [150113 11:51]:
>> On 01/13/2015 01:29 PM, Tony Lindgren wrote:
>> >We only use clk_get() to get the frequency, the rest is done by
>> >the runtime PM calls. Let's free the clock too.
>> >
>> >Cc: Brian Hutchinson <b.hutchman@gmail.com>
>> >Cc: Felipe Balbi <balbi@ti.com>
>> >Signed-off-by: Tony Lindgren <tony@atomide.com>
>> >---
>> > drivers/net/ethernet/ti/davinci_emac.c | 1 +
>> > 1 file changed, 1 insertion(+)
>> >
>> >diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
>> >index deb43b3..e9efc74 100644
>> >--- a/drivers/net/ethernet/ti/davinci_emac.c
>> >+++ b/drivers/net/ethernet/ti/davinci_emac.c
>> >@@ -1881,6 +1881,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
>> > return -EBUSY;
>> > }
>> > emac_bus_frequency = clk_get_rate(emac_clk);
>> >+ clk_put(emac_clk);
>>
>> The devm_clk_get call is used to get the clock so either a devm_clk_put
>> needs to be used here or just let the devm_ call do its thing and
>> automatically do the put when the module is unloaded.
>
> Thanks good catch, updated patch below.
Please, once all the feedback has been addressed, repost the entire
series.
THanks.
^ permalink raw reply
* Re: [PATCH 2/6] net: davinci_emac: Fix runtime pm calls for davinci_emac
From: Felipe Balbi @ 2015-01-13 21:03 UTC (permalink / raw)
To: Tony Lindgren
Cc: Felipe Balbi, David Miller, netdev, linux-omap, Brian Hutchinson,
Mark A. Greer
In-Reply-To: <20150113205439.GK2419@atomide.com>
[-- Attachment #1: Type: text/plain, Size: 4851 bytes --]
On Tue, Jan 13, 2015 at 12:54:40PM -0800, Tony Lindgren wrote:
> * Felipe Balbi <balbi@ti.com> [150113 11:55]:
> > On Tue, Jan 13, 2015 at 11:29:24AM -0800, Tony Lindgren wrote:
> > > --- a/drivers/net/ethernet/ti/davinci_emac.c
> > > +++ b/drivers/net/ethernet/ti/davinci_emac.c
> > > @@ -1538,7 +1538,7 @@ static int emac_dev_open(struct net_device *ndev)
> > > int i = 0;
> > > struct emac_priv *priv = netdev_priv(ndev);
> > >
> > > - pm_runtime_get(&priv->pdev->dev);
> > > + pm_runtime_get_sync(&priv->pdev->dev);
> >
> > gotta check return value on all pm_runtime_get_sync() calls. IIRC,
> > there's a coccinelle script for checking and patching this.
>
> Sure, here's an updated patch with error checking added.
>
> Regards,
>
> Tony
>
> 8< ---------------------
> From: Tony Lindgren <tony@atomide.com>
> Date: Mon, 22 Dec 2014 08:19:06 -0800
> Subject: [PATCH] net: davinci_emac: Fix runtime pm calls for davinci_emac
>
> Commit 3ba97381343b ("net: ethernet: davinci_emac: add pm_runtime support")
> added support for runtime PM, but it causes issues on omap3 related devices
> that actually gate the clocks:
>
> Unhandled fault: external abort on non-linefetch (0x1008)
> ...
> [<c04160f0>] (emac_dev_getnetstats) from [<c04d6a3c>] (dev_get_stats+0x78/0xc8)
> [<c04d6a3c>] (dev_get_stats) from [<c04e9ccc>] (rtnl_fill_ifinfo+0x3b8/0x938)
> [<c04e9ccc>] (rtnl_fill_ifinfo) from [<c04eade4>] (rtmsg_ifinfo+0x68/0xd8)
> [<c04eade4>] (rtmsg_ifinfo) from [<c04dd35c>] (register_netdevice+0x3a0/0x4ec)
> [<c04dd35c>] (register_netdevice) from [<c04dd4bc>] (register_netdev+0x14/0x24)
> [<c04dd4bc>] (register_netdev) from [<c041755c>] (davinci_emac_probe+0x408/0x5c8)
> [<c041755c>] (davinci_emac_probe) from [<c0396d78>] (platform_drv_probe+0x48/0xa4)
>
> Let's fix it by moving the pm_runtime_get() call earlier, and also add it to
> the emac_dev_getnetstats(). Also note that we want to use pm_runtime_get_sync()
> as we don't want to have deferred_resume happen. And let's also check the
> return value for pm_runtime_get_sync() as noted by Felipe Balbi <balbi@ti.com>.
>
> Cc: Brian Hutchinson <b.hutchman@gmail.com>
> Cc: Felipe Balbi <balbi@ti.com>
> Cc: Mark A. Greer <mgreer@animalcreek.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
>
> --- a/drivers/net/ethernet/ti/davinci_emac.c
> +++ b/drivers/net/ethernet/ti/davinci_emac.c
> @@ -1538,7 +1538,13 @@ static int emac_dev_open(struct net_device *ndev)
> int i = 0;
> struct emac_priv *priv = netdev_priv(ndev);
>
> - pm_runtime_get(&priv->pdev->dev);
> + ret = pm_runtime_get_sync(&priv->pdev->dev);
> + if (ret < 0) {
> + pm_runtime_put_noidle(&priv->pdev->dev);
> + dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n",
> + __func__, ret);
> + return ret;
> + }
>
> netif_carrier_off(ndev);
> for (cnt = 0; cnt < ETH_ALEN; cnt++)
> @@ -1725,6 +1731,15 @@ static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)
> struct emac_priv *priv = netdev_priv(ndev);
> u32 mac_control;
> u32 stats_clear_mask;
> + int err;
> +
> + err = pm_runtime_get_sync(&priv->pdev->dev);
> + if (err < 0) {
> + pm_runtime_put_noidle(&priv->pdev->dev);
> + dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n",
> + __func__, err);
> + return &ndev->stats;
> + }
>
> /* update emac hardware stats and reset the registers*/
>
> @@ -1767,6 +1782,8 @@ static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)
> ndev->stats.tx_fifo_errors += emac_read(EMAC_TXUNDERRUN);
> emac_write(EMAC_TXUNDERRUN, stats_clear_mask);
>
> + pm_runtime_put(&priv->pdev->dev);
> +
> return &ndev->stats;
> }
>
> @@ -1981,12 +1998,22 @@ static int davinci_emac_probe(struct platform_device *pdev)
> ndev->ethtool_ops = ðtool_ops;
> netif_napi_add(ndev, &priv->napi, emac_poll, EMAC_POLL_WEIGHT);
>
> + pm_runtime_enable(&pdev->dev);
> + rc = pm_runtime_get_sync(&pdev->dev);
> + if (rc < 0) {
> + pm_runtime_put_noidle(&pdev->dev);
> + dev_err(&pdev->dev, "%s: failed to get_sync(%d)\n",
> + __func__, rc);
> + goto no_cpdma_chan;
> + }
> +
> /* register the network device */
> SET_NETDEV_DEV(ndev, &pdev->dev);
> rc = register_netdev(ndev);
> if (rc) {
> dev_err(&pdev->dev, "error in register_netdev\n");
> rc = -ENODEV;
> + pm_runtime_put(&pdev->dev);
> goto no_cpdma_chan;
> }
>
> @@ -1996,9 +2023,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
> "(regs: %p, irq: %d)\n",
> (void *)priv->emac_base_phys, ndev->irq);
> }
> -
> - pm_runtime_enable(&pdev->dev);
> - pm_runtime_resume(&pdev->dev);
> + pm_runtime_put(&pdev->dev);
>
> return 0;
>
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH 2/6] net: davinci_emac: Fix runtime pm calls for davinci_emac
From: Tony Lindgren @ 2015-01-13 20:54 UTC (permalink / raw)
To: Felipe Balbi
Cc: David Miller, netdev, linux-omap, Brian Hutchinson, Mark A. Greer
In-Reply-To: <20150113195141.GT16533@saruman>
* Felipe Balbi <balbi@ti.com> [150113 11:55]:
> On Tue, Jan 13, 2015 at 11:29:24AM -0800, Tony Lindgren wrote:
> > --- a/drivers/net/ethernet/ti/davinci_emac.c
> > +++ b/drivers/net/ethernet/ti/davinci_emac.c
> > @@ -1538,7 +1538,7 @@ static int emac_dev_open(struct net_device *ndev)
> > int i = 0;
> > struct emac_priv *priv = netdev_priv(ndev);
> >
> > - pm_runtime_get(&priv->pdev->dev);
> > + pm_runtime_get_sync(&priv->pdev->dev);
>
> gotta check return value on all pm_runtime_get_sync() calls. IIRC,
> there's a coccinelle script for checking and patching this.
Sure, here's an updated patch with error checking added.
Regards,
Tony
8< ---------------------
From: Tony Lindgren <tony@atomide.com>
Date: Mon, 22 Dec 2014 08:19:06 -0800
Subject: [PATCH] net: davinci_emac: Fix runtime pm calls for davinci_emac
Commit 3ba97381343b ("net: ethernet: davinci_emac: add pm_runtime support")
added support for runtime PM, but it causes issues on omap3 related devices
that actually gate the clocks:
Unhandled fault: external abort on non-linefetch (0x1008)
...
[<c04160f0>] (emac_dev_getnetstats) from [<c04d6a3c>] (dev_get_stats+0x78/0xc8)
[<c04d6a3c>] (dev_get_stats) from [<c04e9ccc>] (rtnl_fill_ifinfo+0x3b8/0x938)
[<c04e9ccc>] (rtnl_fill_ifinfo) from [<c04eade4>] (rtmsg_ifinfo+0x68/0xd8)
[<c04eade4>] (rtmsg_ifinfo) from [<c04dd35c>] (register_netdevice+0x3a0/0x4ec)
[<c04dd35c>] (register_netdevice) from [<c04dd4bc>] (register_netdev+0x14/0x24)
[<c04dd4bc>] (register_netdev) from [<c041755c>] (davinci_emac_probe+0x408/0x5c8)
[<c041755c>] (davinci_emac_probe) from [<c0396d78>] (platform_drv_probe+0x48/0xa4)
Let's fix it by moving the pm_runtime_get() call earlier, and also add it to
the emac_dev_getnetstats(). Also note that we want to use pm_runtime_get_sync()
as we don't want to have deferred_resume happen. And let's also check the
return value for pm_runtime_get_sync() as noted by Felipe Balbi <balbi@ti.com>.
Cc: Brian Hutchinson <b.hutchman@gmail.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Mark A. Greer <mgreer@animalcreek.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
--- a/drivers/net/ethernet/ti/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -1538,7 +1538,13 @@ static int emac_dev_open(struct net_device *ndev)
int i = 0;
struct emac_priv *priv = netdev_priv(ndev);
- pm_runtime_get(&priv->pdev->dev);
+ ret = pm_runtime_get_sync(&priv->pdev->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&priv->pdev->dev);
+ dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n",
+ __func__, ret);
+ return ret;
+ }
netif_carrier_off(ndev);
for (cnt = 0; cnt < ETH_ALEN; cnt++)
@@ -1725,6 +1731,15 @@ static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)
struct emac_priv *priv = netdev_priv(ndev);
u32 mac_control;
u32 stats_clear_mask;
+ int err;
+
+ err = pm_runtime_get_sync(&priv->pdev->dev);
+ if (err < 0) {
+ pm_runtime_put_noidle(&priv->pdev->dev);
+ dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n",
+ __func__, err);
+ return &ndev->stats;
+ }
/* update emac hardware stats and reset the registers*/
@@ -1767,6 +1782,8 @@ static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)
ndev->stats.tx_fifo_errors += emac_read(EMAC_TXUNDERRUN);
emac_write(EMAC_TXUNDERRUN, stats_clear_mask);
+ pm_runtime_put(&priv->pdev->dev);
+
return &ndev->stats;
}
@@ -1981,12 +1998,22 @@ static int davinci_emac_probe(struct platform_device *pdev)
ndev->ethtool_ops = ðtool_ops;
netif_napi_add(ndev, &priv->napi, emac_poll, EMAC_POLL_WEIGHT);
+ pm_runtime_enable(&pdev->dev);
+ rc = pm_runtime_get_sync(&pdev->dev);
+ if (rc < 0) {
+ pm_runtime_put_noidle(&pdev->dev);
+ dev_err(&pdev->dev, "%s: failed to get_sync(%d)\n",
+ __func__, rc);
+ goto no_cpdma_chan;
+ }
+
/* register the network device */
SET_NETDEV_DEV(ndev, &pdev->dev);
rc = register_netdev(ndev);
if (rc) {
dev_err(&pdev->dev, "error in register_netdev\n");
rc = -ENODEV;
+ pm_runtime_put(&pdev->dev);
goto no_cpdma_chan;
}
@@ -1996,9 +2023,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
"(regs: %p, irq: %d)\n",
(void *)priv->emac_base_phys, ndev->irq);
}
-
- pm_runtime_enable(&pdev->dev);
- pm_runtime_resume(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
return 0;
^ permalink raw reply
* Re: [PATCH] bridge: only provide proxy ARP when CONFIG_INET is enabled
From: David Miller @ 2015-01-13 20:57 UTC (permalink / raw)
To: cwang; +Cc: netdev, kyeyoonp, bridge, arnd
In-Reply-To: <CAHA+R7NfZMRMJ2=pq-VYU_FRdcCheAYu9OW6Hj9fwB0mcT7Xng@mail.gmail.com>
From: Cong Wang <cwang@twopensource.com>
Date: Tue, 13 Jan 2015 11:25:45 -0800
> On Tue, Jan 13, 2015 at 6:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>> When IPV4 support is disabled, we cannot call arp_send from
>> the bridge code, which would result in a kernel link error:
>>
>> net/built-in.o: In function `br_handle_frame_finish':
>> :(.text+0x59914): undefined reference to `arp_send'
>> :(.text+0x59a50): undefined reference to `arp_tbl'
>>
>> This makes the newly added proxy ARP support in the bridge
>> code depend on the CONFIG_INET symbol and lets the compiler
>> optimize the code out to avoid the link error.
>>
>
> Not sure how much sense to make CONFIG_BRIDGE depend
> on CONFIG_INET, at least CONFIG_BONDING does.
It depends upon whether we want to provide and consider
as a valid configuration bridging without INET. Probably
we do.
^ permalink raw reply
* Re: [PATCH] mlx5: avoid build warnings on 32-bit
From: Arnd Bergmann @ 2015-01-13 20:25 UTC (permalink / raw)
To: Eli Cohen
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
linux-rdma-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20150113162803.GA7414@mtldesk30>
On Tuesday 13 January 2015 18:28:03 Eli Cohen wrote:
> On Tue, Jan 13, 2015 at 05:08:06PM +0100, Arnd Bergmann wrote:
>
> Hi Arnd,
> wouldn't it work by casting to uintptr_t instead of unsigned long?
>
These are the same on all architectures that Linux can run on,
but if you have a strong preference, I can send an updated
patch, the effect is exactly the same.
Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 5/6] net: davinci_emac: Fix ioremap for devices with MDIO within the EMAC address space
From: Felipe Balbi @ 2015-01-13 20:21 UTC (permalink / raw)
To: Tony Lindgren
Cc: Felipe Balbi, David Miller, netdev, linux-omap, Brian Hutchinson
In-Reply-To: <20150113195957.GH2419@atomide.com>
[-- Attachment #1: Type: text/plain, Size: 2175 bytes --]
On Tue, Jan 13, 2015 at 11:59:58AM -0800, Tony Lindgren wrote:
> * Felipe Balbi <balbi@ti.com> [150113 11:57]:
> > On Tue, Jan 13, 2015 at 11:29:27AM -0800, Tony Lindgren wrote:
> > > Some devices like dm816x have the MDIO registers within the first EMAC
> > > instance address space. Let's fix the issue by allowing to pass an
> > > optional second IO range for the EMAC control register area.
> > >
> > > Cc: Brian Hutchinson <b.hutchman@gmail.com>
> > > Cc: Felipe Balbi <balbi@ti.com>
> > > Signed-off-by: Tony Lindgren <tony@atomide.com>
> > > ---
> > > drivers/net/ethernet/ti/davinci_emac.c | 15 ++++++++++++---
> > > 1 file changed, 12 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
> > > index 4c8d82c..0342273 100644
> > > --- a/drivers/net/ethernet/ti/davinci_emac.c
> > > +++ b/drivers/net/ethernet/ti/davinci_emac.c
> > > @@ -1877,7 +1877,7 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv)
> > > static int davinci_emac_probe(struct platform_device *pdev)
> > > {
> > > int rc = 0;
> > > - struct resource *res;
> > > + struct resource *res, *res_ctrl;
> > > struct net_device *ndev;
> > > struct emac_priv *priv;
> > > unsigned long hw_ram_addr;
> > > @@ -1936,11 +1936,20 @@ static int davinci_emac_probe(struct platform_device *pdev)
> > > rc = PTR_ERR(priv->remap_addr);
> > > goto no_pdata;
> > > }
> > > +
> > > + res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > > + if (res_ctrl) {
> >
> > devm_ioremap_resource() will check for res_ctrl being a valid pointer,
> > perhaps below would be slightly better ?
> >
> >
> > res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > priv->ctrl_base = devm_ioremap_resource(&pdev->dev, res_ctrl);
> > if (IS_ERR(priv->ctrl_base))
> > priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset;
> >
>
> We have a pile of devices using just one ioremap area so the second
> ioremap area needs to be optional. That's why we only do it based on
> if (res_ctrl).
fair enough
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: BW regression after "tcp: refine TSO autosizing"
From: Or Gerlitz @ 2015-01-13 20:21 UTC (permalink / raw)
To: Eric Dumazet
Cc: Eyal Perry, Linux Netdev List, Amir Vadai, Yevgeny Petrilin,
Saeed Mahameed, Ido Shamay, Amir Ancel, Eyal Perry
In-Reply-To: <1421175434.4099.21.camel@edumazet-glaptop2.roam.corp.google.com>
On Tue, Jan 13, 2015 at 8:57 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> On Tue, 2015-01-13 at 18:48 +0200, Eyal Perry wrote:
>> Hello Eric,
>> Lately we've observed performance degradation in BW of about 30-40% (depends on
>> the setup we use).
>> I've bisected the issue down to the this commit: 605ad7f1 ("tcp: refine TSO
>> autosizing")
>>
>> For instance, I was running the following test:
>> 1. Bounding net device' irqs to core 0 for both client and server side
>> 2. Running netperf with 64K massage size (used the following command)
>> $ netperf -H remote -T 1,1 -l 100 -t TCP_STREAM -- -k THROUGHPUT -M 65536 -m 65536
>>
>> I ran the test on upstream net-next including your patch and than reverted it
>> and these are the results I got was improvement from 14.6Gbps to 22.1Gbps.
>>
>> an additional difference I've noticed when inspecting the ethtool statics,
>> number of xmit_more packets increased from 4 to 160 with the reverted kernel.
>>
>> We are investigating this issue, do you have a hint?
>
> Which driver are you using for this test ?
AFAIK, mlx4
^ permalink raw reply
* [PATCH iproute2] netns: Rename & move get_netns_fd to lib
From: Vadim Kochan @ 2015-01-13 20:08 UTC (permalink / raw)
To: netdev; +Cc: Vadim Kochan
From: Vadim Kochan <vadim4j@gmail.com>
Renamed get_netns_fd -> netns_get_fd and moved to
lib/namespace.c
Signed-off-by: Vadim Kochan <vadim4j@gmail.com>
---
include/namespace.h | 1 +
ip/ip_common.h | 1 -
ip/iplink.c | 3 ++-
ip/ipnetns.c | 15 ---------------
lib/namespace.c | 15 +++++++++++++++
5 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/include/namespace.h b/include/namespace.h
index 2f13e65..b8c5cad 100644
--- a/include/namespace.h
+++ b/include/namespace.h
@@ -42,5 +42,6 @@ static int setns(int fd, int nstype)
#endif /* HAVE_SETNS */
extern int netns_switch(char *netns);
+extern int netns_get_fd(const char *netns);
#endif /* __NAMESPACE_H__ */
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 75bfb82..89a495e 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -87,7 +87,6 @@ struct link_util
struct link_util *get_link_kind(const char *kind);
struct link_util *get_link_slave_kind(const char *slave_kind);
-int get_netns_fd(const char *name);
#ifndef INFINITY_LIFE_TIME
#define INFINITY_LIFE_TIME 0xFFFFFFFFU
diff --git a/ip/iplink.c b/ip/iplink.c
index 2709173..c93d1dc 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -32,6 +32,7 @@
#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
+#include "namespace.h"
#define IPLINK_IOCTL_COMPAT 1
#ifndef LIBDIR
@@ -440,7 +441,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
NEXT_ARG();
if (netns != -1)
duparg("netns", *argv);
- if ((netns = get_netns_fd(*argv)) >= 0)
+ if ((netns = netns_get_fd(*argv)) >= 0)
addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
else if (get_integer(&netns, *argv, 0) == 0)
addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
diff --git a/ip/ipnetns.c b/ip/ipnetns.c
index 519d518..123318e 100644
--- a/ip/ipnetns.c
+++ b/ip/ipnetns.c
@@ -31,21 +31,6 @@ static int usage(void)
exit(-1);
}
-int get_netns_fd(const char *name)
-{
- char pathbuf[MAXPATHLEN];
- const char *path, *ptr;
-
- path = name;
- ptr = strchr(name, '/');
- if (!ptr) {
- snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
- NETNS_RUN_DIR, name );
- path = pathbuf;
- }
- return open(path, O_RDONLY);
-}
-
static int netns_list(int argc, char **argv)
{
struct dirent *entry;
diff --git a/lib/namespace.c b/lib/namespace.c
index 1554ce0..65c1e3d 100644
--- a/lib/namespace.c
+++ b/lib/namespace.c
@@ -84,3 +84,18 @@ int netns_switch(char *name)
bind_etc(name);
return 0;
}
+
+int netns_get_fd(const char *name)
+{
+ char pathbuf[MAXPATHLEN];
+ const char *path, *ptr;
+
+ path = name;
+ ptr = strchr(name, '/');
+ if (!ptr) {
+ snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
+ NETNS_RUN_DIR, name );
+ path = pathbuf;
+ }
+ return open(path, O_RDONLY);
+}
--
2.1.3
^ permalink raw reply related
* Re: [PATCH 3/6] net: davinci_emac: Free clock after checking the frequency
From: Tony Lindgren @ 2015-01-13 20:15 UTC (permalink / raw)
To: Felipe Balbi
Cc: Tom Lendacky, David Miller, netdev, linux-omap, Brian Hutchinson
In-Reply-To: <20150113195035.GS16533@saruman>
* Felipe Balbi <balbi@ti.com> [150113 11:54]:
> On Tue, Jan 13, 2015 at 01:48:24PM -0600, Tom Lendacky wrote:
> > On 01/13/2015 01:29 PM, Tony Lindgren wrote:
> > >We only use clk_get() to get the frequency, the rest is done by
> > >the runtime PM calls. Let's free the clock too.
> > >
> > >Cc: Brian Hutchinson <b.hutchman@gmail.com>
> > >Cc: Felipe Balbi <balbi@ti.com>
> > >Signed-off-by: Tony Lindgren <tony@atomide.com>
> > >---
> > > drivers/net/ethernet/ti/davinci_emac.c | 1 +
> > > 1 file changed, 1 insertion(+)
> > >
> > >diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
> > >index deb43b3..e9efc74 100644
> > >--- a/drivers/net/ethernet/ti/davinci_emac.c
> > >+++ b/drivers/net/ethernet/ti/davinci_emac.c
> > >@@ -1881,6 +1881,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
> > > return -EBUSY;
> > > }
> > > emac_bus_frequency = clk_get_rate(emac_clk);
> > >+ clk_put(emac_clk);
> >
> > The devm_clk_get call is used to get the clock so either a devm_clk_put
> > needs to be used here or just let the devm_ call do its thing and
> > automatically do the put when the module is unloaded.
>
> instead, if you really don't need the clock for anything other than
> getting its rate, why don't you just remove devm_ prefix from clk_get()?
That would make the fix two lines instead of one :) I guess up to David
to figure out which he prefers, I don't really have a preference here.
Regards,
Tony
^ permalink raw reply
* Re: [PATCH 5/6] net: davinci_emac: Fix ioremap for devices with MDIO within the EMAC address space
From: Tony Lindgren @ 2015-01-13 19:59 UTC (permalink / raw)
To: Felipe Balbi; +Cc: David Miller, netdev, linux-omap, Brian Hutchinson
In-Reply-To: <20150113195405.GU16533@saruman>
* Felipe Balbi <balbi@ti.com> [150113 11:57]:
> On Tue, Jan 13, 2015 at 11:29:27AM -0800, Tony Lindgren wrote:
> > Some devices like dm816x have the MDIO registers within the first EMAC
> > instance address space. Let's fix the issue by allowing to pass an
> > optional second IO range for the EMAC control register area.
> >
> > Cc: Brian Hutchinson <b.hutchman@gmail.com>
> > Cc: Felipe Balbi <balbi@ti.com>
> > Signed-off-by: Tony Lindgren <tony@atomide.com>
> > ---
> > drivers/net/ethernet/ti/davinci_emac.c | 15 ++++++++++++---
> > 1 file changed, 12 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
> > index 4c8d82c..0342273 100644
> > --- a/drivers/net/ethernet/ti/davinci_emac.c
> > +++ b/drivers/net/ethernet/ti/davinci_emac.c
> > @@ -1877,7 +1877,7 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv)
> > static int davinci_emac_probe(struct platform_device *pdev)
> > {
> > int rc = 0;
> > - struct resource *res;
> > + struct resource *res, *res_ctrl;
> > struct net_device *ndev;
> > struct emac_priv *priv;
> > unsigned long hw_ram_addr;
> > @@ -1936,11 +1936,20 @@ static int davinci_emac_probe(struct platform_device *pdev)
> > rc = PTR_ERR(priv->remap_addr);
> > goto no_pdata;
> > }
> > +
> > + res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > + if (res_ctrl) {
>
> devm_ioremap_resource() will check for res_ctrl being a valid pointer,
> perhaps below would be slightly better ?
>
>
> res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> priv->ctrl_base = devm_ioremap_resource(&pdev->dev, res_ctrl);
> if (IS_ERR(priv->ctrl_base))
> priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset;
>
We have a pile of devices using just one ioremap area so the second
ioremap area needs to be optional. That's why we only do it based on
if (res_ctrl).
Regards,
Tony
^ permalink raw reply
* [net-next v2 15/15] i40e: limit sriov to partition 1 of NPAR configurations
From: Jeff Kirsher @ 2015-01-13 19:58 UTC (permalink / raw)
To: davem; +Cc: Shannon Nelson, netdev, nhorman, sassmann, jogreene, Jeff Kirsher
In-Reply-To: <1421179093-10932-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Shannon Nelson <shannon.nelson@intel.com>
Make sure we only allow SR/IOV on the master PF of a port in multifunction
mode. This should be the case anyway based on the num_vfs configured in
the NVM, but this will help make sure there's no question. If we're not
in multifunction mode the partition_id will always be 1.
Change-ID: I8b2592366fe6782f15301bde2ebd1d4da240109d
Signed-off-by: Shannon Nelson <shannon.nelson@intel.com>
Tested-by: Jim Young <james.m.young@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 80430b0..f3b036d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -7319,7 +7319,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
#endif /* I40E_FCOE */
#ifdef CONFIG_PCI_IOV
- if (pf->hw.func_caps.num_vfs) {
+ if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) {
pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF;
pf->flags |= I40E_FLAG_SRIOV_ENABLED;
pf->num_req_vfs = min_t(int,
--
1.9.3
^ permalink raw reply related
* [net-next v2 13/15] i40e: limit WoL and link settings to partition 1
From: Jeff Kirsher @ 2015-01-13 19:58 UTC (permalink / raw)
To: davem; +Cc: Shannon Nelson, netdev, nhorman, sassmann, jogreene, Jeff Kirsher
In-Reply-To: <1421179093-10932-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Shannon Nelson <shannon.nelson@intel.com>
When in multi-function mode, e.g. Dell's NPAR, only partition 1
of each MAC is allowed to set WoL, speed, and flow control.
Change-ID: I87a9debc7479361c55a71f0120294ea319f23588
Signed-off-by: Shannon Nelson <shannon.nelson@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 43 +++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 951e876..b8230dc 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -219,6 +219,16 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = {
#define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN)
/**
+ * i40e_partition_setting_complaint - generic complaint for MFP restriction
+ * @pf: the PF struct
+ **/
+static void i40e_partition_setting_complaint(struct i40e_pf *pf)
+{
+ dev_info(&pf->pdev->dev,
+ "The link settings are allowed to be changed only from the first partition of a given port. Please switch to the first partition in order to change the setting.\n");
+}
+
+/**
* i40e_get_settings - Get Link Speed and Duplex settings
* @netdev: network interface device structure
* @ecmd: ethtool command
@@ -485,6 +495,14 @@ static int i40e_set_settings(struct net_device *netdev,
u8 autoneg;
u32 advertise;
+ /* Changing port settings is not supported if this isn't the
+ * port's controlling PF
+ */
+ if (hw->partition_id != 1) {
+ i40e_partition_setting_complaint(pf);
+ return -EOPNOTSUPP;
+ }
+
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
@@ -687,6 +705,14 @@ static int i40e_set_pauseparam(struct net_device *netdev,
u8 aq_failures;
int err = 0;
+ /* Changing the port's flow control is not supported if this isn't the
+ * port's controlling PF
+ */
+ if (hw->partition_id != 1) {
+ i40e_partition_setting_complaint(pf);
+ return -EOPNOTSUPP;
+ }
+
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
@@ -1503,7 +1529,7 @@ static void i40e_get_wol(struct net_device *netdev,
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
- if ((1 << hw->port) & wol_nvm_bits) {
+ if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1) {
wol->supported = 0;
wol->wolopts = 0;
} else {
@@ -1512,13 +1538,28 @@ static void i40e_get_wol(struct net_device *netdev,
}
}
+/**
+ * i40e_set_wol - set the WakeOnLAN configuration
+ * @netdev: the netdev in question
+ * @wol: the ethtool WoL setting data
+ **/
static int i40e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
+ struct i40e_vsi *vsi = np->vsi;
struct i40e_hw *hw = &pf->hw;
u16 wol_nvm_bits;
+ /* WoL not supported if this isn't the controlling PF on the port */
+ if (hw->partition_id != 1) {
+ i40e_partition_setting_complaint(pf);
+ return -EOPNOTSUPP;
+ }
+
+ if (vsi != pf->vsi[pf->lan_vsi])
+ return -EOPNOTSUPP;
+
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
if (((1 << hw->port) & wol_nvm_bits))
--
1.9.3
^ permalink raw reply related
* [net-next v2 12/15] i40e: Adding function for reading PBA String
From: Jeff Kirsher @ 2015-01-13 19:58 UTC (permalink / raw)
To: davem; +Cc: Kamil Krawczyk, netdev, nhorman, sassmann, jogreene, Jeff Kirsher
In-Reply-To: <1421179093-10932-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Kamil Krawczyk <kamil.krawczyk@intel.com>
Function will read PBA Block from Shadow RAM and return it in a string format.
Change-ID: I4ee7059f6e21bd0eba38687da15e772e0b4ab36e
Signed-off-by: Kamil Krawczyk <kamil.krawczyk@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_common.c | 59 ++++++++++++++++++++++++
drivers/net/ethernet/intel/i40e/i40e_prototype.h | 2 +
drivers/net/ethernet/intel/i40e/i40e_type.h | 2 +
3 files changed, 63 insertions(+)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index b16fc03..4f4d9d1 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -742,6 +742,65 @@ i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
#endif
/**
+ * i40e_read_pba_string - Reads part number string from EEPROM
+ * @hw: pointer to hardware structure
+ * @pba_num: stores the part number string from the EEPROM
+ * @pba_num_size: part number string buffer length
+ *
+ * Reads the part number string from the EEPROM.
+ **/
+i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num,
+ u32 pba_num_size)
+{
+ i40e_status status = 0;
+ u16 pba_word = 0;
+ u16 pba_size = 0;
+ u16 pba_ptr = 0;
+ u16 i = 0;
+
+ status = i40e_read_nvm_word(hw, I40E_SR_PBA_FLAGS, &pba_word);
+ if (status || (pba_word != 0xFAFA)) {
+ hw_dbg(hw, "Failed to read PBA flags or flag is invalid.\n");
+ return status;
+ }
+
+ status = i40e_read_nvm_word(hw, I40E_SR_PBA_BLOCK_PTR, &pba_ptr);
+ if (status) {
+ hw_dbg(hw, "Failed to read PBA Block pointer.\n");
+ return status;
+ }
+
+ status = i40e_read_nvm_word(hw, pba_ptr, &pba_size);
+ if (status) {
+ hw_dbg(hw, "Failed to read PBA Block size.\n");
+ return status;
+ }
+
+ /* Subtract one to get PBA word count (PBA Size word is included in
+ * total size)
+ */
+ pba_size--;
+ if (pba_num_size < (((u32)pba_size * 2) + 1)) {
+ hw_dbg(hw, "Buffer to small for PBA data.\n");
+ return I40E_ERR_PARAM;
+ }
+
+ for (i = 0; i < pba_size; i++) {
+ status = i40e_read_nvm_word(hw, (pba_ptr + 1) + i, &pba_word);
+ if (status) {
+ hw_dbg(hw, "Failed to read PBA Block word %d.\n", i);
+ return status;
+ }
+
+ pba_num[(i * 2)] = (pba_word >> 8) & 0xFF;
+ pba_num[(i * 2) + 1] = pba_word & 0xFF;
+ }
+ pba_num[(pba_size * 2)] = '\0';
+
+ return status;
+}
+
+/**
* i40e_get_media_type - Gets media type
* @hw: pointer to the hardware structure
**/
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index d1c7d63..68e852a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -248,6 +248,8 @@ void i40e_clear_pxe_mode(struct i40e_hw *hw);
bool i40e_get_link_status(struct i40e_hw *hw);
i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr);
i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr);
+i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num,
+ u32 pba_num_size);
i40e_status i40e_validate_mac_addr(u8 *mac_addr);
void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable);
#ifdef I40E_FCOE
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
index 611de3e..ff121fe 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -1140,6 +1140,8 @@ struct i40e_hw_port_stats {
/* Checksum and Shadow RAM pointers */
#define I40E_SR_NVM_CONTROL_WORD 0x00
#define I40E_SR_EMP_MODULE_PTR 0x0F
+#define I40E_SR_PBA_FLAGS 0x15
+#define I40E_SR_PBA_BLOCK_PTR 0x16
#define I40E_SR_NVM_IMAGE_VERSION 0x18
#define I40E_SR_NVM_WAKE_ON_LAN 0x19
#define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27
--
1.9.3
^ 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