switchdev API is designed to allow kernel support for various switch
chips.

It is the responsibility of a driver to create netdevice instances which
represents every port and for the switch master itself. Driver uses
swdev_register and swportdev_register functions to make the core aware
of the fact these netdevices are representing switch and switch ports.

Signed-off-by: Jiri Pirko <j...@resnulli.us>
---
 include/linux/netdevice.h |  24 +++++++
 include/linux/switchdev.h |  30 ++++++++
 net/Kconfig               |  10 +++
 net/core/Makefile         |   1 +
 net/core/switchdev.c      | 172 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 237 insertions(+)
 create mode 100644 include/linux/switchdev.h
 create mode 100644 net/core/switchdev.c

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 6b70e6f..5ad80da 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -49,6 +49,8 @@
 
 #include <linux/netdev_features.h>
 #include <linux/neighbour.h>
+#include <linux/sw_flow.h>
+
 #include <uapi/linux/netdevice.h>
 
 struct netpoll_info;
@@ -998,6 +1000,18 @@ typedef u16 (*select_queue_fallback_t)(struct net_device 
*dev,
  *     Callback to use for xmit over the accelerated station. This
  *     is used in place of ndo_start_xmit on accelerated net
  *     devices.
+ *
+ * int (*ndo_swdev_flow_insert)(struct net_device *dev,
+ *                             struct sw_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,
+ *                             struct sw_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);
@@ -1145,6 +1159,12 @@ struct net_device_ops {
        netdev_tx_t             (*ndo_dfwd_start_xmit) (struct sk_buff *skb,
                                                        struct net_device *dev,
                                                        void *priv);
+#if IS_ENABLED(CONFIG_NET_SWITCHDEV)
+       int                     (*ndo_swdev_flow_insert)(struct net_device *dev,
+                                                        struct sw_flow *flow);
+       int                     (*ndo_swdev_flow_remove)(struct net_device *dev,
+                                                        struct sw_flow *flow);
+#endif
 };
 
 /**
@@ -1205,6 +1225,8 @@ enum netdev_priv_flags {
        IFF_SUPP_NOFCS                  = 1<<19,
        IFF_LIVE_ADDR_CHANGE            = 1<<20,
        IFF_MACVLAN                     = 1<<21,
+       IFF_SWITCH                      = 1<<22,
+       IFF_SWITCH_PORT                 = 1<<23,
 };
 
 #define IFF_802_1Q_VLAN                        IFF_802_1Q_VLAN
@@ -1229,6 +1251,8 @@ enum netdev_priv_flags {
 #define IFF_SUPP_NOFCS                 IFF_SUPP_NOFCS
 #define IFF_LIVE_ADDR_CHANGE           IFF_LIVE_ADDR_CHANGE
 #define IFF_MACVLAN                    IFF_MACVLAN
+#define IFF_SWITCH                     IFF_SWITCH
+#define IFF_SWITCH_PORT                        IFF_SWITCH_PORT
 
 /*
  *     The DEVICE structure.
diff --git a/include/linux/switchdev.h b/include/linux/switchdev.h
new file mode 100644
index 0000000..e4aeaba
--- /dev/null
+++ b/include/linux/switchdev.h
@@ -0,0 +1,30 @@
+/*
+ * include/linux/switchdev.h - Switch device API
+ * Copyright (c) 2014 Jiri Pirko <j...@resnulli.us>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _LINUX_SWITCHDEV_H_
+#define _LINUX_SWITCHDEV_H_
+
+#include <linux/netdevice.h>
+#include <linux/sw_flow.h>
+
+bool swdev_dev_check(const struct net_device *dev);
+int swdev_flow_insert(struct net_device *dev, struct sw_flow *flow);
+int swdev_flow_remove(struct net_device *dev, struct sw_flow *flow);
+int __swdev_register(struct net_device *dev);
+int swdev_register(struct net_device *dev);
+void __swdev_unregister(struct net_device *dev);
+void swdev_unregister(struct net_device *dev);
+
+bool swportdev_dev_check(const struct net_device *dev);
+int __swportdev_register(struct net_device *port_dev, struct net_device *dev);
+int swportdev_register(struct net_device *port_dev, struct net_device *dev);
+void __swportdev_unregister(struct net_device *port_dev);
+void swportdev_unregister(struct net_device *port_dev);
+
+#endif /* _LINUX_SWITCHDEV_H_ */
diff --git a/net/Kconfig b/net/Kconfig
index e411046..e02ab8d 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -285,6 +285,16 @@ config NET_FLOW_LIMIT
          with many clients some protection against DoS by a single (spoofed)
          flow that greatly exceeds average workload.
 
+config NET_SWITCHDEV
+       tristate "Switch device"
+       depends on INET
+       ---help---
+         This module provides glue for hardware switch chips so they can be
+         accessed from userspace via Open vSwitch Netlink API. 
+
+         To compile this code as a module, choose M here: the
+         module will be called pktgen.
+
 menu "Network testing"
 
 config NET_PKTGEN
diff --git a/net/core/Makefile b/net/core/Makefile
index 9628c20..426a619 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o
 obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
 obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
 obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
+obj-$(CONFIG_NET_SWITCHDEV) += switchdev.o
diff --git a/net/core/switchdev.c b/net/core/switchdev.c
new file mode 100644
index 0000000..ccd4fbc
--- /dev/null
+++ b/net/core/switchdev.c
@@ -0,0 +1,172 @@
+/*
+ * net/core/switchdev.c - Switch device API
+ * Copyright (c) 2014 Jiri Pirko <j...@resnulli.us>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/switchdev.h>
+#include <net/rtnetlink.h>
+
+bool swdev_dev_check(const struct net_device *dev)
+{
+       return dev->priv_flags & IFF_SWITCH;
+}
+EXPORT_SYMBOL(swdev_dev_check);
+
+int swdev_flow_insert(struct net_device *dev, struct sw_flow *flow)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+
+       BUG_ON(!swdev_dev_check(dev));
+       if (!ops->ndo_swdev_flow_insert)
+               return -EOPNOTSUPP;
+       return ops->ndo_swdev_flow_insert(dev, flow);
+}
+EXPORT_SYMBOL(swdev_flow_insert);
+
+int swdev_flow_remove(struct net_device *dev, struct sw_flow *flow)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+
+       BUG_ON(!swdev_dev_check(dev));
+       if (!ops->ndo_swdev_flow_remove)
+               return -EOPNOTSUPP;
+       return ops->ndo_swdev_flow_remove(dev, flow);
+}
+EXPORT_SYMBOL(swdev_flow_remove);
+
+int __swdev_register(struct net_device *dev)
+{
+       if (dev->priv_flags & IFF_SWITCH) {
+               netdev_err(dev, "Device is already registered as a switch 
device\n");
+               return -EBUSY;
+       }
+       dev->priv_flags |= IFF_SWITCH;
+       netdev_info(dev, "Switch device registered\n");
+       return 0;
+}
+EXPORT_SYMBOL(__swdev_register);
+
+int swdev_register(struct net_device *dev)
+{
+       int err;
+
+       rtnl_lock();
+       err = __swdev_register(dev);
+       rtnl_unlock();
+       return err;
+}
+EXPORT_SYMBOL(swdev_register);
+
+void __swdev_unregister(struct net_device *dev)
+{
+       dev->priv_flags |= IFF_SWITCH;
+       netdev_info(dev, "Switch device unregistered\n");
+}
+EXPORT_SYMBOL(__swdev_unregister);
+
+void swdev_unregister(struct net_device *dev)
+{
+       rtnl_lock();
+       __swdev_unregister(dev);
+       rtnl_unlock();
+}
+EXPORT_SYMBOL(swdev_unregister);
+
+
+bool swportdev_dev_check(const struct net_device *port_dev)
+{
+       return port_dev->priv_flags & IFF_SWITCH_PORT;
+}
+EXPORT_SYMBOL(swportdev_dev_check);
+
+static rx_handler_result_t swportdev_handle_frame(struct sk_buff **pskb)
+{
+       struct sk_buff *skb = *pskb;
+
+       /* We don't care what comes from port device into rx path.
+        * If there's something there, it is destined to ETH_P_ALL
+        * handlers. So just consume it.
+        */
+       dev_kfree_skb(skb);
+       return RX_HANDLER_CONSUMED;
+}
+
+int __swportdev_register(struct net_device *port_dev, struct net_device *dev)
+{
+       int err;
+
+       if (!(dev->priv_flags & IFF_SWITCH)) {
+               netdev_err(dev, "Device is not a switch device\n");
+               return -EINVAL;
+       }
+       if (port_dev->priv_flags & IFF_SWITCH_PORT) {
+               netdev_err(port_dev, "Device is already registered as a switch 
port\n");
+               return -EBUSY;
+       }
+       err = netdev_master_upper_dev_link(port_dev, dev);
+       if (err) {
+               netdev_err(dev, "Device %s failed to set upper link\n",
+                          port_dev->name);
+               return err;
+       }
+       err = netdev_rx_handler_register(port_dev, swportdev_handle_frame, 
NULL);
+       if (err) {
+               netdev_err(dev, "Device %s failed to register rx_handler\n",
+                          port_dev->name);
+               goto err_handler_register;
+       }
+       port_dev->priv_flags |= IFF_SWITCH_PORT;
+       netdev_info(port_dev, "Switch port device registered\n");
+       return 0;
+
+err_handler_register:
+       netdev_upper_dev_unlink(port_dev, dev);
+       return err;
+}
+EXPORT_SYMBOL(__swportdev_register);
+
+int swportdev_register(struct net_device *port_dev, struct net_device *dev)
+{
+       int err;
+
+       rtnl_lock();
+       err = __swportdev_register(port_dev, dev);
+       rtnl_unlock();
+       return err;
+}
+EXPORT_SYMBOL(swportdev_register);
+
+void __swportdev_unregister(struct net_device *port_dev)
+{
+       struct net_device *dev;
+
+       dev = netdev_master_upper_dev_get(port_dev);
+       BUG_ON(!dev);
+       port_dev->priv_flags &= ~IFF_SWITCH_PORT;
+       netdev_rx_handler_unregister(port_dev);
+       netdev_upper_dev_unlink(port_dev, dev);
+       netdev_info(port_dev, "Switch port device unregistered\n");
+}
+EXPORT_SYMBOL(__swportdev_unregister);
+
+void swportdev_unregister(struct net_device *port_dev)
+{
+       rtnl_lock();
+       __swportdev_unregister(port_dev);
+       rtnl_unlock();
+}
+EXPORT_SYMBOL(swportdev_unregister);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <j...@resnulli.us>");
+MODULE_DESCRIPTION("Switch device API");
-- 
1.8.5.3

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

Reply via email to