Skip to contentSkip to conversation

Flow API

PATCH Superseded John Fastabend 7 participants 29 messages Jan 13, 2015 – Jan 19, 2015

Subsystems (3)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover Flow API

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 · 1 line
Signature
#1 lore raw

PATCH v2 1/12 net: flow_table: create interface for hw match/action tables

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.

---
 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
include/linux/if_flow.h new file +188 -0
new file mode 100644
index 0000000..3e1829e
@@ -0,0 +1,188 @@
+/*
+ * include/linux/net/if_flow.h - Flow table interface for Switch devices
+ * Copyright (c) 2014 John Fastabend <[email protected]>
+ *
+ * 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 <[email protected]>
+ */
+
+#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
include/linux/netdevice.h +38 -0
index 679e6e9..74481b9 100644
@@ -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
 };
 
 /**
include/uapi/linux/if_flow.h new file +389 -0

389 lines not on this page · raw message

net/Kconfig +7 -0
index ff9ffc1..8380bfe 100644
@@ -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
net/core/Makefile +1 -0
index 235e6c5..1eea785 100644
@@ -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
net/core/flow_table.c new file +942 -0

942 lines not on this page · raw message

#2 lore raw

Re: [net-next PATCH v2 01/12] net: flow_table: create interface for hw match/action tables

[snip]

net/core/flow_table.c line 2

Quoted 5 lines
net/core/flow_table.c new file +2 -0
@@ -0,0 +1,942 @@
+/*
+ * include/uapi/linux/if_flow.h - Flow table interface for Switch devices

Hi John,

its seems that the path in the above comment should be net/core/flow_table.c.

[snip]

#3 lore raw

Re: [net-next PATCH v2 01/12] net: flow_table: create interface for hw match/action tables

Quoted 10 lines · Simon Horman

[snip]

net/core/flow_table.c line 2

Quoted 5 lines
net/core/flow_table.c new file +2 -0
@@ -0,0 +1,942 @@
+/*
+ * include/uapi/linux/if_flow.h - Flow table interface for Switch devices

Hi John,

Quoted 5 lines

its seems that the path in the above comment should be net/core/flow_table.c.

[snip]

Thanks Simon, I've merged all three comments.

Signature · 1 line
John Fastabend         Intel Corporation
#4 lore raw

PATCH v2 2/12 net: flow_table: add flow, delete flow

In reply to John Fastabend

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.

---
 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(-)
include/linux/if_flow.h +20 -1
index 3e1829e..23dec9b 100644
@@ -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_ */
include/linux/netdevice.h +10 -0
index 74481b9..9d57f8b 100644
@@ -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
 };
 
include/uapi/linux/if_flow.h +51 -0

75 lines not on this page · raw message

net/core/flow_table.c +760 -0

784 lines not on this page · raw message

#5 lore raw

Re: [net-next PATCH v2 02/12] net: flow_table: add flow, delete flow

Quoted 9 lines · John Fastabend

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

should now netlink cmd be called table_set_rules ? and s/flow/rule/ everywhere in commit log?

Quoted 4 lines

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 {

commit log no longer matches implementation ;) should be net_flow_rule ?

can you update your .html writeup?
I hope to see more real examples in there.

btw how the whole thing will work with queue splitting from your other patch?

#6 lore raw

Re: [net-next PATCH v2 02/12] net: flow_table: add flow, delete flow

Quoted 15 lines · Alexei Starovoitov
Quoted 9 lines · John Fastabend

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

should now netlink cmd be called table_set_rules ? and s/flow/rule/ everywhere in commit log?

Quoted 8 lines
Quoted 4 lines

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 {

commit log no longer matches implementation ;)
should be net_flow_rule ?

Oops, I guess I'll update it after waiting a bit for more feedback.

Quoted 1 line

can you update your .html writeup?

Took a quick scan at this think I caught most cases and some typos as well.

Quoted 1 line

I hope to see more real examples in there.

Sure, I'll put together some more interesting examples in the next day or so.

Quoted 4 lines

btw how the whole thing will work with queue splitting from your other patch?

If one of the actions supported by the device is forward_to_queue() or forward_to_socket() we can use the API to steer potentially interesting packets to a user space application for processing.

Going the other way applications could tell the hardware to drop/mangle/fwd packets. At some point I thought it would be interesting to use both the the flow API here and the queue splitting from the other patch with a tool like Suricata.

.John

Signature · 1 line
John Fastabend         Intel Corporation
#7 lore raw

Re: [net-next PATCH v2 02/12] net: flow_table: add flow, delete flow

Quoted 3 lines · John Fastabend

If one of the actions supported by the device is forward_to_queue() or forward_to_socket() we can use the API to steer potentially interesting packets to a user space application for processing.

+1

Ideally using NFQUEUE so we can leverage existing infrastructure.

#8 lore raw

Re: [net-next PATCH v2 02/12] net: flow_table: add flow, delete flow

In reply to John Fastabend

[snip]

Quoted 8 lines
include/linux/if_flow.h +0 -0
index 3e1829e..23dec9b 100644
@@ -185,4 +185,23 @@ struct net_flow_tbl_node {
 	__u32 flags;
 	struct net_flow_jump_table *jump;
 };

include/linux/if_flow.h struct net_flow_tbl_node { · line 196

Quoted 10 lines
include/linux/if_flow.h +9 -1
-#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

Hi John,

@action is miss-spelt above.

[snip]

#9 lore raw

PATCH v2 3/12 net: flow: implement flow cache for get routines

In reply to John Fastabend

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.

---
 include/linux/if_flow.h |   21 +++++++
 net/core/flow_table.c   |  146 +++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 160 insertions(+), 7 deletions(-)
include/linux/if_flow.h +21 -0
index 23dec9b..dc70e3e 100644
@@ -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_ */
net/core/flow_table.c +139 -7

217 lines not on this page · raw message

maintainer
#10 lore raw

Re: [net-next PATCH v2 03/12] net: flow: implement flow cache for get routines

From: John Fastabend <[email protected]>
Date: Tue, 13 Jan 2015 13:36:22 -0800

Quoted 3 lines

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.

Thomas please provide some feedback on this, thanks.

#11 lore raw

Re: [net-next PATCH v2 03/12] net: flow: implement flow cache for get routines

In reply to John Fastabend
Quoted 7 lines · John Fastabend

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.

John, can you please clarify a bit, I'm not sure I understand. Are you talking about delayed freeing of the table itself or elements?

The Netlink usage would be an example of a user with delayed element freeing.

I'm glad to add whatever is required.

#12 lore raw

Re: [net-next PATCH v2 03/12] net: flow: implement flow cache for get routines

Quoted 9 lines · Thomas Graf
Quoted 7 lines · John Fastabend

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.

Quoted 8 lines

John, can you please clarify a bit, I'm not sure I understand. Are you talking about delayed freeing of the table itself or elements?

The Netlink usage would be an example of a user with delayed element freeing.

I'm glad to add whatever is required.

Took another look at the netlink code looks like this is the correct pattern where the call_rcu implements the delayed freeing after a grace period.

	mutex_lock(&my_hash_lock);
	rhashtable_remove(&my_hash, &my_obj->rhash_head);
	mutex_unlock(&my_hash_lock)

	[...]

	call_rcu(&my_obj->rcu, deferred_my_obj_free);

anyways it looks like it is there no problem after all and I don't recall what I was thinking thanks for bearing with me. I'll convert this code to avoid the over-use of rcu_sync.

Thanks,
John

Signature · 1 line
John Fastabend         Intel Corporation
#13 lore raw

Re: [net-next PATCH v2 03/12] net: flow: implement flow cache for get routines

Quoted 3 lines · John Fastabend

anyways it looks like it is there no problem after all and I don't recall what I was thinking thanks for bearing with me. I'll convert this code to avoid the over-use of rcu_sync.

It's likely that you looked at this before the per bucket lock work occured. That's when the Netlink rhashtable code was converted to RCU with the rhashtable API changed accordingly.

#14 lore raw

Re: [net-next PATCH v2 03/12] net: flow: implement flow cache for get routines

In reply to John Fastabend

On Tue, Jan 13, 2015 at 01:36:22PM -0800, John Fastabend wrote:

[snip]

Quoted 4 lines
include/linux/if_flow.h +0 -0
index 23dec9b..dc70e3e 100644

[snip]

include/linux/if_flow.h struct net_flow_tbl_node { · line 204

Quoted 5 lines
include/linux/if_flow.h +1 -0
@@ -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;

Hi John,

its seems that the 'node' field is new to this structure, as compared with earlier versions with of this code, and that some documentation for it should be added to the comment immediately above the struct definition?

[snip]

#15 lore raw

PATCH v2 4/12 net: flow_table: create a set of common headers and actions

In reply to John Fastabend

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.

---
 include/linux/if_flow_common.h |  257 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 257 insertions(+)
 create mode 100644 include/linux/if_flow_common.h
include/linux/if_flow_common.h new file +257 -0
new file mode 100644
index 0000000..d4dd749
@@ -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
#16 lore raw

Re: [net-next PATCH v2 04/12] net: flow_table: create a set of common headers and actions

Quoted 52 lines · John Fastabend

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.

---
 include/linux/if_flow_common.h |  257 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 257 insertions(+)
 create mode 100644 include/linux/if_flow_common.h
include/linux/if_flow_common.h new file +26 -0
new file mode 100644
index 0000000..d4dd749
@@ -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,

include/linux/if_flow_common.h line 36

Quoted 10 lines
include/linux/if_flow_common.h new file +10 -0
+       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},
+};

All of these code chunks get repeated in each module that includes if_flow_common.h, regardless if the module needs all of them.

Maybe #define would be better?

include/linux/if_flow_common.h line 85

Quoted 1 line
include/linux/if_flow_common.h new file +1 -0
+struct net_flow_field net_flow_ipv4_fields[14] = {

net_flow_ipv4_fields[]. more like this...

-scott

#17 lore raw

PATCH v2 5/12 net: flow_table: add validation functions for flows

In reply to John Fastabend

This adds common validation functions that is used before adding flows to verify they match the table spec returned from driver.

---
 net/core/flow_table.c |   75 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)
net/core/flow_table.c +75 -0
index baeae64..a938929 100644
@@ -1627,6 +1627,78 @@ static int net_flow_del_rule_cache(struct net_flow_tbl *table,
 	return -EEXIST;
 }
 
+static int net_flow_is_valid_action_arg(struct net_flow_action *a, int id)
+{
+	struct net_flow_action_arg *args = a->args;
+	int i;
+
+	/* Actions may not have any arguments */
+	if (!a->args)
+		return 0;
+
+	for (i = 0; args[i].type != NFL_ACTION_ARG_TYPE_NULL; i++) {
+		if (a->args[i].type == NFL_ACTION_ARG_TYPE_NULL ||
+		    args[i].type != a->args[i].type)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int net_flow_is_valid_action(struct net_flow_action *a, int *actions)
+{
+	int i;
+
+	for (i = 0; actions[i]; i++) {
+		if (actions[i] == a->uid)
+			return net_flow_is_valid_action_arg(a, a->uid);
+	}
+	return -EINVAL;
+}
+
+static int net_flow_is_valid_match(struct net_flow_field_ref *f,
+				   struct net_flow_field_ref *fields)
+{
+	int i;
+
+	for (i = 0; fields[i].header; i++) {
+		if (f->header == fields[i].header &&
+		    f->field == fields[i].field)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int net_flow_is_valid_rule(struct net_flow_tbl *table,
+				  struct net_flow_rule *flow)
+{
+	struct net_flow_field_ref *fields = table->matches;
+	int *actions = table->actions;
+	int i, err;
+
+	/* Only accept flows with matches AND actions it does not seem
+	 * correct to allow a match without actions or action chains
+	 * that will never be hit
+	 */
+	if (!flow->actions || !flow->matches)
+		return -EINVAL;
+
+	for (i = 0; flow->actions[i].uid; i++) {
+		err = net_flow_is_valid_action(&flow->actions[i], actions);
+		if (err)
+			return -EINVAL;
+	}
+
+	for (i = 0; flow->matches[i].header; i++) {
+		err = net_flow_is_valid_match(&flow->matches[i], fields);
+		if (err)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int net_flow_table_cmd_flows(struct sk_buff *recv_skb,
 				    struct genl_info *info)
 {
@@ -1695,6 +1767,9 @@ static int net_flow_table_cmd_flows(struct sk_buff *recv_skb,
 
 		switch (cmd) {
 		case NFL_TABLE_CMD_SET_FLOWS:
+			err = net_flow_is_valid_rule(table, this);
+			if (err)
+				break;
 			err = dev->netdev_ops->ndo_flow_set_rule(dev, this);
 			if (!err)
 				net_flow_add_rule_cache(table, this);
#18 lore raw

PATCH v2 6/12 net: rocker: add pipeline model for rocker switch

In reply to John Fastabend

This adds rocker support for the net_flow_get_* operations. With this we can interrogate rocker.

Here we see that for static configurations enabling the get operations is simply a matter of defining a pipeline model and returning the structures for the core infrastructure to encapsulate into netlink messages.

---
 drivers/net/ethernet/rocker/rocker.c          |   65 ++++
 drivers/net/ethernet/rocker/rocker_pipeline.h |  451 +++++++++++++++++++++++++
 2 files changed, 516 insertions(+)
 create mode 100644 drivers/net/ethernet/rocker/rocker_pipeline.h
drivers/net/ethernet/rocker/rocker.c +65 -0
index 2f398fa..d2ea451 100644
@@ -36,6 +36,7 @@
 #include <generated/utsrelease.h>
 
 #include "rocker.h"
+#include "rocker_pipeline.h"
 
 static const char rocker_driver_name[] = "rocker";
 
@@ -3781,6 +3782,56 @@ static int rocker_port_switch_port_stp_update(struct net_device *dev, u8 state)
 	return rocker_port_stp_update(rocker_port, state);
 }
 
+static void rocker_destroy_flow_tables(struct rocker_port *rocker_port)
+{
+	int i;
+
+	for (i = 0; rocker_table_list[i]; i++)
+		net_flow_destroy_cache(rocker_table_list[i]);
+}
+
+static int rocker_init_flow_tables(struct rocker_port *rocker_port)
+{
+	int i, err;
+
+	for (i = 0; rocker_table_list[i]; i++) {
+		err = net_flow_init_cache(rocker_table_list[i]);
+		if (err) {
+			rocker_destroy_flow_tables(rocker_port);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_NET_FLOW_TABLES
+static struct net_flow_tbl **rocker_get_tables(struct net_device *d)
+{
+	return rocker_table_list;
+}
+
+static struct net_flow_hdr **rocker_get_headers(struct net_device *d)
+{
+	return rocker_header_list;
+}
+
+static struct net_flow_action **rocker_get_actions(struct net_device *d)
+{
+	return rocker_action_list;
+}
+
+static struct net_flow_tbl_node **rocker_get_tgraph(struct net_device *d)
+{
+	return rocker_table_nodes;
+}
+
+static struct net_flow_hdr_node **rocker_get_hgraph(struct net_device *d)
+{
+	return rocker_header_nodes;
+}
+#endif
+
 static const struct net_device_ops rocker_port_netdev_ops = {
 	.ndo_open			= rocker_port_open,
 	.ndo_stop			= rocker_port_stop,
@@ -3795,6 +3846,13 @@ static const struct net_device_ops rocker_port_netdev_ops = {
 	.ndo_bridge_getlink		= rocker_port_bridge_getlink,
 	.ndo_switch_parent_id_get	= rocker_port_switch_parent_id_get,
 	.ndo_switch_port_stp_update	= rocker_port_switch_port_stp_update,
+#ifdef CONFIG_NET_FLOW_TABLES
+	.ndo_flow_get_tbls		= rocker_get_tables,
+	.ndo_flow_get_hdrs		= rocker_get_headers,
+	.ndo_flow_get_actions		= rocker_get_actions,
+	.ndo_flow_get_tbl_graph		= rocker_get_tgraph,
+	.ndo_flow_get_hdr_graph		= rocker_get_hgraph,
+#endif
 };
 
 /********************
@@ -3960,6 +4018,7 @@ static void rocker_remove_ports(struct rocker *rocker)
 		rocker_port = rocker->ports[i];
 		rocker_port_ig_tbl(rocker_port, ROCKER_OP_FLAG_REMOVE);
 		unregister_netdev(rocker_port->dev);
+		rocker_destroy_flow_tables(rocker_port);
 	}
 	kfree(rocker->ports);
 }
@@ -4023,6 +4082,12 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
 		goto err_port_ig_tbl;
 	}
 
+	err = rocker_init_flow_tables(rocker_port);
+	if (err) {
+		dev_err(&pdev->dev, "install flow table failed\n");
+		goto err_port_ig_tbl;
+	}
+
 	return 0;
 
 err_port_ig_tbl:
drivers/net/ethernet/rocker/rocker_pipeline.h new file +451 -0

451 lines not on this page · raw message

#19 lore raw

Re: [net-next PATCH v2 06/12] net: rocker: add pipeline model for rocker switch

Quoted 43 lines · John Fastabend

This adds rocker support for the net_flow_get_* operations. With this we can interrogate rocker.

Here we see that for static configurations enabling the get operations is simply a matter of defining a pipeline model and returning the structures for the core infrastructure to encapsulate into netlink messages.

---
 drivers/net/ethernet/rocker/rocker.c          |   65 ++++
 drivers/net/ethernet/rocker/rocker_pipeline.h |  451 +++++++++++++++++++++++++
 2 files changed, 516 insertions(+)
 create mode 100644 drivers/net/ethernet/rocker/rocker_pipeline.h
drivers/net/ethernet/rocker/rocker.c +13 -0
index 2f398fa..d2ea451 100644
@@ -36,6 +36,7 @@
 #include <generated/utsrelease.h>
 
 #include "rocker.h"
+#include "rocker_pipeline.h"
 
 static const char rocker_driver_name[] = "rocker";
 
@@ -3781,6 +3782,56 @@ static int rocker_port_switch_port_stp_update(struct net_device *dev, u8 state)
        return rocker_port_stp_update(rocker_port, state);
 }
 
+static void rocker_destroy_flow_tables(struct rocker_port *rocker_port)
+{
+       int i;
+
+       for (i = 0; rocker_table_list[i]; i++)
+               net_flow_destroy_cache(rocker_table_list[i]);
+}
+
+static int rocker_init_flow_tables(struct rocker_port *rocker_port)
+{
+       int i, err;
+

drivers/net/ethernet/rocker/rocker.c static int rocker_port_switch_port_stp_update(struct net_device *dev, u8 state) · line 3806

Quoted 10 lines
drivers/net/ethernet/rocker/rocker.c +10 -0
+       for (i = 0; rocker_table_list[i]; i++) {
+               err = net_flow_init_cache(rocker_table_list[i]);
+               if (err) {
+                       rocker_destroy_flow_tables(rocker_port);
+                       return err;
+               }
+       }
+
+       return 0;
+}

Can these be moved up above drivers? They don't seem driver or device specific. Call .ndo_flow_get_tbls to get table list.

Quoted 17 lines
drivers/net/ethernet/rocker/rocker.c +17 -0
+
+#ifdef CONFIG_NET_FLOW_TABLES
+static struct net_flow_tbl **rocker_get_tables(struct net_device *d)
+{
+       return rocker_table_list;
+}
+
+static struct net_flow_hdr **rocker_get_headers(struct net_device *d)
+{
+       return rocker_header_list;
+}
+
+static struct net_flow_action **rocker_get_actions(struct net_device *d)
+{
+       return rocker_action_list;
+}
+

drivers/net/ethernet/rocker/rocker.c static int rocker_port_switch_port_stp_update(struct net_device *dev, u8 state) · line 3833

Quoted 10 lines
drivers/net/ethernet/rocker/rocker.c +10 -0
+static struct net_flow_tbl_node **rocker_get_tgraph(struct net_device *d)
+{
+       return rocker_table_nodes;
+}
+
+static struct net_flow_hdr_node **rocker_get_hgraph(struct net_device *d)
+{
+       return rocker_header_nodes;
+}
+#endif

Do these need to be functions since they all just return static pointer lists?

drivers/net/ethernet/rocker/rocker.c static const struct net_device_ops rocker_port_netdev_ops = { · line 3854

Quoted 13 lines
drivers/net/ethernet/rocker/rocker.c +6 -0
 static const struct net_device_ops rocker_port_netdev_ops = {
        .ndo_open                       = rocker_port_open,
        .ndo_stop                       = rocker_port_stop,
@@ -3795,6 +3846,13 @@ static const struct net_device_ops rocker_port_netdev_ops = {
        .ndo_bridge_getlink             = rocker_port_bridge_getlink,
        .ndo_switch_parent_id_get       = rocker_port_switch_parent_id_get,
        .ndo_switch_port_stp_update     = rocker_port_switch_port_stp_update,
+#ifdef CONFIG_NET_FLOW_TABLES
+       .ndo_flow_get_tbls              = rocker_get_tables,
+       .ndo_flow_get_hdrs              = rocker_get_headers,
+       .ndo_flow_get_actions           = rocker_get_actions,
+       .ndo_flow_get_tbl_graph         = rocker_get_tgraph,
+       .ndo_flow_get_hdr_graph         = rocker_get_hgraph,

Please keep the rocker_port_xxx for .ndo_xxx naming convention.

Quoted 13 lines
drivers/net/ethernet/rocker/rocker.c +2 -0
+#endif
 };
 
 /********************
@@ -3960,6 +4018,7 @@ static void rocker_remove_ports(struct rocker *rocker)
                rocker_port = rocker->ports[i];
                rocker_port_ig_tbl(rocker_port, ROCKER_OP_FLAG_REMOVE);
                unregister_netdev(rocker_port->dev);
+               rocker_destroy_flow_tables(rocker_port);
        }
        kfree(rocker->ports);
 }

drivers/net/ethernet/rocker/rocker.c static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) · line 4087

Quoted 6 lines
drivers/net/ethernet/rocker/rocker.c +3 -0
@@ -4023,6 +4082,12 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
                goto err_port_ig_tbl;
        }
 
+       err = rocker_init_flow_tables(rocker_port);
+       if (err) {
+               dev_err(&pdev->dev, "install flow table failed\n");

s/table/tables/

drivers/net/ethernet/rocker/rocker.c static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) · line 4088

Quoted 1 line
drivers/net/ethernet/rocker/rocker.c +1 -0
+               goto err_port_ig_tbl;

Need to rocker_port_ig_tbl(rocker_port, ROCKER_OP_FLAG_REMOVE) to clean up.

-scott

#20 lore raw

PATCH v2 7/12 net: rocker: add set flow rules

In reply to John Fastabend

Implement set flow operations for existing rocker tables.

---
 drivers/net/ethernet/rocker/rocker.c |  422 ++++++++++++++++++++++++++++++++++
 1 file changed, 422 insertions(+)
drivers/net/ethernet/rocker/rocker.c +422 -0
index d2ea451..08efd8b 100644
@@ -3830,6 +3830,425 @@ static struct net_flow_hdr_node **rocker_get_hgraph(struct net_device *d)
 {
 	return rocker_header_nodes;
 }
+
+static u32 rocker_goto_value(u32 id)
+{
+	switch (id) {
+	case ROCKER_FLOW_TABLE_ID_INGRESS_PORT:
+		return ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT;
+	case ROCKER_FLOW_TABLE_ID_VLAN:
+		return ROCKER_OF_DPA_TABLE_ID_VLAN;
+	case ROCKER_FLOW_TABLE_ID_TERMINATION_MAC:
+		return ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
+	case ROCKER_FLOW_TABLE_ID_UNICAST_ROUTING:
+		return ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
+	case ROCKER_FLOW_TABLE_ID_MULTICAST_ROUTING:
+		return ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING;
+	case ROCKER_FLOW_TABLE_ID_BRIDGING:
+		return ROCKER_OF_DPA_TABLE_ID_BRIDGING;
+	case ROCKER_FLOW_TABLE_ID_ACL_POLICY:
+		return ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
+	default:
+		return 0;
+	}
+}
+
+static int rocker_flow_set_ig_port(struct net_device *dev,
+				   struct net_flow_rule *flow)
+{
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	enum rocker_of_dpa_table_id goto_tbl;
+	u32 in_lport_mask, in_lport;
+	int flags = 0;
+
+	/* ingress port table only supports one field/mask/action this
+	 * simplifies the key construction and we can assume the values
+	 * are the correct types/mask/action by valid check above. The
+	 * user could pass multiple match/actions in a message with the
+	 * same field multiple times currently the valid test does not
+	 * catch this and we just use the first specified.
+	 */
+	in_lport = flow->matches[0].value_u32;
+	in_lport_mask = flow->matches[0].mask_u32;
+	goto_tbl = rocker_goto_value(flow->actions[0].args[0].value_u16);
+
+	return rocker_flow_tbl_ig_port(rocker_port, flags,
+				       in_lport, in_lport_mask,
+				       goto_tbl);
+}
+
+static int rocker_flow_set_vlan(struct net_device *dev,
+				struct net_flow_rule *flow)
+{
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	__be16 vlan_id, vlan_id_mask, new_vlan_id;
+	bool untagged, have_in_lport = false;
+	enum rocker_of_dpa_table_id goto_tbl;
+	int i, flags = 0;
+	u32 in_lport;
+
+	goto_tbl = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
+
+	/* If user does not specify vid match default to any */
+	vlan_id = htons(1);
+	vlan_id_mask = 0;
+
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		switch (flow->matches[i].instance) {
+		case ROCKER_HEADER_INSTANCE_IN_LPORT:
+			in_lport = flow->matches[i].value_u32;
+			have_in_lport = true;
+			break;
+		case ROCKER_HEADER_INSTANCE_VLAN_OUTER:
+			if (flow->matches[i].field != HEADER_VLAN_VID)
+				break;
+
+			vlan_id = htons(flow->matches[i].value_u16);
+			vlan_id_mask = htons(flow->matches[i].mask_u16);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (!have_in_lport)
+		return -EINVAL;
+
+	/* If user does not specify a new vlan id use default vlan id */
+	new_vlan_id = rocker_port_vid_to_vlan(rocker_port, vlan_id, &untagged);
+
+	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		struct net_flow_action_arg *arg = &flow->actions[i].args[0];
+
+		switch (flow->actions[i].uid) {
+		case ACTION_SET_VLAN_ID:
+			new_vlan_id = htons(arg->value_u16);
+			if (new_vlan_id)
+				untagged = false;
+			break;
+		}
+	}
+
+	return rocker_flow_tbl_vlan(rocker_port, flags, in_lport,
+				    vlan_id, vlan_id_mask, goto_tbl,
+				    untagged, new_vlan_id);
+}
+
+static int rocker_flow_set_term_mac(struct net_device *dev,
+				    struct net_flow_rule *flow)
+{
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	__be16 vlan_id, vlan_id_mask, ethtype = 0;
+	const u8 *eth_dst, *eth_dst_mask;
+	u32 in_lport, in_lport_mask;
+	int i, flags = 0;
+	bool copy_to_cpu;
+
+	/* If user does not specify vid match default to any */
+	vlan_id = rocker_port->internal_vlan_id;
+	vlan_id_mask = 0;
+
+	/* If user does not specify in_lport match default to any */
+	in_lport = rocker_port->lport;
+	in_lport_mask = 0;
+
+	/* If user does not specify a mac address match any */
+	eth_dst = rocker_port->dev->dev_addr;
+	eth_dst_mask = zero_mac;
+
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		switch (flow->matches[i].instance) {
+		case ROCKER_HEADER_INSTANCE_IN_LPORT:
+			in_lport = flow->matches[i].value_u32;
+			in_lport_mask = flow->matches[i].mask_u32;
+			break;
+		case ROCKER_HEADER_INSTANCE_VLAN_OUTER:
+			if (flow->matches[i].field != HEADER_VLAN_VID)
+				break;
+
+			vlan_id = htons(flow->matches[i].value_u16);
+			vlan_id_mask = htons(flow->matches[i].mask_u16);
+			break;
+		case ROCKER_HEADER_INSTANCE_ETHERNET:
+			switch (flow->matches[i].field) {
+			case HEADER_ETHERNET_DST_MAC:
+				eth_dst = (u8 *)&flow->matches[i].value_u64;
+				eth_dst_mask = (u8 *)&flow->matches[i].mask_u64;
+				break;
+			case HEADER_ETHERNET_ETHERTYPE:
+				ethtype = htons(flow->matches[i].value_u16);
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (!ethtype)
+		return -EINVAL;
+
+	/* By default do not copy to cpu */
+	copy_to_cpu = false;
+
+	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		switch (flow->actions[i].uid) {
+		case ACTION_COPY_TO_CPU:
+			copy_to_cpu = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return rocker_flow_tbl_term_mac(rocker_port, in_lport, in_lport_mask,
+					ethtype, eth_dst, eth_dst_mask,
+					vlan_id, vlan_id_mask,
+					copy_to_cpu, flags);
+}
+
+static int rocker_flow_set_ucast_routing(struct net_device *dev,
+					 struct net_flow_rule *flow)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rocker_flow_set_mcast_routing(struct net_device *dev,
+					 struct net_flow_rule *flow)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rocker_flow_set_bridge(struct net_device *dev,
+				  struct net_flow_rule *flow)
+{
+	enum rocker_of_dpa_table_id goto_tbl;
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	u32 in_lport, in_lport_mask, group_id, tunnel_id;
+	__be16 vlan_id, vlan_id_mask;
+	const u8 *eth_dst, *eth_dst_mask;
+	int i, flags = 0;
+	bool copy_to_cpu;
+
+	goto_tbl = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
+
+	/* If user does not specify vid match default to any */
+	vlan_id = rocker_port->internal_vlan_id;
+	vlan_id_mask = 0;
+
+	/* If user does not specify in_lport match default to any */
+	in_lport = rocker_port->lport;
+	in_lport_mask = 0;
+
+	/* If user does not specify a mac address match any */
+	eth_dst = rocker_port->dev->dev_addr;
+	eth_dst_mask = NULL;
+
+	/* Do not support for tunnel_id yet. */
+	tunnel_id = 0;
+
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		switch (flow->matches[i].instance) {
+		case ROCKER_HEADER_INSTANCE_IN_LPORT:
+			in_lport = flow->matches[i].value_u32;
+			in_lport_mask = flow->matches[i].mask_u32;
+			break;
+		case ROCKER_HEADER_INSTANCE_VLAN_OUTER:
+			if (flow->matches[i].field != HEADER_VLAN_VID)
+				break;
+
+			vlan_id = htons(flow->matches[i].value_u16);
+			vlan_id_mask = htons(flow->matches[i].mask_u16);
+			break;
+		case ROCKER_HEADER_INSTANCE_ETHERNET:
+			switch (flow->matches[i].field) {
+			case HEADER_ETHERNET_DST_MAC:
+				eth_dst = (u8 *)&flow->matches[i].value_u64;
+				eth_dst_mask = (u8 *)&flow->matches[i].mask_u64;
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* By default do not copy to cpu and skip group assignment */
+	copy_to_cpu = false;
+	group_id = ROCKER_GROUP_NONE;
+
+	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		struct net_flow_action_arg *arg = &flow->actions[i].args[0];
+
+		switch (flow->actions[i].uid) {
+		case ACTION_COPY_TO_CPU:
+			copy_to_cpu = true;
+			break;
+		case ROCKER_ACTION_SET_GROUP_ID:
+			group_id = arg->value_u32;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* Ignoring eth_dst_mask it seems to cause a EINVAL return code */
+	return rocker_flow_tbl_bridge(rocker_port, flags,
+				      eth_dst, eth_dst_mask,
+				      vlan_id, tunnel_id,
+				      goto_tbl, group_id, copy_to_cpu);
+}
+
+static int rocker_flow_set_acl(struct net_device *dev,
+			       struct net_flow_rule *flow)
+{
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	u32 in_lport, in_lport_mask, group_id, tunnel_id;
+	__be16 vlan_id, vlan_id_mask, ethtype = 0;
+	const u8 *eth_dst, *eth_src, *eth_dst_mask, *eth_src_mask;
+	u8 protocol, protocol_mask, dscp, dscp_mask;
+	int i, flags = 0;
+
+	/* If user does not specify vid match default to any */
+	vlan_id = rocker_port->internal_vlan_id;
+	vlan_id_mask = 0;
+
+	/* If user does not specify in_lport match default to any */
+	in_lport = rocker_port->lport;
+	in_lport_mask = 0;
+
+	/* If user does not specify a mac address match any */
+	eth_dst = rocker_port->dev->dev_addr;
+	eth_src = zero_mac;
+	eth_dst_mask = NULL;
+	eth_src_mask = NULL;
+
+	/* If user does not set protocol/dscp mask them out */
+	protocol = 0;
+	dscp = 0;
+	protocol_mask = 0;
+	dscp_mask = 0;
+
+	/* Do not support for tunnel_id yet. */
+	tunnel_id = 0;
+
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		switch (flow->matches[i].instance) {
+		case ROCKER_HEADER_INSTANCE_IN_LPORT:
+			in_lport = flow->matches[i].value_u32;
+			in_lport_mask = flow->matches[i].mask_u32;
+			break;
+		case ROCKER_HEADER_INSTANCE_VLAN_OUTER:
+			if (flow->matches[i].field != HEADER_VLAN_VID)
+				break;
+
+			vlan_id = htons(flow->matches[i].value_u16);
+			vlan_id_mask = htons(flow->matches[i].mask_u16);
+			break;
+		case ROCKER_HEADER_INSTANCE_ETHERNET:
+			switch (flow->matches[i].field) {
+			case HEADER_ETHERNET_SRC_MAC:
+				eth_src = (u8 *)&flow->matches[i].value_u64;
+				eth_src_mask = (u8 *)&flow->matches[i].mask_u64;
+				break;
+			case HEADER_ETHERNET_DST_MAC:
+				eth_dst = (u8 *)&flow->matches[i].value_u64;
+				eth_dst_mask = (u8 *)&flow->matches[i].mask_u64;
+				break;
+			case HEADER_ETHERNET_ETHERTYPE:
+				ethtype = htons(flow->matches[i].value_u16);
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		case ROCKER_HEADER_INSTANCE_IPV4:
+			switch (flow->matches[i].field) {
+			case HEADER_IPV4_PROTOCOL:
+				protocol = flow->matches[i].value_u8;
+				protocol_mask = flow->matches[i].mask_u8;
+				break;
+			case HEADER_IPV4_DSCP:
+				dscp = flow->matches[i].value_u8;
+				dscp_mask = flow->matches[i].mask_u8;
+				break;
+			default:
+				return -EINVAL;
+			}
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* By default do not copy to cpu and skip group assignment */
+	group_id = ROCKER_GROUP_NONE;
+
+	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		switch (flow->actions[i].uid) {
+		case ROCKER_ACTION_SET_GROUP_ID:
+			group_id = flow->actions[i].args[0].value_u32;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return rocker_flow_tbl_acl(rocker_port, flags,
+				   in_lport, in_lport_mask,
+				   eth_src, eth_src_mask,
+				   eth_dst, eth_dst_mask, ethtype,
+				   vlan_id, vlan_id_mask,
+				   protocol, protocol_mask,
+				   dscp, dscp_mask,
+				   group_id);
+}
+
+static int rocker_set_flows(struct net_device *dev,
+			    struct net_flow_rule *flow)
+{
+	int err = -EINVAL;
+
+	if (!flow->matches || !flow->actions)
+		return -EINVAL;
+
+	switch (flow->table_id) {
+	case ROCKER_FLOW_TABLE_ID_INGRESS_PORT:
+		err = rocker_flow_set_ig_port(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_VLAN:
+		err = rocker_flow_set_vlan(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_TERMINATION_MAC:
+		err = rocker_flow_set_term_mac(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_UNICAST_ROUTING:
+		err = rocker_flow_set_ucast_routing(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_MULTICAST_ROUTING:
+		err = rocker_flow_set_mcast_routing(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_BRIDGING:
+		err = rocker_flow_set_bridge(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_ACL_POLICY:
+		err = rocker_flow_set_acl(dev, flow);
+		break;
+	default:
+		break;
+	}
+
+	return err;
+}
+
+static int rocker_del_flows(struct net_device *dev,
+			    struct net_flow_rule *flow)
+{
+	return -EOPNOTSUPP;
+}
 #endif
 
 static const struct net_device_ops rocker_port_netdev_ops = {
@@ -3852,6 +4271,9 @@ static const struct net_device_ops rocker_port_netdev_ops = {
 	.ndo_flow_get_actions		= rocker_get_actions,
 	.ndo_flow_get_tbl_graph		= rocker_get_tgraph,
 	.ndo_flow_get_hdr_graph		= rocker_get_hgraph,
+
+	.ndo_flow_set_rule		= rocker_set_flows,
+	.ndo_flow_del_rule		= rocker_del_flows,
 #endif
 };
 
#21 lore raw

PATCH v2 8/12 net: rocker: add group_id slices and drop explicit goto

In reply to John Fastabend

This adds the group tables for l3_unicast, l2_rewrite and l2. In addition to adding the tables we extend the metadata fields to support three different group id lookups. One for each table and drop the more generic one previously being used.

Finally we can also drop the goto action as it is not used anymore.

---
 drivers/net/ethernet/rocker/rocker.c          |  174 ++++++++++++++++++++++++
 drivers/net/ethernet/rocker/rocker_pipeline.h |  180 ++++++++++++++++++++++---
 2 files changed, 328 insertions(+), 26 deletions(-)
drivers/net/ethernet/rocker/rocker.c +170 -4
index 08efd8b..de4c58e 100644
@@ -4088,8 +4088,8 @@ static int rocker_flow_set_bridge(struct net_device *dev,
 		case ACTION_COPY_TO_CPU:
 			copy_to_cpu = true;
 			break;
-		case ROCKER_ACTION_SET_GROUP_ID:
-			group_id = arg->value_u32;
+		case ROCKER_ACTION_SET_L3_UNICAST_GID:
+			group_id = ROCKER_GROUP_L3_UNICAST(arg->value_u32);
 			break;
 		default:
 			return -EINVAL;
@@ -4188,9 +4188,11 @@ static int rocker_flow_set_acl(struct net_device *dev,
 	group_id = ROCKER_GROUP_NONE;
 
 	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		struct net_flow_action_arg *arg = &flow->actions[i].args[0];
+
 		switch (flow->actions[i].uid) {
-		case ROCKER_ACTION_SET_GROUP_ID:
-			group_id = flow->actions[i].args[0].value_u32;
+		case ROCKER_ACTION_SET_L3_UNICAST_GID:
+			group_id = ROCKER_GROUP_L3_UNICAST(arg->value_u32);
 			break;
 		default:
 			return -EINVAL;
@@ -4207,6 +4209,161 @@ static int rocker_flow_set_acl(struct net_device *dev,
 				   group_id);
 }
 
+static int rocker_flow_set_group_slice_l3_unicast(struct net_device *dev,
+						  struct net_flow_rule *flow)
+{
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	struct rocker_group_tbl_entry *entry;
+	int i, flags = 0;
+
+	entry = kzalloc(sizeof(*entry), rocker_op_flags_gfp(flags));
+	if (!entry)
+		return -ENOMEM;
+
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		struct net_flow_field_ref *r = &flow->matches[i];
+
+		switch (r->instance) {
+		case ROCKER_HEADER_INSTANCE_L3_UNICAST_GID:
+			entry->group_id = ROCKER_GROUP_L3_UNICAST(r->value_u32);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		struct net_flow_action_arg *arg = &flow->actions[i].args[0];
+
+		switch (flow->actions[i].uid) {
+		case ACTION_SET_ETH_SRC:
+			ether_addr_copy(entry->l3_unicast.eth_src,
+					(u8 *)&arg->value_u64);
+			break;
+		case ACTION_SET_ETH_DST:
+			ether_addr_copy(entry->l3_unicast.eth_dst,
+					(u8 *)&arg->value_u64);
+			break;
+		case ACTION_SET_VLAN_ID:
+			entry->l3_unicast.vlan_id = htons(arg->value_u16);
+			break;
+		case ACTION_CHECK_TTL_DROP:
+			entry->l3_unicast.ttl_check = true;
+			break;
+		case ROCKER_ACTION_SET_L2_REWRITE_GID:
+			entry->l3_unicast.group_id =
+				ROCKER_GROUP_L2_REWRITE(arg->value_u32);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return rocker_group_tbl_do(rocker_port, flags, entry);
+}
+
+static int rocker_flow_set_group_slice_l2_rewrite(struct net_device *dev,
+						  struct net_flow_rule *flow)
+{
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	struct rocker_group_tbl_entry *entry;
+	int i, flags = 0;
+
+	entry = kzalloc(sizeof(*entry), rocker_op_flags_gfp(flags));
+	if (!entry)
+		return -ENOMEM;
+
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		struct net_flow_field_ref *r = &flow->matches[i];
+
+		switch (r->instance) {
+		case ROCKER_HEADER_INSTANCE_L2_REWRITE_GID:
+			entry->group_id = ROCKER_GROUP_L2_REWRITE(r->value_u32);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		struct net_flow_action_arg *arg = &flow->actions[i].args[0];
+
+		switch (flow->actions[i].uid) {
+		case ACTION_SET_ETH_SRC:
+			ether_addr_copy(entry->l2_rewrite.eth_src,
+					(u8 *)&arg->value_u64);
+			break;
+		case ACTION_SET_ETH_DST:
+			ether_addr_copy(entry->l2_rewrite.eth_dst,
+					(u8 *)&arg->value_u64);
+			break;
+		case ACTION_SET_VLAN_ID:
+			entry->l2_rewrite.vlan_id = htons(arg->value_u16);
+			break;
+		case ROCKER_ACTION_SET_L2_GID:
+			entry->l2_rewrite.group_id =
+				ROCKER_GROUP_L2_INTERFACE(arg->value_u32,
+							  rocker_port->lport);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return rocker_group_tbl_do(rocker_port, flags, entry);
+}
+
+static int rocker_flow_set_group_slice_l2(struct net_device *dev,
+					  struct net_flow_rule *flow)
+{
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	struct rocker_group_tbl_entry *entry;
+	int i, flags = 0;
+	u32 lport;
+
+	entry = kzalloc(sizeof(*entry), rocker_op_flags_gfp(flags));
+	if (!entry)
+		return -ENOMEM;
+
+	lport = rocker_port->lport;
+
+	/* Use the dev lport if we don't have a specified lport instance
+	 * from the user. We need to walk the list once before to extract
+	 * any lport attribute.
+	 */
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		switch (flow->matches[i].instance) {
+		case ROCKER_HEADER_METADATA_IN_LPORT:
+			lport = flow->matches[i].value_u32;
+		}
+	}
+
+	for (i = 0; flow->matches && flow->matches[i].instance; i++) {
+		struct net_flow_field_ref *r = &flow->matches[i];
+
+		switch (r->instance) {
+		case ROCKER_HEADER_INSTANCE_L2_GID:
+			entry->group_id =
+				ROCKER_GROUP_L2_INTERFACE(r->value_u32, lport);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; flow->actions && flow->actions[i].uid; i++) {
+		switch (flow->actions[i].uid) {
+		case ACTION_POP_VLAN:
+			entry->l2_interface.pop_vlan = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return rocker_group_tbl_do(rocker_port, flags, entry);
+}
+
 static int rocker_set_flows(struct net_device *dev,
 			    struct net_flow_rule *flow)
 {
@@ -4237,6 +4394,15 @@ static int rocker_set_flows(struct net_device *dev,
 	case ROCKER_FLOW_TABLE_ID_ACL_POLICY:
 		err = rocker_flow_set_acl(dev, flow);
 		break;
+	case ROCKER_FLOW_TABLE_ID_GROUP_SLICE_L3_UNICAST:
+		err = rocker_flow_set_group_slice_l3_unicast(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_GROUP_SLICE_L2_REWRITE:
+		err = rocker_flow_set_group_slice_l2_rewrite(dev, flow);
+		break;
+	case ROCKER_FLOW_TABLE_ID_GROUP_SLICE_L2:
+		err = rocker_flow_set_group_slice_l2(dev, flow);
+		break;
 	default:
 		break;
 	}
drivers/net/ethernet/rocker/rocker_pipeline.h +158 -22

272 lines not on this page · raw message

#22 lore raw

PATCH v2 9/12 net: rocker: add multicast path to bridging

In reply to John Fastabend

Add path in table graph to send packets to the bridge table.

---
 drivers/net/ethernet/rocker/rocker_pipeline.h |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/net/ethernet/rocker/rocker_pipeline.h +8 -0
index 6d1e2ee..1a3dcdb 100644
@@ -511,6 +511,14 @@ struct net_flow_tbl_node rocker_table_node_vlan = {
 	.jump = rocker_table_node_vlan_next};
 
 struct net_flow_jump_table rocker_table_node_term_mac_next[] = {
+	{ .field = {.instance = ROCKER_HEADER_INSTANCE_ETHERNET,
+		    .header = HEADER_ETHERNET,
+		    .field = HEADER_ETHERNET_DST_MAC,
+		    .mask_type = NFL_MASK_TYPE_LPM,
+		    .type = NFL_FIELD_REF_ATTR_TYPE_U64,
+		    .value_u64 = (__u64)0x1,
+		    .mask_u64 = (__u64)0x1,
+	}, .node = ROCKER_FLOW_TABLE_ID_BRIDGING},
 	{ .field = {0}, .node = ROCKER_FLOW_TABLE_ID_UNICAST_ROUTING},
 	{ .field = {0}, .node = 0},
 };
#23 lore raw

PATCH v2 10/12 net: rocker: add cookie to group acls and use flow_id to set cookie

In reply to John Fastabend

Rocker uses a cookie value to identify flows however the flow API already has a unique id for each flow. To help the translation add support to set the cookie value through the internal rocker flow API and then use the unique id in the cases where it is available.

This patch extends the internal code paths to support the new cookie value.

---
 drivers/net/ethernet/rocker/rocker.c |   64 ++++++++++++++++++++++------------
 1 file changed, 42 insertions(+), 22 deletions(-)
drivers/net/ethernet/rocker/rocker.c +42 -22
index de4c58e..4cbead8 100644
@@ -120,6 +120,7 @@ struct rocker_flow_tbl_entry {
 
 struct rocker_group_tbl_entry {
 	struct hlist_node entry;
+	u64 cookie;
 	u32 cmd;
 	u32 group_id; /* key */
 	u16 group_count;
@@ -2233,7 +2234,8 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port,
 		kfree(match);
 	} else {
 		found = match;
-		found->cookie = rocker->flow_tbl_next_cookie++;
+		if (!found->cookie)
+			found->cookie = rocker->flow_tbl_next_cookie++;
 		hash_add(rocker->flow_tbl, &found->entry, found->key_crc32);
 		add_to_hw = true;
 	}
@@ -2311,7 +2313,7 @@ static int rocker_flow_tbl_do(struct rocker_port *rocker_port,
 		return rocker_flow_tbl_add(rocker_port, entry, nowait);
 }
 
-static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port,
+static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, u64 flow_id,
 				   int flags, u32 in_lport, u32 in_lport_mask,
 				   enum rocker_of_dpa_table_id goto_tbl)
 {
@@ -2327,11 +2329,14 @@ static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port,
 	entry->key.ig_port.in_lport_mask = in_lport_mask;
 	entry->key.ig_port.goto_tbl = goto_tbl;
 
+	if (flow_id)
+		entry->cookie = flow_id;
+
 	return rocker_flow_tbl_do(rocker_port, flags, entry);
 }
 
 static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port,
-				int flags, u32 in_lport,
+				int flags, u64 flow_id, u32 in_lport,
 				__be16 vlan_id, __be16 vlan_id_mask,
 				enum rocker_of_dpa_table_id goto_tbl,
 				bool untagged, __be16 new_vlan_id)
@@ -2352,10 +2357,14 @@ static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port,
 	entry->key.vlan.untagged = untagged;
 	entry->key.vlan.new_vlan_id = new_vlan_id;
 
+	if (flow_id)
+		entry->cookie = flow_id;
+
 	return rocker_flow_tbl_do(rocker_port, flags, entry);
 }
 
 static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port,
+				    u64 flow_id,
 				    u32 in_lport, u32 in_lport_mask,
 				    __be16 eth_type, const u8 *eth_dst,
 				    const u8 *eth_dst_mask, __be16 vlan_id,
@@ -2388,11 +2397,14 @@ static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port,
 	entry->key.term_mac.vlan_id_mask = vlan_id_mask;
 	entry->key.term_mac.copy_to_cpu = copy_to_cpu;
 
+	if (flow_id)
+		entry->cookie = flow_id;
+
 	return rocker_flow_tbl_do(rocker_port, flags, entry);
 }
 
 static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port,
-				  int flags,
+				  int flags, u64 flow_id,
 				  const u8 *eth_dst, const u8 *eth_dst_mask,
 				  __be16 vlan_id, u32 tunnel_id,
 				  enum rocker_of_dpa_table_id goto_tbl,
@@ -2442,11 +2454,14 @@ static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port,
 	entry->key.bridge.group_id = group_id;
 	entry->key.bridge.copy_to_cpu = copy_to_cpu;
 
+	if (flow_id)
+		entry->cookie = flow_id;
+
 	return rocker_flow_tbl_do(rocker_port, flags, entry);
 }
 
 static int rocker_flow_tbl_acl(struct rocker_port *rocker_port,
-			       int flags, u32 in_lport,
+			       int flags, u64 flow_id, u32 in_lport,
 			       u32 in_lport_mask,
 			       const u8 *eth_src, const u8 *eth_src_mask,
 			       const u8 *eth_dst, const u8 *eth_dst_mask,
@@ -2494,6 +2509,9 @@ static int rocker_flow_tbl_acl(struct rocker_port *rocker_port,
 	entry->key.acl.ip_tos_mask = ip_tos_mask;
 	entry->key.acl.group_id = group_id;
 
+	if (flow_id)
+		entry->cookie = flow_id;
+
 	return rocker_flow_tbl_do(rocker_port, flags, entry);
 }
 
@@ -2604,7 +2622,7 @@ static int rocker_group_tbl_do(struct rocker_port *rocker_port,
 }
 
 static int rocker_group_l2_interface(struct rocker_port *rocker_port,
-				     int flags, __be16 vlan_id,
+				     int flags, int flow_id, __be16 vlan_id,
 				     u32 out_lport, int pop_vlan)
 {
 	struct rocker_group_tbl_entry *entry;
@@ -2615,6 +2633,7 @@ static int rocker_group_l2_interface(struct rocker_port *rocker_port,
 
 	entry->group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_lport);
 	entry->l2_interface.pop_vlan = pop_vlan;
+	entry->cookie = flow_id;
 
 	return rocker_group_tbl_do(rocker_port, flags, entry);
 }
@@ -2713,7 +2732,7 @@ static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port,
 	if (rocker_port->stp_state == BR_STATE_LEARNING ||
 	    rocker_port->stp_state == BR_STATE_FORWARDING) {
 		out_lport = rocker_port->lport;
-		err = rocker_group_l2_interface(rocker_port, flags,
+		err = rocker_group_l2_interface(rocker_port, flags, 0,
 						vlan_id, out_lport,
 						pop_vlan);
 		if (err) {
@@ -2739,7 +2758,7 @@ static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port,
 		return 0;
 
 	out_lport = 0;
-	err = rocker_group_l2_interface(rocker_port, flags,
+	err = rocker_group_l2_interface(rocker_port, flags, 0,
 					vlan_id, out_lport,
 					pop_vlan);
 	if (err) {
@@ -2813,7 +2832,7 @@ static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port,
 	u32 group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_lport);
 	int err;
 
-	err = rocker_flow_tbl_acl(rocker_port, flags,
+	err = rocker_flow_tbl_acl(rocker_port, flags, 0,
 				  in_lport, in_lport_mask,
 				  eth_src, eth_src_mask,
 				  ctrl->eth_dst, ctrl->eth_dst_mask,
@@ -2842,7 +2861,7 @@ static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port,
 	if (!rocker_port_is_bridged(rocker_port))
 		return 0;
 
-	err = rocker_flow_tbl_bridge(rocker_port, flags,
+	err = rocker_flow_tbl_bridge(rocker_port, flags, 0,
 				     ctrl->eth_dst, ctrl->eth_dst_mask,
 				     vlan_id, tunnel_id,
 				     goto_tbl, group_id, ctrl->copy_to_cpu);
@@ -2864,7 +2883,7 @@ static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port,
 	if (ntohs(vlan_id) == 0)
 		vlan_id = rocker_port->internal_vlan_id;
 
-	err = rocker_flow_tbl_term_mac(rocker_port,
+	err = rocker_flow_tbl_term_mac(rocker_port, 0,
 				       rocker_port->lport, in_lport_mask,
 				       ctrl->eth_type, ctrl->eth_dst,
 				       ctrl->eth_dst_mask, vlan_id,
@@ -2978,7 +2997,7 @@ static int rocker_port_vlan(struct rocker_port *rocker_port, int flags,
 		return err;
 	}
 
-	err = rocker_flow_tbl_vlan(rocker_port, flags,
+	err = rocker_flow_tbl_vlan(rocker_port, flags, 0,
 				   in_lport, vlan_id, vlan_id_mask,
 				   goto_tbl, untagged, internal_vlan_id);
 	if (err)
@@ -3003,7 +3022,7 @@ static int rocker_port_ig_tbl(struct rocker_port *rocker_port, int flags)
 	in_lport_mask = 0xffff0000;
 	goto_tbl = ROCKER_OF_DPA_TABLE_ID_VLAN;
 
-	err = rocker_flow_tbl_ig_port(rocker_port, flags,
+	err = rocker_flow_tbl_ig_port(rocker_port, flags, 0,
 				      in_lport, in_lport_mask,
 				      goto_tbl);
 	if (err)
@@ -3053,7 +3072,7 @@ static int rocker_port_fdb_learn(struct rocker_port *rocker_port,
 		group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_lport);
 
 	if (!(flags & ROCKER_OP_FLAG_REFRESH)) {
-		err = rocker_flow_tbl_bridge(rocker_port, flags, addr, NULL,
+		err = rocker_flow_tbl_bridge(rocker_port, flags, 0, addr, NULL,
 					     vlan_id, tunnel_id, goto_tbl,
 					     group_id, copy_to_cpu);
 		if (err)
@@ -3188,7 +3207,7 @@ static int rocker_port_router_mac(struct rocker_port *rocker_port,
 		vlan_id = rocker_port->internal_vlan_id;
 
 	eth_type = htons(ETH_P_IP);
-	err = rocker_flow_tbl_term_mac(rocker_port,
+	err = rocker_flow_tbl_term_mac(rocker_port, 0,
 				       rocker_port->lport, in_lport_mask,
 				       eth_type, rocker_port->dev->dev_addr,
 				       dst_mac_mask, vlan_id, vlan_id_mask,
@@ -3197,7 +3216,7 @@ static int rocker_port_router_mac(struct rocker_port *rocker_port,
 		return err;
 
 	eth_type = htons(ETH_P_IPV6);
-	err = rocker_flow_tbl_term_mac(rocker_port,
+	err = rocker_flow_tbl_term_mac(rocker_port, 0,
 				       rocker_port->lport, in_lport_mask,
 				       eth_type, rocker_port->dev->dev_addr,
 				       dst_mac_mask, vlan_id, vlan_id_mask,
@@ -3232,7 +3251,7 @@ static int rocker_port_fwding(struct rocker_port *rocker_port)
 			continue;
 		vlan_id = htons(vid);
 		pop_vlan = rocker_vlan_id_is_internal(vlan_id);
-		err = rocker_group_l2_interface(rocker_port, flags,
+		err = rocker_group_l2_interface(rocker_port, flags, 0,
 						vlan_id, out_lport,
 						pop_vlan);
 		if (err) {
@@ -3872,7 +3891,7 @@ static int rocker_flow_set_ig_port(struct net_device *dev,
 	in_lport_mask = flow->matches[0].mask_u32;
 	goto_tbl = rocker_goto_value(flow->actions[0].args[0].value_u16);
 
-	return rocker_flow_tbl_ig_port(rocker_port, flags,
+	return rocker_flow_tbl_ig_port(rocker_port, flags, 0,
 				       in_lport, in_lport_mask,
 				       goto_tbl);
 }
@@ -3929,7 +3948,7 @@ static int rocker_flow_set_vlan(struct net_device *dev,
 		}
 	}
 
-	return rocker_flow_tbl_vlan(rocker_port, flags, in_lport,
+	return rocker_flow_tbl_vlan(rocker_port, flags, 0, in_lport,
 				    vlan_id, vlan_id_mask, goto_tbl,
 				    untagged, new_vlan_id);
 }
@@ -4003,7 +4022,8 @@ static int rocker_flow_set_term_mac(struct net_device *dev,
 		}
 	}
 
-	return rocker_flow_tbl_term_mac(rocker_port, in_lport, in_lport_mask,
+	return rocker_flow_tbl_term_mac(rocker_port, 0,
+					in_lport, in_lport_mask,
 					ethtype, eth_dst, eth_dst_mask,
 					vlan_id, vlan_id_mask,
 					copy_to_cpu, flags);
@@ -4097,7 +4117,7 @@ static int rocker_flow_set_bridge(struct net_device *dev,
 	}
 
 	/* Ignoring eth_dst_mask it seems to cause a EINVAL return code */
-	return rocker_flow_tbl_bridge(rocker_port, flags,
+	return rocker_flow_tbl_bridge(rocker_port, flags, 0,
 				      eth_dst, eth_dst_mask,
 				      vlan_id, tunnel_id,
 				      goto_tbl, group_id, copy_to_cpu);
@@ -4199,7 +4219,7 @@ static int rocker_flow_set_acl(struct net_device *dev,
 		}
 	}
 
-	return rocker_flow_tbl_acl(rocker_port, flags,
+	return rocker_flow_tbl_acl(rocker_port, flags, 0,
 				   in_lport, in_lport_mask,
 				   eth_src, eth_src_mask,
 				   eth_dst, eth_dst_mask, ethtype,
#24 lore raw

PATCH v2 11/12 net: rocker: have flow api calls set cookie value

In reply to John Fastabend
---
 drivers/net/ethernet/rocker/rocker.c |   16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)
drivers/net/ethernet/rocker/rocker.c +11 -5
index 4cbead8..8acd730 100644
@@ -3891,7 +3891,7 @@ static int rocker_flow_set_ig_port(struct net_device *dev,
 	in_lport_mask = flow->matches[0].mask_u32;
 	goto_tbl = rocker_goto_value(flow->actions[0].args[0].value_u16);
 
-	return rocker_flow_tbl_ig_port(rocker_port, flags, 0,
+	return rocker_flow_tbl_ig_port(rocker_port, flags, flow->uid,
 				       in_lport, in_lport_mask,
 				       goto_tbl);
 }
@@ -3948,7 +3948,7 @@ static int rocker_flow_set_vlan(struct net_device *dev,
 		}
 	}
 
-	return rocker_flow_tbl_vlan(rocker_port, flags, 0, in_lport,
+	return rocker_flow_tbl_vlan(rocker_port, flags, flow->uid, in_lport,
 				    vlan_id, vlan_id_mask, goto_tbl,
 				    untagged, new_vlan_id);
 }
@@ -4022,7 +4022,7 @@ static int rocker_flow_set_term_mac(struct net_device *dev,
 		}
 	}
 
-	return rocker_flow_tbl_term_mac(rocker_port, 0,
+	return rocker_flow_tbl_term_mac(rocker_port, flow->uid,
 					in_lport, in_lport_mask,
 					ethtype, eth_dst, eth_dst_mask,
 					vlan_id, vlan_id_mask,
@@ -4117,7 +4117,7 @@ static int rocker_flow_set_bridge(struct net_device *dev,
 	}
 
 	/* Ignoring eth_dst_mask it seems to cause a EINVAL return code */
-	return rocker_flow_tbl_bridge(rocker_port, flags, 0,
+	return rocker_flow_tbl_bridge(rocker_port, flags, flow->uid,
 				      eth_dst, eth_dst_mask,
 				      vlan_id, tunnel_id,
 				      goto_tbl, group_id, copy_to_cpu);
@@ -4219,7 +4219,7 @@ static int rocker_flow_set_acl(struct net_device *dev,
 		}
 	}
 
-	return rocker_flow_tbl_acl(rocker_port, flags, 0,
+	return rocker_flow_tbl_acl(rocker_port, flags, flow->uid,
 				   in_lport, in_lport_mask,
 				   eth_src, eth_src_mask,
 				   eth_dst, eth_dst_mask, ethtype,
@@ -4279,6 +4279,8 @@ static int rocker_flow_set_group_slice_l3_unicast(struct net_device *dev,
 		}
 	}
 
+	entry->cookie = flow->uid;
+
 	return rocker_group_tbl_do(rocker_port, flags, entry);
 }
 
@@ -4330,6 +4332,8 @@ static int rocker_flow_set_group_slice_l2_rewrite(struct net_device *dev,
 		}
 	}
 
+	entry->cookie = flow->uid;
+
 	return rocker_group_tbl_do(rocker_port, flags, entry);
 }
 
@@ -4381,6 +4385,8 @@ static int rocker_flow_set_group_slice_l2(struct net_device *dev,
 		}
 	}
 
+	entry->cookie = flow->uid;
+
 	return rocker_group_tbl_do(rocker_port, flags, entry);
 }
 
#25 lore raw

PATCH v2 12/12 net: rocker: implement delete flow routine

In reply to John Fastabend
---
 drivers/net/ethernet/rocker/rocker.c |   46 +++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)
drivers/net/ethernet/rocker/rocker.c +45 -1
index 8acd730..0b400c4 100644
@@ -4439,7 +4439,51 @@ static int rocker_set_flows(struct net_device *dev,
 static int rocker_del_flows(struct net_device *dev,
 			    struct net_flow_rule *flow)
 {
-	return -EOPNOTSUPP;
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	struct rocker_flow_tbl_entry *entry;
+	struct rocker_group_tbl_entry *group;
+	struct hlist_node *tmp;
+	int bkt, err = -EEXIST;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rocker_port->rocker->flow_tbl_lock, flags);
+	hash_for_each_safe(rocker_port->rocker->flow_tbl,
+			   bkt, tmp, entry, entry) {
+		if (rocker_goto_value(flow->table_id) != entry->key.tbl_id ||
+		    flow->uid != entry->cookie)
+			continue;
+
+		hash_del(&entry->entry);
+		err = 0;
+		break;
+	}
+	spin_unlock_irqrestore(&rocker_port->rocker->flow_tbl_lock, flags);
+
+	if (!err)
+		goto done;
+
+	spin_lock_irqsave(&rocker_port->rocker->group_tbl_lock, flags);
+	hash_for_each_safe(rocker_port->rocker->group_tbl,
+			   bkt, tmp, group, entry) {
+		if (rocker_goto_value(flow->table_id) !=
+			ROCKER_GROUP_TYPE_GET(group->group_id) ||
+		    flow->uid != group->cookie)
+			continue;
+
+		hash_del(&group->entry);
+		err = 0;
+		break;
+	}
+	spin_unlock_irqrestore(&rocker_port->rocker->group_tbl_lock, flags);
+
+done:
+	if (!err) {
+		err = rocker_cmd_exec(rocker_port->rocker, rocker_port,
+				      rocker_cmd_flow_tbl_del,
+				      entry, NULL, NULL, true);
+		kfree(entry);
+	}
+	return err;
 }
 #endif
 
#27 lore raw
Quoted 5 lines · Or Gerlitz
Quoted 2 lines · John Fastabend

I tried to roll in all the feedback from v1 into this series annotated here,

Quoted 9 lines

Hi John,

Can you please drop V2 to your
https://github.com/jrfastab/rocker-net-next.git tree?

thanks,

Or.

Should be there now, but as Alexei noted I seem to have missed updating the patch commit messages so they still reference the old names.

Signature · 1 line
John Fastabend         Intel Corporation
#28 lore raw
Quoted 5 lines · John Fastabend
Quoted 4 lines · Or Gerlitz
Quoted 1 line · John Fastabend
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I tried to roll in all the feedback from v1 into this series annotated here,

Can you please drop V2 to your
https://github.com/jrfastab/rocker-net-next.git tree?

Should be there now, but as Alexei noted I seem to have missed updating the patch commit messages so they still reference the old names.

Oh, I see it now.. yep, with the old names and dates to Dec 30th...