[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1411134590-4586-7-git-send-email-jiri@resnulli.us>
Date: Fri, 19 Sep 2014 15:49:47 +0200
From: Jiri Pirko <jiri@...nulli.us>
To: netdev@...r.kernel.org
Cc: davem@...emloft.net, nhorman@...driver.com, andy@...yhouse.net,
tgraf@...g.ch, dborkman@...hat.com, ogerlitz@...lanox.com,
jesse@...ira.com, pshelar@...ira.com, azhou@...ira.com,
ben@...adent.org.uk, stephen@...workplumber.org,
jeffrey.t.kirsher@...el.com, vyasevic@...hat.com,
xiyou.wangcong@...il.com, john.r.fastabend@...el.com,
edumazet@...gle.com, jhs@...atatu.com, sfeldma@...ulusnetworks.com,
f.fainelli@...il.com, roopa@...ulusnetworks.com,
linville@...driver.com, dev@...nvswitch.org, jasowang@...hat.com,
ebiederm@...ssion.com, nicolas.dichtel@...nd.com,
ryazanov.s.a@...il.com, buytenh@...tstofly.org,
aviadr@...lanox.com, nbd@...nwrt.org, alexei.starovoitov@...il.com,
Neil.Jerram@...aswitch.com, ronye@...lanox.com,
simon.horman@...ronome.com, alexander.h.duyck@...el.com
Subject: [patch net-next v2 6/9] switchdev: add basic support for flow matching and actions
This patch adds basic support for flows. The infrastructure is prepared
to easily add another flow matching types. So far, only the key one is
implemented.
Signed-off-by: Jiri Pirko <jiri@...nulli.us>
---
include/linux/netdevice.h | 16 ++++++
include/net/switchdev.h | 113 ++++++++++++++++++++++++++++++++++++++++++
net/switchdev/switchdev.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 252 insertions(+)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b290dcf..034baca 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1005,6 +1005,18 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* Called to get an ID of the switch chip this port is part of.
* If driver implements this, it indicates that it represents a port
* of a switch chip.
+ *
+ * int (*ndo_swdev_flow_insert)(struct net_device *dev,
+ * const struct swdev_flow *flow);
+ * Called to insert a flow into switch device. If driver does
+ * not implement this, it is assumed that the hw does not have
+ * a capability to work with flows.
+ *
+ * int (*ndo_swdev_flow_remove)(struct net_device *dev,
+ * const struct swdev_flow *flow);
+ * Called to remove a flow from switch device. If driver does
+ * not implement this, it is assumed that the hw does not have
+ * a capability to work with flows.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
@@ -1157,6 +1169,10 @@ struct net_device_ops {
#ifdef CONFIG_NET_SWITCHDEV
int (*ndo_swdev_id_get)(struct net_device *dev,
struct netdev_phys_item_id *psid);
+ int (*ndo_swdev_flow_insert)(struct net_device *dev,
+ const struct swdev_flow *flow);
+ int (*ndo_swdev_flow_remove)(struct net_device *dev,
+ const struct swdev_flow *flow);
#endif
};
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index af30f75..060d3fc 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -12,9 +12,110 @@
#include <linux/netdevice.h>
+struct swdev_flow_match_key {
+ struct {
+ u32 priority; /* Packet QoS priority. */
+ u32 in_port_ifindex; /* Input switch port ifindex (or 0). */
+ } phy;
+ struct {
+ u8 src[ETH_ALEN]; /* Ethernet source address. */
+ u8 dst[ETH_ALEN]; /* Ethernet destination address. */
+ __be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
+ __be16 type; /* Ethernet frame type. */
+ } eth;
+ struct {
+ u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */
+ u8 tos; /* IP ToS. */
+ u8 ttl; /* IP TTL/hop limit. */
+ u8 frag; /* One of OVS_FRAG_TYPE_*. */
+ } ip;
+ struct {
+ __be16 src; /* TCP/UDP/SCTP source port. */
+ __be16 dst; /* TCP/UDP/SCTP destination port. */
+ __be16 flags; /* TCP flags. */
+ } tp;
+ union {
+ struct {
+ struct {
+ __be32 src; /* IP source address. */
+ __be32 dst; /* IP destination address. */
+ } addr;
+ struct {
+ u8 sha[ETH_ALEN]; /* ARP source hardware address. */
+ u8 tha[ETH_ALEN]; /* ARP target hardware address. */
+ } arp;
+ } ipv4;
+ struct {
+ struct {
+ struct in6_addr src; /* IPv6 source address. */
+ struct in6_addr dst; /* IPv6 destination address. */
+ } addr;
+ __be32 label; /* IPv6 flow label. */
+ struct {
+ struct in6_addr target; /* ND target address. */
+ u8 sll[ETH_ALEN]; /* ND source link layer address. */
+ u8 tll[ETH_ALEN]; /* ND target link layer address. */
+ } nd;
+ } ipv6;
+ };
+};
+
+enum swdev_flow_match_type {
+ SW_FLOW_MATCH_TYPE_KEY,
+};
+
+struct swdev_flow_match {
+ enum swdev_flow_match_type type;
+ union {
+ struct {
+ struct swdev_flow_match_key key;
+ struct swdev_flow_match_key key_mask;
+ };
+ };
+};
+
+enum swdev_flow_action_type {
+ SW_FLOW_ACTION_TYPE_OUTPUT,
+ SW_FLOW_ACTION_TYPE_VLAN_PUSH,
+ SW_FLOW_ACTION_TYPE_VLAN_POP,
+};
+
+struct swdev_flow_action {
+ enum swdev_flow_action_type type;
+ union {
+ u32 out_port_ifindex;
+ struct {
+ __be16 proto;
+ __be16 tci;
+ } vlan;
+ };
+};
+
+struct swdev_flow {
+ struct swdev_flow_match match;
+ unsigned action_count;
+ struct swdev_flow_action action[0];
+};
+
+static inline struct swdev_flow *swdev_flow_alloc(unsigned action_count,
+ gfp_t flags)
+{
+ struct swdev_flow *flow;
+
+ flow = kzalloc(sizeof(struct swdev_flow) +
+ sizeof(struct swdev_flow_action) * action_count,
+ flags);
+ if (!flow)
+ return NULL;
+ flow->action_count = action_count;
+ return flow;
+}
+
#ifdef CONFIG_NET_SWITCHDEV
int swdev_id_get(struct net_device *dev, struct netdev_phys_item_id *psid);
+int swdev_flow_insert(struct net_device *dev, const struct swdev_flow *flow);
+int swdev_flow_remove(struct net_device *dev, const struct swdev_flow *flow);
#else
@@ -24,6 +125,18 @@ static inline int swdev_id_get(struct net_device *dev,
return -EOPNOTSUPP;
}
+static inline int swdev_flow_insert(struct net_device *dev,
+ const struct swdev_flow *flow)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int swdev_flow_remove(struct net_device *dev,
+ const struct swdev_flow *flow)
+{
+ return -EOPNOTSUPP;
+}
+
#endif
#endif /* _LINUX_SWITCHDEV_H_ */
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 14a5fc9..90bc5e4 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -30,3 +30,126 @@ int swdev_id_get(struct net_device *dev, struct netdev_phys_item_id *psid)
return ops->ndo_swdev_id_get(dev, psid);
}
EXPORT_SYMBOL(swdev_id_get);
+
+static void print_flow_key_phy(const char *prefix,
+ const struct swdev_flow_match_key *key)
+{
+ pr_debug("%s phy { prio %08x, in_port_ifindex %08x }\n",
+ prefix,
+ key->phy.priority, key->phy.in_port_ifindex);
+}
+
+static void print_flow_key_eth(const char *prefix,
+ const struct swdev_flow_match_key *key)
+{
+ pr_debug("%s eth { sm %pM, dm %pM, tci %04x, type %04x }\n",
+ prefix,
+ key->eth.src, key->eth.dst, ntohs(key->eth.tci),
+ ntohs(key->eth.type));
+}
+
+static void print_flow_key_ip(const char *prefix,
+ const struct swdev_flow_match_key *key)
+{
+ pr_debug("%s ip { proto %02x, tos %02x, ttl %02x }\n",
+ prefix,
+ key->ip.proto, key->ip.tos, key->ip.ttl);
+}
+
+static void print_flow_key_ipv4(const char *prefix,
+ const struct swdev_flow_match_key *key)
+{
+ pr_debug("%s ipv4 { si %pI4, di %pI4, sm %pM, dm %pM }\n",
+ prefix,
+ &key->ipv4.addr.src, &key->ipv4.addr.dst,
+ key->ipv4.arp.sha, key->ipv4.arp.tha);
+}
+
+static void print_flow_actions(const struct swdev_flow_action *action,
+ unsigned action_count)
+{
+ int i;
+
+ pr_debug(" actions:\n");
+ for (i = 0; i < action_count; i++) {
+ switch (action->type) {
+ case SW_FLOW_ACTION_TYPE_OUTPUT:
+ pr_debug(" output { ifindex %u }\n",
+ action->out_port_ifindex);
+ break;
+ case SW_FLOW_ACTION_TYPE_VLAN_PUSH:
+ pr_debug(" vlan push { proto %04x, tci %04x }\n",
+ ntohs(action->vlan.proto),
+ ntohs(action->vlan.tci));
+ break;
+ case SW_FLOW_ACTION_TYPE_VLAN_POP:
+ pr_debug(" vlan pop\n");
+ break;
+ }
+ action++;
+ }
+}
+
+#define PREFIX_NONE " "
+#define PREFIX_MASK " mask"
+
+static void print_flow_match(const struct swdev_flow_match *match)
+{
+ switch (match->type) {
+ case SW_FLOW_MATCH_TYPE_KEY:
+ print_flow_key_phy(PREFIX_NONE, &match->key);
+ print_flow_key_phy(PREFIX_MASK, &match->key_mask);
+ print_flow_key_eth(PREFIX_NONE, &match->key);
+ print_flow_key_eth(PREFIX_MASK, &match->key_mask);
+ print_flow_key_ip(PREFIX_NONE, &match->key);
+ print_flow_key_ip(PREFIX_MASK, &match->key_mask);
+ print_flow_key_ipv4(PREFIX_NONE, &match->key);
+ print_flow_key_ipv4(PREFIX_MASK, &match->key_mask);
+ }
+}
+
+static void print_flow(const struct swdev_flow *flow, struct net_device *dev,
+ const char *comment)
+{
+ pr_debug("%s flow %s:\n", dev->name, comment);
+ print_flow_match(&flow->match);
+ print_flow_actions(flow->action, flow->action_count);
+}
+
+/**
+ * swdev_flow_insert - Insert a flow into switch
+ * @dev: port device
+ * @flow: flow descriptor
+ *
+ * Insert a flow into switch this port is part of.
+ */
+int swdev_flow_insert(struct net_device *dev, const struct swdev_flow *flow)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ print_flow(flow, dev, "insert");
+ if (!ops->ndo_swdev_flow_insert)
+ return -EOPNOTSUPP;
+ WARN_ON(!ops->ndo_swdev_id_get);
+ return ops->ndo_swdev_flow_insert(dev, flow);
+}
+EXPORT_SYMBOL(swdev_flow_insert);
+
+/**
+ * swdev_flow_remove - Remove a flow from switch
+ * @dev: port device
+ * @flow: flow descriptor
+ *
+ * Remove a flow from switch this port is part of.
+ */
+int swdev_flow_remove(struct net_device *dev, const struct swdev_flow *flow)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ print_flow(flow, dev, "remove");
+ if (!ops->ndo_swdev_flow_remove)
+ return -EOPNOTSUPP;
+ WARN_ON(!ops->ndo_swdev_id_get);
+ return ops->ndo_swdev_flow_remove(dev, flow);
+}
+EXPORT_SYMBOL(swdev_flow_remove);
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists