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 <j...@resnulli.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

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to