Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH 4/4] rhashtable: Add parent argument to mutex_is_held
From: Thomas Graf @ 2014-11-17  6:16 UTC (permalink / raw)
  To: Herbert Xu; +Cc: Josh Triplett, netdev, eric.dumazet, paulmck
In-Reply-To: <20141117044657.GA30723@gondor.apana.org.au>

On 11/17/14 at 12:46pm, Herbert Xu wrote:
> On Sat, Nov 15, 2014 at 06:37:26PM -0800, Josh Triplett wrote:
> > On November 15, 2014 6:22:27 PM PST, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> > >Josh Triplett <josh@joshtriplett.org> wrote:
> > >>
> > >> - Set up the new empty table with the new set of hash parameters.
> > >> - synchronize_rcu().  Readers will now search both old and new
> > >tables.
> > >> - Peel nodes off the ends of the old hash table and add them to the
> > >new
> > >
> > >We currently use a singly linked list in rhashtable.  Peeling nodes
> > >off the end would mean upgrading to a doubly linked list, which is
> > >no different than keeping two lists in terms of cache footprint, no?
> > 
> > No, since each pass just handles one set of nodes from each bucket anyway, you can just do a bit more work in the rehasher instead.
> 
> OK let me see if I could implement something like that in rhashtable.

This sounds great. Thanks Herbert and Josh!

^ permalink raw reply

* Re: [PATCH 4/4] rhashtable: Add parent argument to mutex_is_held
From: Thomas Graf @ 2014-11-17  6:20 UTC (permalink / raw)
  To: Herbert Xu; +Cc: netdev, eric.dumazet, paulmck, josh, David S. Miller
In-Reply-To: <20141115155108.GA21998@gondor.apana.org.au>

On 11/15/14 at 11:51pm, Herbert Xu wrote:
> So I noticed that you got rid of the rehash when you converted
> netlink over to rthashtable.  Was this aspect of the conversion
> discussed anywhere? In particular, how do you protect against
> a malicious user that's trying to attack the netlink hash table?

I looked at the trade off between lockless lookups and rehashing and
figured it makes sense to chose making lookups cheaper as sockets can
be constrained per user. However, given the followup-up discussion it
seems we can get both.

Thomas

^ permalink raw reply

* [PATCH net-next v2] ipvlan: Initial check-in of the IPVLAN driver.
From: Mahesh Bandewar @ 2014-11-17  6:27 UTC (permalink / raw)
  To: netdev
  Cc: Eric Dumazet, Maciej Zenczykowski, Laurent Chavey, Tim Hockin,
	David Miller, Brandon Philips, Pavel Emelianov, Mahesh Bandewar

This driver is very similar to the macvlan driver except that it
uses L3 on the frame to determine the logical interface while
functioning as packet dispatcher. It inherits L2 of the master
device hence the packets on wire will have the same L2 for all
the packets originating from all virtual devices off of the same
master device.

This driver was developed keeping the namespace use-case in
mind. Hence most of the examples given here take that as the
base setup where main-device belongs to the default-ns and
virtual devices are assigned to the additional namespaces.

The device operates in two different modes and the difference
in these two modes in primarily in the TX side.

(a) L2 mode : In this mode, the device behaves as a L2 device.
TX processing upto L2 happens on the stack of the virtual device
associated with (namespace). Packets are switched after that
into the main device (default-ns) and queued for xmit.

RX processing is simple and all multicast, broadcast (if
applicable), and unicast belonging to the address(es) are
delivered to the virtual devices.

(b) L3 mode : In this mode, the device behaves like a L3 device.
TX processing upto L3 happens on the stack of the virtual device
associated with (namespace). Packets are switched to the
main-device (default-ns) for the L2 processing. Hence the routing
table of the default-ns will be used in this mode.

RX processins is somewhat similar to the L2 mode except that in
this mode only Unicast packets are delivered to the virtual device
while main-dev will handle all other packets.

The devices can be added using the "ip" command from the iproute2
package -

	ip link add link <master> <virtual> type ipvlan mode [ l2 | l3 ]

Signed-off-by: Mahesh Bandewar <maheshb@google.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Maciej Żenczykowski <maze@google.com>
Cc: Laurent Chavey <chavey@google.com>
Cc: Tim Hockin <thockin@google.com>
Cc: Brandon Philips <brandon.philips@coreos.com>
Cc: Pavel Emelianov <xemul@parallels.com>
---
 Documentation/networking/ipvlan.txt | 114 +++++
 drivers/net/Kconfig                 |  18 +
 drivers/net/Makefile                |   1 +
 drivers/net/ipvlan/Makefile         |   7 +
 drivers/net/ipvlan/ipvlan.h         | 157 +++++++
 drivers/net/ipvlan/ipvlan_core.c    | 630 +++++++++++++++++++++++++++
 drivers/net/ipvlan/ipvlan_main.c    | 830 ++++++++++++++++++++++++++++++++++++
 drivers/net/ipvlan/ipvlan_sysfs.c   | 119 ++++++
 include/linux/netdevice.h           |   4 +
 include/uapi/linux/if_link.h        |  15 +
 10 files changed, 1895 insertions(+)
 create mode 100644 Documentation/networking/ipvlan.txt
 create mode 100644 drivers/net/ipvlan/Makefile
 create mode 100644 drivers/net/ipvlan/ipvlan.h
 create mode 100644 drivers/net/ipvlan/ipvlan_core.c
 create mode 100644 drivers/net/ipvlan/ipvlan_main.c
 create mode 100644 drivers/net/ipvlan/ipvlan_sysfs.c

diff --git a/Documentation/networking/ipvlan.txt b/Documentation/networking/ipvlan.txt
new file mode 100644
index 000000000000..1f5d34cb4841
--- /dev/null
+++ b/Documentation/networking/ipvlan.txt
@@ -0,0 +1,114 @@
+
+                            IPVLAN Driver HOWTO
+
+Initial Release:
+	Mahesh Bandewar <maheshb AT google.com>
+
+1. Introduction:
+	This is conceptually very similar to the macvlan driver with one major
+exception of using L3 for mux-ing /demux-ing among slaves. This property makes
+the master device share the L2 with it's slave devices. I have developed this
+driver in conjuntion with network namespaces and not sure if there is use case
+outside of it.
+
+
+2. Building and Installation:
+	In order to build the driver, please select the config item CONFIG_IPVLAN.
+The driver can be built into the kernel (CONFIG_IPVLAN=y) or as a module
+(CONFIG_IPVLAN=m).
+
+
+3. Configuration:
+	There are no module parameters for this driver and it can be configured
+using IProute2/ip utility.
+
+	ip link add link <master-dev> <slave-dev> type ipvlan mode { l2 | L3 }
+
+	e.g. ip link add link ipvl0 eth0 type ipvlan mode l2
+
+
+4. Operating modes:
+	IPvlan has two modes of operation - L2 and L3. For a given master device,
+you can select one of these two modes and all slaves on that master will
+operate in the same (selected) mode. The RX mode is almost identical except
+that in L3 mode the slaves wont receive any multicast / broadcast traffic.
+L3 mode is more restrictive since routing is controlled from the other (mostly)
+default namespace.
+
+4.1 L2 mode:
+	In this mode TX processing happens on the stack instance attached to the
+slave device and packets are switched and queued to the master device to send
+out. In this mode the slaves will RX/TX multicast and broadcast (if applicable)
+as well.
+
+4.2 L3 mode:
+	In this mode TX processing upto L3 happens on the stack instance attached
+to the slave device and packets are switched to the stack instance of the
+master device for the L2 processing and routing from that instance will be
+used before packets are queued on the outbound device. In this mode the slaves
+will not receive nor can send multicast / broadcast traffic.
+
+
+5. Sysfs interface:
+	Currently the mode of operation is available at -
+		 /sys/class/net/<master>/ipvlan/mode
+The value can be 0 or 1; where 0 :=> L2, 1 := L3 mode
+
+
+6. What to choose (macvlan vs. ipvlan)?
+	These two devices are very similar in many regards and the specific use
+case could very well define which device to choose. if one of the following
+situations defines your use case then you can choose to use ipvlan -
+	(a) The Linux host that is connected to the external switch / router has
+policy configured that allows only one mac per port.
+	(b) No of virtual devices created on a master exceed the mac capacity and
+puts the NIC in promiscous mode and degraded performance is a concern.
+	(c) If the slave device is to be put into the hostile / untrusted network
+namespace where L2 on the slave could be changed / misused.
+
+
+7. Example configuration:
+
+  +=============================================================+
+  |  Host: host1                                                |
+  |                                                             |
+  |   +----------------------+      +----------------------+    |
+  |   |   NS:ns0             |      |  NS:ns1              |    |
+  |   |                      |      |                      |    |
+  |   |                      |      |                      |    |
+  |   |        ipvl0         |      |         ipvl1        |    |
+  |   +----------#-----------+      +-----------#----------+    |
+  |              #                              #               |
+  |              ################################               |
+  |                              # eth0                         |
+  +==============================#==============================+
+
+
+	(a) Create two network namespaces - ns0, ns1
+		ip netns add ns0
+		ip netns add ns1
+
+	(b) Create two ipvlan slaves on eth0 (master device)
+		ip link add link eth0 ipvl0 type ipvlan mode l2
+		ip link add link eth0 ipvl1 type ipvlan mode l2
+
+	(c) Assign slaves to the respective network namespaces
+		ip link set dev ipvl0 netns ns0
+		ip link set dev ipvl1 netns ns1
+
+	(d) Now switch to the namespace (ns0 or ns1) to configure the slave devices
+		- For ns0
+			(1) ip netns exec ns0 bash
+			(2) ip link set dev ipvl0 up
+			(3) ip link set dev lo up
+			(4) ip -4 addr add 127.0.0.1 dev lo
+			(5) ip -4 addr add $IPADDR dev ipvl0
+			(6) ip -4 route add default via $ROUTER dev ipvl0
+		- For ns1
+			(1) ip netns exec ns1 bash
+			(2) ip link set dev ipvl1 up
+			(3) ip link set dev lo up
+			(4) ip -4 addr add 127.0.0.1 dev lo
+			(5) ip -4 addr add $IPADDR dev ipvl1
+			(6) ip -4 route add default via $ROUTER dev ipvl1
+
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index f9009be3f307..b6d64f546574 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -145,6 +145,24 @@ config MACVTAP
 	  To compile this driver as a module, choose M here: the module
 	  will be called macvtap.
 
+
+config IPVLAN
+    tristate "IP-VLAN support"
+    ---help---
+      This allows one to create virtual devices off of a main interface
+      and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
+      on packets. All interfaces (including the main interface) share L2
+      making it transparent to the connected L2 switch.
+
+      Ipvlan devices can be added using the "ip" command from the
+      iproute2 package starting with the iproute2-X.Y.ZZ release:
+
+      "ip link add link <main-dev> [ NAME ] type ipvlan"
+
+      To compile this driver as a module, choose M here: the module
+      will be called ipvlan.
+
+
 config VXLAN
        tristate "Virtual eXtensible Local Area Network (VXLAN)"
        depends on INET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 61aefdd1e173..e25fdd7d905e 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -6,6 +6,7 @@
 # Networking Core Drivers
 #
 obj-$(CONFIG_BONDING) += bonding/
+obj-$(CONFIG_IPVLAN) += ipvlan/
 obj-$(CONFIG_DUMMY) += dummy.o
 obj-$(CONFIG_EQUALIZER) += eql.o
 obj-$(CONFIG_IFB) += ifb.o
diff --git a/drivers/net/ipvlan/Makefile b/drivers/net/ipvlan/Makefile
new file mode 100644
index 000000000000..2efff4e9bb40
--- /dev/null
+++ b/drivers/net/ipvlan/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Ethernet Ipvlan driver
+#
+
+obj-$(CONFIG_IPVLAN) += ipvlan.o
+
+ipvlan-objs := ipvlan_core.o ipvlan_main.o ipvlan_sysfs.o
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
new file mode 100644
index 000000000000..78bb1ee9bc68
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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 __IPVLAN_H
+#define __IPVLAN_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rculist.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_link.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <net/rtnetlink.h>
+#include <net/gre.h>
+#include <net/route.h>
+#include <net/addrconf.h>
+
+#define IPVLAN_DRV	"ipvlan"
+#define IPV_DRV_VER	"0.1"
+
+#define IPVLAN_HASH_SIZE	(1 << BITS_PER_BYTE)
+#define IPVLAN_HASH_MASK	(IPVLAN_HASH_SIZE - 1)
+
+#define IPVLAN_MAC_FILTER_BITS	8
+#define IPVLAN_MAC_FILTER_SIZE	(1 << IPVLAN_MAC_FILTER_BITS)
+#define IPVLAN_MAC_FILTER_MASK	(IPVLAN_MAC_FILTER_SIZE - 1)
+
+/* Define IPVL_DEBUG and set the appropriate dbg_level for debugging. */
+#ifdef	IPVL_DEBUG
+/*
+ * 1 : non-datapath debugging
+ * 2 : Custom
+ * 3 : function enters and exists.
+ * 4 : printk in data path (be careful!)
+ */
+#define IPVL_DBG_LEVEL 1
+#define ipvlan_dbg(level, msg...)	do { \
+						if (level <= IPVL_DBG_LEVEL) \
+						printk(KERN_DEBUG msg); \
+					} while (0)
+#else
+#define ipvlan_dbg(level, msg...) do { ; } while (0)
+#endif
+
+typedef enum {
+	IPVL_IPV6 = 0,
+	IPVL_ICMPV6,
+	IPVL_IPV4,
+	IPVL_ARP,
+} ipvl_hdr_type;
+
+struct ipvl_pcpu_stats {
+	u64			rx_pkts;
+	u64			rx_bytes;
+	u64			rx_mcast;
+	u64			tx_pkts;
+	u64			tx_bytes;
+	struct u64_stats_sync	syncp;
+	u32			rx_errs;
+	u32			tx_drps;
+};
+
+/* Forward declaration */
+struct ipvl_port;
+
+struct ipvl_dev {
+	struct net_device	*dev;
+	struct list_head	pnode;
+	struct ipvl_port	*port;
+	struct net_device	*phy_dev;
+	struct list_head	addrs;
+	int			ipv4cnt;
+	int			ipv6cnt;
+	struct ipvl_pcpu_stats	*pcpu_stats;
+	DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
+	netdev_features_t	sfeatures;
+	u16			mtu_adj;
+};
+
+struct ipvl_addr {
+	struct ipvl_dev		*master; /* Back pointer to master */
+	union {
+		struct in6_addr	ip6;	 /* IPv6 address on logical interface */
+		struct in_addr	ip4;	 /* IPv4 address on logical interface */
+	} ipu;
+#define ip6addr	ipu.ip6
+#define ip4addr ipu.ip4
+	struct hlist_node	hlnode;  /* Hash-table linkage */
+	struct list_head	anode;   /* logical-interface linkage */
+	struct rcu_head		rcu;
+	ipvl_hdr_type		atype;
+};
+
+struct ipvl_port {
+	struct net_device	*dev;
+	struct hlist_head	hlhead[IPVLAN_HASH_SIZE];
+	struct list_head	ipvlans;
+	struct rcu_head		rcu;
+	int			count;
+	struct kobject		kobj;
+	u16			mode;
+};
+
+static inline struct ipvl_port *ipvlan_port_get_rcu(const struct net_device *d)
+{
+	return rcu_dereference(d->rx_handler_data);
+}
+
+static inline struct ipvl_port *ipvlan_port_get_rtnl(const struct net_device *d)
+{
+	return rtnl_dereference(d->rx_handler_data);
+}
+
+static inline bool ipvlan_dev_master(struct net_device *d)
+{
+	return d->priv_flags & IFF_IPVLAN_MASTER;
+}
+
+static inline bool ipvlan_dev_slave(struct net_device *d)
+{
+	return d->priv_flags & IFF_IPVLAN_SLAVE;
+}
+
+/* ---- Prototype declarations ---- */
+/* ---- ipvlan_main.c ---- */
+void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev);
+void ipvlan_set_port_mode(struct ipvl_port *port, u32 nval);
+
+/* ---- ipvlan_sysfs.c ---- */
+int ipvlan_add_per_master_sysfs_mode(struct ipvl_port *port,
+				     struct net_device *dev);
+void ipvlan_del_per_master_sysfs_mode(struct ipvl_port *port);
+
+/* ---- ipvlan_core.c ---- */
+void ipvlan_init_secret(void);
+unsigned int ipvlan_mac_hash(const unsigned char *addr);
+rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb);
+int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
+void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
+bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6);
+struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
+					const void *iaddr, bool is_v6);
+void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync);
+#endif /* __IPVLAN_H */
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
new file mode 100644
index 000000000000..fc814ff311e5
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -0,0 +1,630 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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 "ipvlan.h"
+
+static u32 ipvlan_jhash_secret;
+
+void ipvlan_init_secret(void)
+{
+	net_get_random_once(&ipvlan_jhash_secret, sizeof(ipvlan_jhash_secret));
+}
+
+static void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
+			    unsigned int len, bool success, bool mcast)
+{
+	if (!ipvlan)
+		return;
+
+	if (likely(success)) {
+		struct ipvl_pcpu_stats *pcptr;
+
+		pcptr = this_cpu_ptr(ipvlan->pcpu_stats);
+		u64_stats_update_begin(&pcptr->syncp);
+		pcptr->rx_pkts++;
+		pcptr->rx_bytes += len;
+		if (mcast)
+			pcptr->rx_mcast++;
+		u64_stats_update_end(&pcptr->syncp);
+	} else {
+		this_cpu_inc(ipvlan->pcpu_stats->rx_errs);
+	}
+}
+
+static u8 ipvlan_get_v6_hash(const void *iaddr)
+{
+	const struct in6_addr *ip6_addr = iaddr;
+
+	return __ipv6_addr_jhash(ip6_addr, ipvlan_jhash_secret)
+	       & IPVLAN_HASH_MASK;
+}
+
+static u8 ipvlan_get_v4_hash(const void *iaddr)
+{
+	const struct in_addr *ip4_addr = iaddr;
+	return jhash_1word(ip4_addr->s_addr, ipvlan_jhash_secret)
+	       & IPVLAN_HASH_MASK;
+}
+
+struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
+					const void *iaddr, bool is_v6)
+{
+	struct ipvl_addr *addr;
+	u8 hash = is_v6 ? ipvlan_get_v6_hash(iaddr) :
+			    ipvlan_get_v4_hash(iaddr);
+
+	hlist_for_each_entry_rcu(addr, &port->hlhead[hash], hlnode) {
+		if (is_v6 && addr->atype == IPVL_IPV6 &&
+			ipv6_addr_equal(&addr->ip6addr, iaddr))
+			return addr;
+		else if (!is_v6 && addr->atype == IPVL_IPV4 &&
+			 addr->ip4addr.s_addr ==
+				((struct in_addr *)iaddr)->s_addr)
+			return addr;
+	}
+	return NULL;
+}
+
+void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr)
+{
+	struct ipvl_port *port = ipvlan->port;
+	u8 hash = (addr->atype == IPVL_IPV6) ?
+		ipvlan_get_v6_hash(&addr->ip6addr) :
+		ipvlan_get_v4_hash(&addr->ip4addr);
+
+	hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
+}
+
+void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync)
+{
+	hlist_del_rcu(&addr->hlnode);
+	if (sync)
+		synchronize_rcu();
+}
+
+bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
+{
+	struct ipvl_port *port = ipvlan->port;
+	struct ipvl_addr *addr;
+
+	list_for_each_entry(addr, &ipvlan->addrs, anode) {
+		if ((is_v6 && addr->atype == IPVL_IPV6 &&
+		     ipv6_addr_equal(&addr->ip6addr, iaddr))
+		   || (!is_v6 && addr->atype == IPVL_IPV4 &&
+		      addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr)
+		  )
+			return true;
+	}
+
+	if (ipvlan_ht_addr_lookup(port, iaddr, is_v6))
+		return true;
+
+	return false;
+}
+
+static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type)
+{
+	void *lyr3h = NULL;
+
+	switch (skb->protocol) {
+	case htons(ETH_P_ARP): {
+		struct arphdr *arph;
+
+		if (unlikely(!pskb_may_pull(skb, sizeof(*arph))))
+			return NULL;
+
+		arph = arp_hdr(skb);
+		*type = IPVL_ARP;
+		lyr3h = arph;
+		break;
+	}
+
+	case htons(ETH_P_IP): {
+		u32 pktlen;
+		struct iphdr *ip4h;
+
+		if (unlikely(!pskb_may_pull(skb, sizeof(*ip4h))))
+			return NULL;
+
+		ip4h = ip_hdr(skb);
+		pktlen = ntohs(ip4h->tot_len);
+		if (ip4h->ihl < 5 || ip4h->version != 4)
+			return NULL;
+		if (skb->len < pktlen || pktlen < (ip4h->ihl * 4))
+			return NULL;
+
+		*type = IPVL_IPV4;
+		lyr3h = ip4h;
+		break;
+	}
+	case htons(ETH_P_IPV6): {
+		struct ipv6hdr *ip6h;
+
+		if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h))))
+			return NULL;
+
+		ip6h = ipv6_hdr(skb);
+		if (ip6h->version != 6)
+			return NULL;
+
+		*type = IPVL_IPV6;
+		lyr3h = ip6h;
+		/* Only Neighbour Solicitation pkts need different treatment */
+		if (ipv6_addr_any(&ip6h->saddr) &&
+		    ip6h->nexthdr == NEXTHDR_ICMP) {
+			/* Get to the ICMPv6 header */
+			*type = IPVL_ICMPV6;
+			lyr3h = ip6h + 1;
+		}
+		break;
+	}
+	default:
+		return NULL;
+	}
+
+	return lyr3h;
+}
+
+unsigned int ipvlan_mac_hash(const unsigned char *addr)
+{
+	u32 hash = jhash_1word(__get_unaligned_cpu32(addr+2),
+			       ipvlan_jhash_secret);
+	return hash & IPVLAN_MAC_FILTER_MASK;
+}
+
+static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb,
+				   const struct ipvl_dev *in_dev, bool local)
+{
+	struct ethhdr *eth = eth_hdr(skb);
+	struct ipvl_dev *ipvlan;
+	struct sk_buff *nskb;
+	unsigned int len;
+	unsigned int mac_hash;
+	int ret;
+
+	/* If it's a PAUSE frame discard it! */
+	if (skb->protocol == htons(ETH_P_PAUSE))
+		return;
+
+	list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+		if (local && (ipvlan == in_dev))
+			continue;
+
+		mac_hash = ipvlan_mac_hash(eth->h_dest);
+		if (!test_bit(mac_hash, ipvlan->mac_filters))
+			continue;
+
+		ret = NET_RX_DROP;
+		len = skb->len + ETH_HLEN;
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (!nskb)
+			goto mcast_acct;
+
+		if (ether_addr_equal(eth->h_dest, ipvlan->phy_dev->broadcast))
+			nskb->pkt_type = PACKET_BROADCAST;
+		else
+			nskb->pkt_type = PACKET_MULTICAST;
+
+		nskb->dev = ipvlan->dev;
+		if (local)
+			ret = dev_forward_skb(ipvlan->dev, nskb);
+		else
+			ret = netif_rx(nskb);
+mcast_acct:
+		ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true);
+	}
+
+	/* Locally generated? ...Forward a copy to the main-device as
+	 * well. On the RX side we'll ignore it (wont give it to any
+	 * of the virtual devices.
+	 */
+	if (local) {
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			if (ether_addr_equal(eth->h_dest, port->dev->broadcast))
+				nskb->pkt_type = PACKET_BROADCAST;
+			else
+				nskb->pkt_type = PACKET_MULTICAST;
+
+			dev_forward_skb(port->dev, nskb);
+		}
+	}
+}
+
+static int ipvlan_rcv_frame(struct ipvl_addr *addr, struct sk_buff *skb,
+			    bool local)
+{
+	struct ipvl_dev *ipvlan = addr->master;
+	struct net_device *dev = ipvlan->dev;
+	unsigned int len;
+	rx_handler_result_t ret = RX_HANDLER_CONSUMED;
+	bool success = false;
+
+	len = skb->len + ETH_HLEN;
+	if (unlikely(!(dev->flags & IFF_UP))) {
+		kfree_skb(skb);
+		goto out;
+	}
+
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (!skb)
+		goto out;
+
+	skb->dev = dev;
+	skb->pkt_type = PACKET_HOST;
+
+	if (local) {
+		if (dev_forward_skb(ipvlan->dev, skb) == NET_RX_SUCCESS)
+			success = true;
+	} else {
+		ret = RX_HANDLER_ANOTHER;
+		success = true;
+	}
+
+out:
+	ipvlan_count_rx(ipvlan, len, success, false);
+	return ret;
+}
+
+static struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port,
+					    void *lyr3h, int addr_type,
+					    bool use_dest)
+{
+	struct ipvl_addr *addr = NULL;
+
+	if (addr_type == IPVL_IPV6) {
+		struct ipv6hdr *ip6h;
+		struct in6_addr *i6addr;
+
+		ip6h = (struct ipv6hdr *)lyr3h;
+		i6addr = use_dest ? &ip6h->daddr : &ip6h->saddr;
+		addr = ipvlan_ht_addr_lookup(port, i6addr, true);
+	} else if (addr_type == IPVL_ICMPV6) {
+		struct nd_msg *ndmh;
+		struct in6_addr *i6addr;
+		ndmh = (struct nd_msg *)lyr3h;
+
+		/* Make sure that the NeighborSolicitation ICMPv6 packets
+		 * are handled to avoid DAD issue.
+		 */
+		if (ndmh->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
+			/* Reach the target address */
+			i6addr = &ndmh->target;
+			addr = ipvlan_ht_addr_lookup(port, i6addr, true);
+		}
+	} else if (addr_type == IPVL_IPV4) {
+		struct iphdr *ip4h;
+		__be32 *i4addr;
+
+		ip4h = (struct iphdr *)lyr3h;
+		i4addr = use_dest ? &ip4h->daddr : &ip4h->saddr;
+		addr = ipvlan_ht_addr_lookup(port, i4addr, false);
+	} else if (addr_type == IPVL_ARP) {
+		struct arphdr *arph;
+		unsigned char *arp_ptr;
+		__be32 dip;
+
+		arph = (struct arphdr *)lyr3h;
+		arp_ptr = (unsigned char *)(arph + 1);
+		if (use_dest)
+			/* Skip 2 L2 headers + 1 src L3 (IPv4) header */
+			arp_ptr += (2 * port->dev->addr_len) + 4;
+		else
+			/* Skip L2 header to get to src L3 (IPv4) */
+			arp_ptr += port->dev->addr_len;
+
+		memcpy(&dip, arp_ptr, 4); /* Get the dst IPv4 */
+		addr = ipvlan_ht_addr_lookup(port, &dip, false);
+	}
+
+	return addr;
+}
+
+static int ipvlan_process_v4_outbound(struct sk_buff *skb)
+{
+	const struct iphdr *ip4h = ip_hdr(skb);
+	struct net_device *dev = skb->dev;
+	struct rtable *rt;
+	int err, ret = NET_XMIT_DROP;
+	struct flowi4 fl4 = {
+		.flowi4_oif = dev->iflink,
+		.flowi4_tos = RT_TOS(ip4h->tos),
+		.flowi4_flags = FLOWI_FLAG_ANYSRC,
+		.daddr = ip4h->daddr,
+		.saddr = ip4h->saddr,
+	};
+
+	rt = ip_route_output_flow(dev_net(dev), &fl4, NULL);
+	if (IS_ERR(rt))
+		goto err;
+
+	if (rt->rt_type != RTN_UNICAST && rt->rt_type != RTN_LOCAL) {
+		ip_rt_put(rt);
+		goto err;
+	}
+	skb_dst_drop(skb);
+	skb_dst_set(skb, &rt->dst);
+	err = ip_local_out(skb);
+	if (unlikely(net_xmit_eval(err)))
+		dev->stats.tx_errors++;
+	else
+		ret = NET_XMIT_SUCCESS;
+	goto out;
+err:
+	dev->stats.tx_errors++;
+	kfree_skb(skb);
+out:
+	return ret;
+}
+
+static int ipvlan_process_v6_outbound(struct sk_buff *skb)
+{
+	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
+	struct net_device *dev = skb->dev;
+	struct dst_entry *dst;
+	int err, ret = NET_XMIT_DROP;
+	struct flowi6 fl6 = {
+		.flowi6_iif = skb->dev->ifindex,
+		.daddr = ip6h->daddr,
+		.saddr = ip6h->saddr,
+		.flowi6_flags = FLOWI_FLAG_ANYSRC,
+		.flowlabel = ip6_flowinfo(ip6h),
+		.flowi6_mark = skb->mark,
+		.flowi6_proto = ip6h->nexthdr,
+	};
+
+	dst = ip6_route_output(dev_net(dev), NULL, &fl6);
+	if (IS_ERR(dst))
+		goto err;
+
+	skb_dst_drop(skb);
+	skb_dst_set(skb, dst);
+	err = ip6_local_out(skb);
+	if (unlikely(net_xmit_eval(err)))
+		dev->stats.tx_errors++;
+	else
+		ret = NET_XMIT_SUCCESS;
+	goto out;
+err:
+	dev->stats.tx_errors++;
+	kfree_skb(skb);
+out:
+	return ret;
+}
+
+static int ipvlan_process_outbound(struct sk_buff *skb,
+				   const struct ipvl_dev *ipvlan)
+{
+	struct ethhdr *ethh = eth_hdr(skb);
+	int ret = NET_XMIT_DROP;
+
+	/* In this mode we dont care about multicast and broadcast traffic */
+	if (is_multicast_ether_addr(ethh->h_dest)) {
+		pr_warn_ratelimited("Dropped {multi|broad}cast of type= [%x]\n",
+				    ntohs(skb->protocol));
+		kfree_skb(skb);
+		goto out;
+	}
+
+	/* The ipvlan is a pseudo-L2 device, so the packets that we receive
+	 * will have L2; which need to discarded and processed further
+	 * in the net-ns of the main-device.
+	 */
+	if (skb_mac_header_was_set(skb)) {
+		skb_pull(skb, sizeof(*ethh));
+		skb->mac_header = (typeof(skb->mac_header))~0U;
+		skb_reset_network_header(skb);
+	}
+
+	if (skb->protocol == htons(ETH_P_IPV6))
+		ret = ipvlan_process_v6_outbound(skb);
+	else if (skb->protocol == htons(ETH_P_IP))
+		ret = ipvlan_process_v4_outbound(skb);
+	else {
+		pr_warn_ratelimited("Dropped outbound packet type=%x\n",
+				    ntohs(skb->protocol));
+		kfree_skb(skb);
+	}
+out:
+	return ret;
+}
+
+static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev)
+{
+	const struct ipvl_dev *ipvlan = netdev_priv(dev);
+	void *lyr3h;
+	struct ipvl_addr *addr;
+	int addr_type;
+
+	ipvlan_dbg(4, "L3:Xmit on dev %s,PROT=%x\n", dev->name,
+		   ntohs(skb->protocol));
+	lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+	if (!lyr3h)
+		goto out;
+
+	addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
+	if (addr)
+		return ipvlan_rcv_frame(addr, skb, true);
+
+out:
+	/* Send it out */
+	skb->dev = ipvlan->phy_dev;
+	return ipvlan_process_outbound(skb, ipvlan);
+}
+
+static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
+{
+	const struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct ethhdr *eth = eth_hdr(skb);
+	struct ipvl_addr *addr;
+	void *lyr3h;
+	int addr_type;
+
+	ipvlan_dbg(4, "L2:Xmit on dev %s,PROT=%x\n", dev->name,
+		   ntohs(skb->protocol));
+	if (ether_addr_equal(eth->h_dest, eth->h_source)) {
+		ipvlan_dbg(4, "Comm betn 2 virt devs PROT=%x\n",
+			   ntohs(skb->protocol));
+		if ((lyr3h = ipvlan_get_L3_hdr(skb, &addr_type)) == NULL)
+			goto to_default;
+
+		addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
+		if (addr)
+			return ipvlan_rcv_frame(addr, skb, true);
+
+		/* No matching ipvlan dev! Must be on the Physical device */
+to_default:
+		skb = skb_share_check(skb, GFP_ATOMIC);
+		if (!skb)
+			return NET_XMIT_DROP;
+
+		/* Packet definitely does not belong to any of the
+		 * virtual devices, but the dest is local. So forward
+		 * the skb for the main-dev. At the RX side we just return
+		 * RX_PASS for it to be processed further on the stack.
+		 */
+		return dev_forward_skb(ipvlan->phy_dev, skb);
+
+	} else if (is_multicast_ether_addr(eth->h_dest)) {
+		u8 ip_summed = skb->ip_summed;
+		/* Packet needs to be multicast-ed. */
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		ipvlan_dbg(4, "%s[%d] Mcast Xmit on [%s], PROT=[%x]\n",
+			   __func__, __LINE__, dev->name,
+			   ntohs(skb->protocol));
+		ipvlan_multicast_frame(ipvlan->port, skb, ipvlan, true);
+		skb->ip_summed = ip_summed;
+	}
+
+	/* Send it out */
+	skb->dev = ipvlan->phy_dev;
+	return dev_queue_xmit(skb);
+}
+
+int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct ipvl_port *port = ipvlan_port_get_rcu(ipvlan->phy_dev);
+
+	if (!port)
+		goto out;
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
+		goto out;
+
+	switch(port->mode) {
+	case IPVLAN_MODE_L2:
+		return ipvlan_xmit_mode_l2(skb, dev);
+	case IPVLAN_MODE_L3:
+		return ipvlan_xmit_mode_l3(skb, dev);
+	}
+
+	/* Should not reach here */
+	BUG();
+out:
+	kfree_skb(skb);
+	return NET_XMIT_DROP;
+}
+
+static bool ipvlan_external_frame(struct sk_buff *skb, struct ipvl_port *port)
+{
+	struct ethhdr *eth = eth_hdr(skb);
+	struct ipvl_addr *addr;
+	void *lyr3h;
+	int addr_type;
+
+	if (ether_addr_equal(eth->h_source, skb->dev->dev_addr)) {
+		if ((lyr3h = ipvlan_get_L3_hdr(skb, &addr_type)) == NULL)
+			return true;
+
+		addr = ipvlan_addr_lookup(port, lyr3h, addr_type, false);
+		if (addr)
+			return false;
+	}
+
+	return true;
+}
+
+static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb,
+						 struct ipvl_port *port)
+{
+	void *lyr3h;
+	int addr_type;
+	struct ipvl_addr *addr;
+	struct sk_buff *skb = *pskb;
+	rx_handler_result_t ret = RX_HANDLER_PASS;
+
+	lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+	if (!lyr3h)
+		goto out;
+
+	addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
+	if (addr) {
+		ipvlan_dbg(4, "%s[%d]L3:Ucast Recv for [%s], PROT=[%x]\n",
+			   __func__, __LINE__, addr->master->dev->name,
+			   ntohs(skb->protocol));
+		ret = ipvlan_rcv_frame(addr, skb, false);
+	}
+out:
+	return ret;
+}
+
+static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb,
+						 struct ipvl_port *port)
+{
+	struct sk_buff *skb = *pskb;
+	struct ethhdr *eth = eth_hdr(skb);
+	rx_handler_result_t ret = RX_HANDLER_PASS;
+	void *lyr3h;
+	int addr_type;
+
+	/* First Handle broadcast/multicast frames */
+	if (is_multicast_ether_addr(eth->h_dest)) {
+		/* Pass to virtual devs only if they haven't seen the frame. */
+		if (ipvlan_external_frame(skb, port)) {
+			ipvlan_dbg(4, "%s[%d]L2:Mcast Recv:[%s], PROT=[%x]\n",
+				   __func__, __LINE__, port->dev->name,
+				   ntohs(skb->protocol));
+			ipvlan_multicast_frame(port, skb, NULL, false);
+		}
+	} else if ((lyr3h = ipvlan_get_L3_hdr(skb, &addr_type)) != NULL) {
+		struct ipvl_addr *addr;
+
+		addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
+		if (addr) {
+			ipvlan_dbg(4, "%s[%d]L2:Ucast Recv:[%s], PROT=[%x]\n",
+				   __func__, __LINE__, addr->master->dev->name,
+				   ntohs(skb->protocol));
+			ret = ipvlan_rcv_frame(addr, skb, false);
+		}
+	}
+
+	return ret;
+}
+
+rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
+{
+	struct sk_buff *skb = *pskb;
+	struct ipvl_port *port = ipvlan_port_get_rcu(skb->dev);
+
+	if (!port)
+		goto out;
+
+	switch (port->mode) {
+	case IPVLAN_MODE_L2:
+		return ipvlan_handle_mode_l2(pskb, port);
+	case IPVLAN_MODE_L3:
+		return ipvlan_handle_mode_l3(pskb, port);
+	}
+
+	/* Should not reach here */
+	BUG();
+out:
+	return RX_HANDLER_PASS;
+}
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
new file mode 100644
index 000000000000..e81b3ed79fdf
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -0,0 +1,830 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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 "ipvlan.h"
+
+void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev)
+{
+	ipvlan->dev->mtu = dev->mtu - ipvlan->mtu_adj;
+}
+
+void ipvlan_set_port_mode(struct ipvl_port *port, u32 nval)
+{
+	struct ipvl_dev *ipvlan;
+
+	if (port->mode != nval) {
+		list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+			if (nval == IPVLAN_MODE_L3)
+				ipvlan->dev->flags |= IFF_NOARP;
+			else
+				ipvlan->dev->flags &= ~IFF_NOARP;
+		}
+		port->mode = nval;
+	}
+}
+
+static int ipvlan_port_create(struct net_device *dev)
+{
+	struct ipvl_port *port;
+	int err, idx;
+
+	ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+	if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) {
+		pr_warn("%s[%d]: Returning -EINVAL...\n",
+			__func__, __LINE__);
+		return -EINVAL;
+	}
+	if ((port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL)) == NULL) {
+		pr_warn("%s[%d]: Returning -ENOMEM...\n",
+			__func__, __LINE__);
+		return -ENOMEM;
+	}
+	port->dev = dev;
+	port->mode = IPVLAN_MODE_L3;
+	INIT_LIST_HEAD(&port->ipvlans);
+	for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++)
+		INIT_HLIST_HEAD(&port->hlhead[idx]);
+
+	err = ipvlan_add_per_master_sysfs_mode(port, dev);
+	if (err)
+		goto err;
+
+	err = netdev_rx_handler_register(dev, ipvlan_handle_frame, port);
+	if (err)
+		goto err;
+
+	dev->priv_flags |= IFF_IPVLAN_MASTER;
+	ipvlan_dbg(3, "%s[%d]: Returning (%d)...\n", __func__, __LINE__, err);
+	return 0;
+
+err:
+	kfree_rcu(port, rcu);
+	return err;
+}
+
+static void ipvlan_port_destroy(struct net_device *dev)
+{
+	struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
+
+	dev->priv_flags &= ~IFF_IPVLAN_MASTER;
+	ipvlan_del_per_master_sysfs_mode(port);
+	netdev_rx_handler_unregister(dev);
+	kfree_rcu(port, rcu);
+}
+
+/* ipvlan network devices have devices nesting below it and are a special
+ * "super class" of normal network devices; split their locks off into a
+ * separate class since they always nest.
+ */
+static struct lock_class_key ipvlan_netdev_xmit_lock_key;
+static struct lock_class_key ipvlan_netdev_addr_lock_key;
+
+#define IPVLAN_FEATURES \
+	(NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+	 NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \
+	 NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \
+	 NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)
+
+#define IPVLAN_STATE_MASK \
+	((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT))
+
+static void ipvlan_set_lockdep_class_one(struct net_device *dev,
+					 struct netdev_queue *txq,
+					 void *_unused)
+{
+	lockdep_set_class(&txq->_xmit_lock, &ipvlan_netdev_xmit_lock_key);
+}
+
+static void ipvlan_set_lockdep_class(struct net_device *dev)
+{
+	lockdep_set_class(&dev->addr_list_lock, &ipvlan_netdev_addr_lock_key);
+	netdev_for_each_tx_queue(dev, ipvlan_set_lockdep_class_one, NULL);
+}
+
+/* ---- IPVLAN Netdev Ops ---- */
+static int ipvlan_init(struct net_device *dev)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	const struct net_device *phy_dev = ipvlan->phy_dev;
+
+	dev->state = (dev->state & ~IPVLAN_STATE_MASK) |
+		     (phy_dev->state & IPVLAN_STATE_MASK);
+	dev->features = phy_dev->features & IPVLAN_FEATURES;
+	dev->features |= NETIF_F_LLTX;
+	dev->gso_max_size = phy_dev->gso_max_size;
+	dev->iflink = phy_dev->ifindex;
+	dev->hard_header_len = phy_dev->hard_header_len;
+
+	ipvlan_set_lockdep_class(dev);
+
+	ipvlan->pcpu_stats = alloc_percpu(struct ipvl_pcpu_stats);
+	if (!ipvlan->pcpu_stats)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void ipvlan_uninit(struct net_device *dev)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct ipvl_port *port = ipvlan->port;
+
+	if (ipvlan->pcpu_stats)
+		free_percpu(ipvlan->pcpu_stats);
+
+	port->count -= 1;
+	if (!port->count)
+		ipvlan_port_destroy(port->dev);
+}
+
+static int ipvlan_open(struct net_device *dev)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct net_device *phy_dev = ipvlan->phy_dev;
+	struct ipvl_addr *addr;
+
+	if (ipvlan->port->mode == IPVLAN_MODE_L3)
+		dev->flags |= IFF_NOARP;
+	else
+		dev->flags &= ~IFF_NOARP;
+
+	if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
+		list_for_each_entry(addr, &ipvlan->addrs, anode) {
+			ipvlan_ht_addr_add(ipvlan, addr);
+		}
+	}
+	return dev_uc_add(phy_dev, phy_dev->dev_addr);
+}
+
+static int ipvlan_stop(struct net_device *dev)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct net_device *phy_dev = ipvlan->phy_dev;
+	struct ipvl_addr *addr;
+
+	dev_uc_unsync(phy_dev, dev);
+	dev_mc_unsync(phy_dev, dev);
+
+	dev_uc_del(phy_dev, phy_dev->dev_addr);
+
+	if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
+		list_for_each_entry(addr, &ipvlan->addrs, anode) {
+			ipvlan_ht_addr_del(addr, !dev->dismantle);
+		}
+	}
+	return 0;
+}
+
+netdev_tx_t ipvlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	const struct ipvl_dev *ipvlan = netdev_priv(dev);
+	int skblen = skb->len;
+	int ret;
+
+	ret = ipvlan_queue_xmit(skb, dev);
+	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
+		struct ipvl_pcpu_stats *pcptr;
+
+		pcptr = this_cpu_ptr(ipvlan->pcpu_stats);
+
+		u64_stats_update_begin(&pcptr->syncp);
+		pcptr->tx_pkts++;
+		pcptr->tx_bytes += skblen;
+		u64_stats_update_end(&pcptr->syncp);
+	} else {
+		this_cpu_inc(ipvlan->pcpu_stats->tx_drps);
+	}
+	return ret;
+}
+
+static netdev_features_t ipvlan_fix_features(struct net_device *dev,
+					     netdev_features_t features)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	return features & (ipvlan->sfeatures | ~IPVLAN_FEATURES);
+}
+
+static void ipvlan_change_rx_flags(struct net_device *dev, int change)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct net_device *phy_dev = ipvlan->phy_dev;
+
+	if (change & IFF_ALLMULTI)
+		dev_set_allmulti(phy_dev, dev->flags & IFF_ALLMULTI? 1 : -1);
+}
+
+static void ipvlan_set_broadcast_mac_filter(struct ipvl_dev *ipvlan, bool set)
+{
+	struct net_device *dev = ipvlan->dev;
+	unsigned int hashbit = ipvlan_mac_hash(dev->broadcast);
+
+	if (set && !test_bit(hashbit, ipvlan->mac_filters)) {
+		/* Set broadcast hash-bit (for IPv4) */
+		__set_bit(hashbit, ipvlan->mac_filters);
+	} else if (!set && test_bit(hashbit, ipvlan->mac_filters)) {
+		/* Reset broadcast hash-bit */
+		__clear_bit(hashbit, ipvlan->mac_filters);
+	}
+}
+
+static void ipvlan_set_multicast_mac_filter(struct net_device *dev)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+	if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+		bitmap_fill(ipvlan->mac_filters, IPVLAN_MAC_FILTER_SIZE);
+	} else {
+		struct netdev_hw_addr *ha;
+		DECLARE_BITMAP(mc_filters, IPVLAN_MAC_FILTER_SIZE);
+
+		bitmap_zero(mc_filters, IPVLAN_MAC_FILTER_SIZE);
+		netdev_for_each_mc_addr(ha, dev) {
+			__set_bit(ipvlan_mac_hash(ha->addr), mc_filters);
+		}
+		bitmap_copy(ipvlan->mac_filters, mc_filters,
+			    IPVLAN_MAC_FILTER_SIZE);
+	}
+	dev_uc_sync(ipvlan->phy_dev, dev);
+	dev_mc_sync(ipvlan->phy_dev, dev);
+}
+
+static struct rtnl_link_stats64 *ipvlan_get_stats64(struct net_device *dev,
+						struct rtnl_link_stats64 *stats)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+	if (ipvlan->pcpu_stats) {
+		struct ipvl_pcpu_stats *pcptr;
+		u64 rx_pkts, rx_bytes, rx_mcast, tx_pkts, tx_bytes;
+		u32 rx_errs = 0, tx_drps = 0;
+		u32 strt;
+		int idx;
+
+		for_each_possible_cpu(idx) {
+			pcptr = per_cpu_ptr(ipvlan->pcpu_stats, idx);
+			do {
+				strt= u64_stats_fetch_begin_irq(&pcptr->syncp);
+				rx_pkts = pcptr->rx_pkts;
+				rx_bytes = pcptr->rx_bytes;
+				rx_mcast = pcptr->rx_mcast;
+				tx_pkts = pcptr->tx_pkts;
+				tx_bytes = pcptr->tx_bytes;
+			} while(u64_stats_fetch_retry_irq(&pcptr->syncp, strt));
+
+			stats->rx_packets += rx_pkts;
+			stats->rx_bytes += rx_bytes;
+			stats->multicast += rx_mcast;
+			stats->tx_packets += tx_pkts;
+			stats->tx_bytes += tx_bytes;
+
+			/* u32 values are updated without syncp protection. */
+			rx_errs += pcptr->rx_errs;
+			tx_drps += pcptr->tx_drps;
+		}
+		stats->rx_errors = rx_errs;
+		stats->rx_dropped = rx_errs;
+		stats->tx_dropped = tx_drps;
+	}
+	return stats;
+}
+
+static int ipvlan_vlan_rx_add_vid(struct net_device *dev,
+				   __be16 proto, u16 vid)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct net_device *phy_dev = ipvlan->phy_dev;
+
+	return vlan_vid_add(phy_dev, proto, vid);
+}
+
+static int ipvlan_vlan_rx_kill_vid(struct net_device *dev,
+				   __be16 proto, u16 vid)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct net_device *phy_dev = ipvlan->phy_dev;
+
+	vlan_vid_del(phy_dev, proto, vid);
+	return 0;
+}
+
+static const struct net_device_ops ipvlan_netdev_ops = {
+	.ndo_init		= ipvlan_init,
+	.ndo_uninit		= ipvlan_uninit,
+	.ndo_open		= ipvlan_open,
+	.ndo_stop		= ipvlan_stop,
+	.ndo_start_xmit		= ipvlan_start_xmit,
+	.ndo_fix_features	= ipvlan_fix_features,
+	.ndo_change_rx_flags	= ipvlan_change_rx_flags,
+	.ndo_set_rx_mode	= ipvlan_set_multicast_mac_filter,
+	.ndo_get_stats64	= ipvlan_get_stats64,
+	.ndo_vlan_rx_add_vid	= ipvlan_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= ipvlan_vlan_rx_kill_vid,
+};
+
+/* ---- Ethernet Header Ops ---- */
+static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
+			      unsigned short type, const void *daddr,
+			      const void *saddr, unsigned len)
+{
+	const struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct net_device *phy_dev = ipvlan->phy_dev;
+
+	/* TODO Probably use a different field than dev_addr so that the
+	 * mac-address on the virtual device is portable and can be carried
+	 * while the packets use the mac-addr on the physical device.
+	 */
+	return dev_hard_header(skb, phy_dev, type, daddr,
+			       saddr ? : dev->dev_addr, len);
+}
+
+static const struct header_ops ipvlan_header_ops = {
+	.create  	= ipvlan_hard_header,
+	.rebuild	= eth_rebuild_header,
+	.parse		= eth_header_parse,
+	.cache		= eth_header_cache,
+	.cache_update	= eth_header_cache_update,
+};
+
+/* ---- Ethtool ops ---- */
+static int ipvlan_ethtool_get_settings(struct net_device *dev,
+				       struct ethtool_cmd *cmd)
+{
+	const struct ipvl_dev *ipvlan = netdev_priv(dev);
+	return __ethtool_get_settings(ipvlan->phy_dev, cmd);
+}
+
+static void ipvlan_ethtool_get_drvinfo(struct net_device *dev,
+				       struct ethtool_drvinfo *drvinfo)
+{
+	strlcpy(drvinfo->driver, IPVLAN_DRV, sizeof(drvinfo->driver));
+	strlcpy(drvinfo->version, IPV_DRV_VER, sizeof(drvinfo->version));
+}
+
+static const struct ethtool_ops ipvlan_ethtool_ops = {
+	.get_link	= ethtool_op_get_link,
+	.get_settings	= ipvlan_ethtool_get_settings,
+	.get_drvinfo	= ipvlan_ethtool_get_drvinfo,
+};
+
+/* ---- Link-ops ---- */
+static int ipvlan_nl_changelink(struct net_device *dev,
+				struct nlattr *tb[], struct nlattr *data[])
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev);
+
+	if (data && data[IFLA_IPVLAN_MODE]) {
+		u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
+		ipvlan_set_port_mode(port, nmode);
+	}
+
+	return 0;
+}
+
+static size_t ipvlan_nl_getsize(const struct net_device *dev)
+{
+	return (0
+		+ nla_total_size(2) /* IFLA_IPVLAN_MODE */
+		);
+}
+
+static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+	if (data && data[IFLA_IPVLAN_MODE]) {
+		u16 mode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
+
+		if (mode < IPVLAN_MODE_L2 || mode >= IPVLAN_MODE_MAX)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ipvlan_nl_fillinfo(struct sk_buff *skb,
+			      const struct net_device *dev)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev);
+	int ret = -EINVAL;
+
+	if (!port)
+		goto err;
+
+	ret = -EMSGSIZE;
+	if (nla_put_u16(skb, IFLA_IPVLAN_MODE, port->mode))
+		goto err;
+
+	return 0;
+
+err:
+	return ret;
+}
+
+static int ipvlan_link_new(struct net *src_net, struct net_device *dev,
+			   struct nlattr *tb[], struct nlattr *data[])
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct ipvl_port *port;
+	struct net_device *phy_dev;
+	int err;
+
+	ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+	if (!tb[IFLA_LINK]) {
+		ipvlan_dbg(3, "%s[%d]: Returning -EINVAL...\n",
+			   __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	phy_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
+	if (phy_dev == NULL) {
+		ipvlan_dbg(3, "%s[%d]: Returning -ENODEV...\n",
+			   __func__, __LINE__);
+		return -ENODEV;
+	}
+
+	if (ipvlan_dev_slave(phy_dev)) {
+		struct ipvl_dev *tmp = netdev_priv(phy_dev);
+
+		phy_dev = tmp->phy_dev;
+	} else if (!ipvlan_dev_master(phy_dev)) {
+		err = ipvlan_port_create(phy_dev);
+		if (err < 0) {
+			ipvlan_dbg(3, "%s[%d]: Returning error (%d)...\n",
+				   __func__, __LINE__, err);
+			return err;
+		}
+	}
+
+	port = ipvlan_port_get_rtnl(phy_dev);
+	/* Get the mode if specified. */
+	if (data && data[IFLA_IPVLAN_MODE])
+		port->mode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
+
+	ipvlan->phy_dev = phy_dev;
+	ipvlan->dev = dev;
+	ipvlan->port = port;
+	ipvlan->sfeatures = IPVLAN_FEATURES;
+	INIT_LIST_HEAD(&ipvlan->addrs);
+	ipvlan->ipv4cnt = 0;
+	ipvlan->ipv6cnt = 0;
+
+	/* Probably put a random address here to be presented to the
+	 * world but keep using the physical-dev address for the outgoing
+	 * packets.
+	 */
+	memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN);
+
+	/* Mark this as a IPVLAN secondary device. */
+	dev->priv_flags |= IFF_IPVLAN_SLAVE;
+
+	port->count += 1;
+	err = register_netdevice(dev);
+	if (err < 0) {
+		ipvlan_dbg(3, "%s[%d]: Returning error...\n",
+			   __func__, __LINE__);
+		goto ipvlan_destroy_port;
+	}
+	err = netdev_upper_dev_link(phy_dev, dev);
+	if (err) {
+		ipvlan_dbg(3, "%s[%d]: Returning error (%d)\n",
+			   __func__, __LINE__, err);
+		goto ipvlan_destroy_port;
+	}
+
+	list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans);
+	netif_stacked_transfer_operstate(phy_dev, dev);
+	ipvlan_dbg(3, "%s[%d]: Returning success...\n", __func__, __LINE__);
+	return 0;
+
+ipvlan_destroy_port:
+	port->count -= 1;
+	if (!port->count)
+		ipvlan_port_destroy(phy_dev);
+
+	ipvlan_dbg(3, "%s[%d]: Return (after Destroying Port)",
+		   __func__, __LINE__);
+	return err;
+}
+
+static void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
+{
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct ipvl_addr *addr, *next;
+
+	if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
+		list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
+			ipvlan_ht_addr_del(addr, !dev->dismantle);
+			list_del_rcu(&addr->anode);
+		}
+	}
+	list_del_rcu(&ipvlan->pnode);
+	unregister_netdevice_queue(dev, head);
+	netdev_upper_dev_unlink(ipvlan->phy_dev, dev);
+}
+
+static void ipvlan_link_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
+	dev->priv_flags |= IFF_UNICAST_FLT;
+	dev->netdev_ops = &ipvlan_netdev_ops;
+	dev->destructor = free_netdev;
+	dev->header_ops = &ipvlan_header_ops;
+	dev->ethtool_ops = &ipvlan_ethtool_ops;
+	dev->tx_queue_len = 0;
+}
+
+static const struct nla_policy ipvlan_nl_policy[IFLA_IPVLAN_MAX + 1] =
+{
+	[IFLA_IPVLAN_MODE] = { .type = NLA_U16 },
+};
+
+static struct rtnl_link_ops ipvlan_link_ops = {
+	.kind		= "ipvlan",
+	.priv_size	= sizeof(struct ipvl_dev),
+
+	.get_size	= ipvlan_nl_getsize,
+	.policy		= ipvlan_nl_policy,
+	.validate	= ipvlan_nl_validate,
+	.fill_info	= ipvlan_nl_fillinfo,
+	.changelink	= ipvlan_nl_changelink,
+	.maxtype	= IFLA_IPVLAN_MAX,
+
+	.setup		= ipvlan_link_setup,
+	.newlink	= ipvlan_link_new,
+	.dellink	= ipvlan_link_delete,
+};
+
+int ipvlan_link_register(struct rtnl_link_ops *ops)
+{
+	return rtnl_link_register(ops);
+}
+
+/* ---- IPVLAN event handling ---- */
+static int ipvlan_device_event(struct notifier_block *unused,
+			       unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct ipvl_dev *ipvlan, *next;
+	struct ipvl_port *port;
+	LIST_HEAD(lst_kill);
+
+	if (!ipvlan_dev_master(dev))
+		return NOTIFY_DONE;
+
+	port = ipvlan_port_get_rtnl(dev);
+
+	switch (event) {
+	case NETDEV_CHANGE:
+		list_for_each_entry(ipvlan, &port->ipvlans, pnode)
+			netif_stacked_transfer_operstate(ipvlan->phy_dev,
+							 ipvlan->dev);
+		break;
+
+	case NETDEV_UNREGISTER:
+		if (dev->reg_state != NETREG_UNREGISTERING)
+			break;
+
+		list_for_each_entry_safe(ipvlan, next, &port->ipvlans,
+					 pnode)
+			ipvlan->dev->rtnl_link_ops->dellink(ipvlan->dev,
+							    &lst_kill);
+		unregister_netdevice_many(&lst_kill);
+		break;
+
+	case NETDEV_FEAT_CHANGE:
+		list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+			ipvlan->dev->features = dev->features & IPVLAN_FEATURES;
+			ipvlan->dev->gso_max_size = dev->gso_max_size;
+			netdev_features_change(ipvlan->dev);
+		}
+		break;
+
+	case NETDEV_CHANGEMTU:
+		list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+			ipvlan_adjust_mtu(ipvlan, dev);
+		}
+		break;
+
+	case NETDEV_PRE_TYPE_CHANGE:
+		/* Forbid underlying device to change its type. */
+		return NOTIFY_BAD;
+	}
+	return NOTIFY_DONE;
+}
+
+static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
+{
+	struct ipvl_addr *addr;
+
+	if (ipvlan_addr_busy(ipvlan, ip6_addr, true)) {
+		pr_warn("%s[%d]: Failed IPv6=%x:%x:%x:%x address for %s intf\n",
+			__func__, __LINE__, ip6_addr->s6_addr32[0],
+			ip6_addr->s6_addr32[1], ip6_addr->s6_addr32[2],
+			ip6_addr->s6_addr32[3], ipvlan->dev->name);
+		return -EINVAL;
+	}
+	if ((addr = kzalloc(sizeof(struct ipvl_addr), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	ipvlan_dbg(1, "%s[%d]: Adding IPv6=%x:%x:%x:%x address for %s intf\n",
+		   __func__, __LINE__, ip6_addr->s6_addr32[0],
+		   ip6_addr->s6_addr32[1], ip6_addr->s6_addr32[2],
+		   ip6_addr->s6_addr32[3], ipvlan->dev->name);
+	addr->master = ipvlan;
+	memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr));
+	addr->atype = IPVL_IPV6;
+	list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+	ipvlan->ipv6cnt++;
+	ipvlan_ht_addr_add(ipvlan, addr);
+
+	return 0;
+}
+
+static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
+{
+	struct ipvl_addr *addr;
+
+	if ((addr = ipvlan_ht_addr_lookup(ipvlan->port, ip6_addr, true)) ==NULL)
+		return;
+
+	ipvlan_dbg(1,
+		   "%s[%d]: Deleting IPv6=%x:%x:%x:%x address for %s intf.\n",
+		   __func__, __LINE__, ip6_addr->s6_addr32[0],
+		   ip6_addr->s6_addr32[1], ip6_addr->s6_addr32[2],
+		   ip6_addr->s6_addr32[3], ipvlan->dev->name);
+	/* Delete from the hash-table */
+	ipvlan_ht_addr_del(addr, true);
+	/* Delete from the logical's addr list */
+	list_del_rcu(&addr->anode);
+	ipvlan->ipv6cnt--;
+	WARN_ON(ipvlan->ipv6cnt < 0);
+	kfree_rcu(addr, rcu);
+
+	return;
+}
+
+static int ipvlan_addr6_event(struct notifier_block *unused,
+			      unsigned long event, void *ptr)
+{
+	struct inet6_ifaddr *if6 = (struct inet6_ifaddr *)ptr;
+	struct net_device *dev = (struct net_device *)if6->idev->dev;
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+	ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+	if (!ipvlan_dev_slave(dev))
+		return NOTIFY_DONE;
+
+	if (!ipvlan || !ipvlan->port)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UP:
+		if (ipvlan_add_addr6(ipvlan, &if6->addr))
+			return NOTIFY_BAD;
+		break;
+
+	case NETDEV_DOWN:
+		ipvlan_del_addr6(ipvlan, &if6->addr);
+		break;
+	}
+
+	ipvlan_dbg(3, "%s[%d]: Leaving...\n", __func__, __LINE__);
+	return NOTIFY_OK;
+}
+
+static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
+{
+	struct ipvl_addr *addr;
+
+	if (ipvlan_addr_busy(ipvlan, ip4_addr, false)) {
+		pr_warn("%s[%d]: Failed to add IPv4=%x on %s intf.\n",
+			__func__, __LINE__, ntohl(ip4_addr->s_addr),
+			   ipvlan->dev->name);
+		return -EINVAL;
+	}
+	if ((addr = kzalloc(sizeof(struct ipvl_addr), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	ipvlan_dbg(1, "%s[%d]: Adding IPv4=%x address for %s intf.\n",
+		   __func__, __LINE__, ip4_addr->s_addr, ipvlan->dev->name);
+	addr->master = ipvlan;
+	memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr));
+	addr->atype = IPVL_IPV4;
+	list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+	ipvlan->ipv4cnt++;
+	ipvlan_ht_addr_add(ipvlan, addr);
+	ipvlan_set_broadcast_mac_filter(ipvlan, true);
+
+	return 0;
+}
+
+static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
+{
+	struct ipvl_addr *addr;
+
+	if ((addr= ipvlan_ht_addr_lookup(ipvlan->port, ip4_addr, false)) ==NULL)
+		return;
+
+	ipvlan_dbg(1, "%s[%d]: Deleting IPv4=%x address for %s intf.\n",
+		   __func__, __LINE__, ip4_addr->s_addr, ipvlan->dev->name);
+	/* Delete from the hash-table */
+	ipvlan_ht_addr_del(addr, true);
+	/* Delete from the logical's addr list */
+	list_del_rcu(&addr->anode);
+	ipvlan->ipv4cnt--;
+	WARN_ON(ipvlan->ipv4cnt < 0);
+	if (!ipvlan->ipv4cnt)
+	    ipvlan_set_broadcast_mac_filter(ipvlan, false);
+	kfree_rcu(addr, rcu);
+
+	return;
+}
+
+static int ipvlan_addr4_event(struct notifier_block *unused,
+			      unsigned long event, void *ptr)
+{
+	struct in_ifaddr *if4 = (struct in_ifaddr *)ptr;
+	struct net_device *dev = (struct net_device *)if4->ifa_dev->dev;
+	struct ipvl_dev *ipvlan = netdev_priv(dev);
+	struct in_addr ip4_addr;
+
+	ipvlan_dbg(3, "%s[%d]: Entering...\n", __func__, __LINE__);
+	if (!ipvlan_dev_slave(dev))
+		return NOTIFY_DONE;
+
+	if (!ipvlan || !ipvlan->port)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UP:
+		ip4_addr.s_addr = if4->ifa_address;
+		if (ipvlan_add_addr4(ipvlan, &ip4_addr))
+			return NOTIFY_BAD;
+		break;
+
+	case NETDEV_DOWN:
+		ip4_addr.s_addr = if4->ifa_address;
+		ipvlan_del_addr4(ipvlan, &ip4_addr);
+		break;
+	}
+
+	ipvlan_dbg(3, "%s[%d]: Leaving...\n", __func__, __LINE__);
+	return NOTIFY_OK;
+}
+
+static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {
+	.notifier_call = ipvlan_addr4_event,
+};
+
+static struct notifier_block ipvlan_notifier_block __read_mostly = {
+	.notifier_call = ipvlan_device_event,
+};
+
+static struct notifier_block ipvlan_addr6_notifier_block __read_mostly = {
+	.notifier_call = ipvlan_addr6_event,
+};
+
+static int __init ipvlan_init_module(void)
+{
+	int err;
+
+	ipvlan_init_secret();
+	register_netdevice_notifier(&ipvlan_notifier_block);
+	register_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+	register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+
+	err = ipvlan_link_register(&ipvlan_link_ops);
+	if (err < 0)
+		goto error;
+
+	return 0;
+error:
+	unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+	unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+	unregister_netdevice_notifier(&ipvlan_notifier_block);
+	return err;
+}
+
+static void __exit ipvlan_cleanup_module(void)
+{
+	rtnl_link_unregister(&ipvlan_link_ops);
+	unregister_netdevice_notifier(&ipvlan_notifier_block);
+	unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+	unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+}
+
+module_init(ipvlan_init_module);
+module_exit(ipvlan_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mahesh Bandewar <maheshb@google.com>");
+MODULE_DESCRIPTION("Driver for L3 (IPv6/IPv4) based VLANs");
+MODULE_ALIAS_RTNL_LINK("ipvlan");
diff --git a/drivers/net/ipvlan/ipvlan_sysfs.c b/drivers/net/ipvlan/ipvlan_sysfs.c
new file mode 100644
index 000000000000..ce0a6378d435
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_sysfs.c
@@ -0,0 +1,119 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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 "ipvlan.h"
+
+/* ---- SysFS entries ---- */
+#define port_of(ko)		container_of(ko, struct ipvl_port, kobj)
+#define ipvl_mode_attr_of(_a)	container_of(_a, struct ipvl_mode_attr, attr)
+
+
+/* -- For Master mode -- */
+struct ipvl_mode_attr {
+	struct attribute attr;
+	ssize_t (*show)(struct ipvl_port *port, char *buf);
+	ssize_t (*store)(struct ipvl_port *port, const char *buf, size_t len);
+};
+
+static ssize_t ipvlan_show_mode(struct ipvl_port *port, char *buf)
+{
+	return sprintf(buf, "%hu\n", port->mode);
+}
+
+static ssize_t ipvlan_store_mode(struct ipvl_port *port,
+				 const char *buf, size_t count)
+{
+	int ret = count;
+	u16 nval;
+
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	if (!port) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (sscanf(buf, "%hu", &nval) != 1) {
+		pr_warn("%s: no mode specified.\n", port->dev->name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (nval != 0 && nval != 1) {
+		pr_warn("%s: mode value can only be 0 or 1.\n",
+			   port->dev->name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ipvlan_set_port_mode(port, nval);
+
+out:
+	rtnl_unlock();
+	return ret;
+}
+
+static struct ipvl_mode_attr mode_attr =
+	__ATTR(mode, S_IRUGO | S_IWUSR, ipvlan_show_mode, ipvlan_store_mode);
+
+static struct attribute *ipvl_mode_attrs[] = {
+	&mode_attr.attr,
+	NULL
+};
+
+static ssize_t ipvlan_sysfs_show_mode(struct kobject *kobj,
+				      struct attribute *attr, char *buf)
+{
+	struct ipvl_mode_attr *attribute = ipvl_mode_attr_of(attr);
+	struct ipvl_port *port = port_of(kobj);
+
+	if (!attribute->show)
+		return -EIO;
+
+	return attribute->show(port, buf);
+}
+
+static ssize_t ipvlan_sysfs_store_mode(struct kobject *kobj,
+				       struct attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct ipvl_mode_attr *attribute = ipvl_mode_attr_of(attr);
+	struct ipvl_port *port = port_of(kobj);
+
+	if (!attribute->store)
+		return -EIO;
+
+	return attribute->store(port, buf, count);
+}
+
+static struct sysfs_ops ipvl_mode_sysfs_ops = {
+	.show  = ipvlan_sysfs_show_mode,
+	.store = ipvlan_sysfs_store_mode,
+};
+
+static struct kobj_type ipvl_master_ktype = {
+#ifdef CONFIG_SYSFS
+	.sysfs_ops = &ipvl_mode_sysfs_ops,
+#endif
+	.default_attrs = ipvl_mode_attrs,
+};
+
+int ipvlan_add_per_master_sysfs_mode(struct ipvl_port *port,
+				     struct net_device *dev)
+{
+	return kobject_init_and_add(&port->kobj, &ipvl_master_ktype,
+			&(dev->dev.kobj), "ipvlan");
+}
+
+void ipvlan_del_per_master_sysfs_mode(struct ipvl_port *port)
+{
+		kobject_put(&port->kobj);
+}
+/* ---- END SysFS entries ---- */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index db63cf459ba1..c6bbc8d4b4f8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1228,6 +1228,8 @@ enum netdev_priv_flags {
 	IFF_LIVE_ADDR_CHANGE		= 1<<20,
 	IFF_MACVLAN			= 1<<21,
 	IFF_XMIT_DST_RELEASE_PERM	= 1<<22,
+	IFF_IPVLAN_MASTER		= 1<<23,
+	IFF_IPVLAN_SLAVE		= 1<<24,
 };
 
 #define IFF_802_1Q_VLAN			IFF_802_1Q_VLAN
@@ -1253,6 +1255,8 @@ enum netdev_priv_flags {
 #define IFF_LIVE_ADDR_CHANGE		IFF_LIVE_ADDR_CHANGE
 #define IFF_MACVLAN			IFF_MACVLAN
 #define IFF_XMIT_DST_RELEASE_PERM	IFF_XMIT_DST_RELEASE_PERM
+#define IFF_IPVLAN_MASTER		IFF_IPVLAN_MASTER
+#define IFF_IPVLAN_SLAVE		IFF_IPVLAN_SLAVE
 
 /**
  *	struct net_device - The DEVICE structure.
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 7072d8325016..36bddc233633 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -330,6 +330,21 @@ enum macvlan_macaddr_mode {
 
 #define MACVLAN_FLAG_NOPROMISC	1
 
+/* IPVLAN section */
+enum {
+	IFLA_IPVLAN_UNSPEC,
+	IFLA_IPVLAN_MODE,
+	__IFLA_IPVLAN_MAX
+};
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+enum ipvlan_mode {
+	IPVLAN_MODE_L2 = 0,
+	IPVLAN_MODE_L3,
+	IPVLAN_MODE_MAX
+};
+
 /* VXLAN section */
 enum {
 	IFLA_VXLAN_UNSPEC,
-- 
2.1.0.rc2.206.gedb03e5

^ permalink raw reply related

* [PATCH Iproute2 next v1] ip link: Add ipvlan support to the iproute2/ip util
From: Mahesh Bandewar @ 2014-11-17  6:27 UTC (permalink / raw)
  To: netdev, Stephen Hemminger
  Cc: Eric Dumazet, Maciej Zenczykowski, Laurent Chavey, Tim Hockin,
	David Miller, Brandon Philips, Pavel Emelianov, Mahesh Bandewar

Adding basic support to create virtual devices using 'ip'
utility. Following is the syntax -

	ip link add link <master> <virtual> type ipvlan mode [ l2 | l3 ]
	e.g. ip link add link eth0 ipvl0 type ipvlan mode l3

Signed-off-by: Mahesh Bandewar <maheshb@google.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Maciej Żenczykowski <maze@google.com>
Cc: Laurent Chavey <chavey@google.com>
Cc: Tim Hockin <thockin@google.com>
Cc: Brandon Philips <brandon.philips@coreos.com>
Cc: Pavel Emelianov <xemul@parallels.com>
---
 include/linux/if_link.h | 14 ++++++++
 ip/Makefile             |  2 +-
 ip/iplink.c             |  2 +-
 ip/iplink_ipvlan.c      | 85 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+), 2 deletions(-)
 create mode 100644 ip/iplink_ipvlan.c

diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 47320636361c..ef1e9f73fb15 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -325,6 +325,20 @@ enum macvlan_macaddr_mode {
 	MACVLAN_MACADDR_SET,
 };
 
+/* IPVLAN section */
+enum {
+	IFLA_IPVLAN_UNSPEC,
+	IFLA_IPVLAN_MODE,
+	__IFLA_IPVLAN_MAX,
+};
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+enum ipvlan_mode {
+	IPVLAN_MODE_L2 = 0, /* Process packets all the way upto L2 */
+	IPVLAN_MODE_L3 = 1, /* Process Packets all the way upto L3 */
+};
+
 #define MACVLAN_FLAG_NOPROMISC	1
 
 /* VXLAN section */
diff --git a/ip/Makefile b/ip/Makefile
index fdc82f7286a0..01901bc8571c 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -6,7 +6,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o link_vti6.o \
     iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
     link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \
-    iplink_bridge.o iplink_bridge_slave.o ipfou.o
+    iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/iplink.c b/ip/iplink.c
index 43b26f4cea08..2fc7fa2b47c8 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -90,7 +90,7 @@ void iplink_usage(void)
 		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n");
 		fprintf(stderr, "          bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n");
 		fprintf(stderr, "          gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n");
-		fprintf(stderr, "          bond_slave }\n");
+		fprintf(stderr, "          bond_slave | ipvlan }\n");
 	}
 	exit(-1);
 }
diff --git a/ip/iplink_ipvlan.c b/ip/iplink_ipvlan.c
new file mode 100644
index 000000000000..6712fdb92fd4
--- /dev/null
+++ b/ip/iplink_ipvlan.c
@@ -0,0 +1,85 @@
+/* iplink_ipvlan.c	IPVLAN device support
+ *
+ *              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.
+ *
+ * Authors:     Mahesh Bandewar <maheshb@google.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/if_link.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... ipvlan [ mode { l2 | l3 } ]\n");
+}
+
+static int mode_arg(void)
+{
+	fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", "
+		"or \"l3\"\n");
+        return -1;
+}
+
+static int ipvlan_parse_opt(struct link_util *lu, int argc, char **argv,
+			  struct nlmsghdr *n)
+{
+	while (argc > 0) {
+	    if (matches(*argv, "mode") == 0) {
+			__u16 mode = 0;
+			NEXT_ARG();
+
+			if (strcmp(*argv, "l2") == 0)
+				mode = IPVLAN_MODE_L2;
+			else if (strcmp(*argv, "l3") == 0)
+				mode = IPVLAN_MODE_L3;
+			else
+				mode_arg();
+
+			addattr16(n, 1024, IFLA_IPVLAN_MODE, mode);
+		} else if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "ipvlan: unknown option \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	return 0;
+}
+
+static void ipvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_IPVLAN_MODE]) {
+		if (RTA_PAYLOAD(tb[IFLA_IPVLAN_MODE]) == sizeof(__u16)) {
+			__u16 mode = rta_getattr_u16(tb[IFLA_IPVLAN_MODE]);
+
+			fprintf(f, " mode %s ",
+				mode == IPVLAN_MODE_L2 ? "l2"
+				: mode == IPVLAN_MODE_L3 ? "l3" : "unknown");
+		}
+	}
+}
+
+struct link_util ipvlan_link_util = {
+	.id		= "ipvlan",
+	.maxattr	= IFLA_IPVLAN_MAX,
+	.parse_opt	= ipvlan_parse_opt,
+	.print_opt	= ipvlan_print_opt,
+};
-- 
2.1.0.rc2.206.gedb03e5

^ permalink raw reply related

* Re: [PATCH net-next 01/14] net: provide a per host RSS key generic infrastructure
From: Andi Kleen @ 2014-11-17  6:46 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S. Miller, netdev, Thomas Lendacky, Ariel Elior,
	Michael Chan, Prashant Sreedharan, Rasesh Mody, Sathya Perla,
	Subbu Seetharaman, Ajit Khaparde, Jesse Brandeburg, Jeff Kirsher,
	Amir Vadai, Shradha Shah, Shreyas Bhatewara
In-Reply-To: <1416147798-16561-2-git-send-email-edumazet@google.com>

Eric Dumazet <edumazet@google.com> writes:

> Some drivers use a constant (and well known key), some drivers use a random
> key per port, making bonding setups hard to tune. Well known keys increase
> attack surface, considering that number of queues is usually a power of two.

Wouldn't it be better to have some kernel facility to query on what
port a given mapping would end up rather than exporting
the key? Similar to querying the rounting table.
That would be more abstract.

> +	{
> +		.procname	= "netdev_rss_key",
> +		.data		= &netdev_rss_key,
> +		.maxlen		= sizeof(int),
> +		.mode		= 0444,

Surely 0400 ? After all it's a kind a "secret"

-Andi

^ permalink raw reply

* RE: [PATCH Iproute2 next v1] ip link: Add ipvlan support to the iproute2/ip util
From: Sathya Perla @ 2014-11-17  6:57 UTC (permalink / raw)
  To: Mahesh Bandewar, netdev, Stephen Hemminger
  Cc: Eric Dumazet, Maciej Zenczykowski, Laurent Chavey, Tim Hockin,
	David Miller, Brandon Philips, Pavel Emelianov
In-Reply-To: <1416205677-26706-1-git-send-email-maheshb@google.com>

> -----Original Message-----
> From: netdev-owner@vger.kernel.org [mailto:netdev-
> 
> Adding basic support to create virtual devices using 'ip'
> utility. Following is the syntax -
> 
> 	ip link add link <master> <virtual> type ipvlan mode [ l2 | l3 ]
> 	e.g. ip link add link eth0 ipvl0 type ipvlan mode l3
> 
..
> ---
>  include/linux/if_link.h | 14 ++++++++
>  ip/Makefile             |  2 +-
>  ip/iplink.c             |  2 +-
>  ip/iplink_ipvlan.c      | 85
> +++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 101 insertions(+), 2 deletions(-)
>  create mode 100644 ip/iplink_ipvlan.c
> 
> diff --git a/include/linux/if_link.h b/include/linux/if_link.h
> index 47320636361c..ef1e9f73fb15 100644
> --- a/include/linux/if_link.h
> +++ b/include/linux/if_link.h
> @@ -325,6 +325,20 @@ enum macvlan_macaddr_mode {
>  	MACVLAN_MACADDR_SET,
>  };
> 
> +/* IPVLAN section */
> +enum {
> +	IFLA_IPVLAN_UNSPEC,
> +	IFLA_IPVLAN_MODE,
> +	__IFLA_IPVLAN_MAX,
> +};
> +
> +#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
> +
> +enum ipvlan_mode {
> +	IPVLAN_MODE_L2 = 0, /* Process packets all the way upto L2 */
> +	IPVLAN_MODE_L3 = 1, /* Process Packets all the way upto L3 */
> +};
> +
>  #define MACVLAN_FLAG_NOPROMISC	1
> 
>... +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/socket.h>
> +#include <linux/if_link.h>
> +
> +#include "rt_names.h"
> +#include "utils.h"
> +#include "ip_common.h"
> +
> +static void explain(void)
> +{
> +	fprintf(stderr, "Usage: ... ipvlan [ mode { l2 | l3 } ]\n");
> +}
> +
> +static int mode_arg(void)
> +{
> +	fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", "
> +		"or \"l3\"\n");
> +        return -1;
I guess you wanted to "return -1" from the caller routine ipvlan_parse_opt()
and not from this routine.

> +}
> +
> +static int ipvlan_parse_opt(struct link_util *lu, int argc, char **argv,
> +			  struct nlmsghdr *n)
> +{
> +	while (argc > 0) {
> +	    if (matches(*argv, "mode") == 0) {
> +			__u16 mode = 0;
> +			NEXT_ARG();
> +
> +			if (strcmp(*argv, "l2") == 0)
> +				mode = IPVLAN_MODE_L2;
> +			else if (strcmp(*argv, "l3") == 0)
> +				mode = IPVLAN_MODE_L3;
> +			else
> +				mode_arg();
> +
> +			addattr16(n, 1024, IFLA_IPVLAN_MODE, mode);
> +		} else if (matches(*argv, "help") == 0) {
> +			explain();
> +			return -1;
> +		} else {
> +			fprintf(stderr, "ipvlan: unknown option \"%s\"?\n",
> *argv);
> +			explain();
> +			return -1;
> +		}
> +		argc--, argv++;
> +	}
> +
> +	return 0;
> +}
> +

^ permalink raw reply

* Re: [PATCH net-next 01/14] net: provide a per host RSS key generic infrastructure
From: Eric Dumazet @ 2014-11-17  6:58 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Eric Dumazet, David S. Miller, netdev, Thomas Lendacky,
	Ariel Elior, Michael Chan, Prashant Sreedharan, Rasesh Mody,
	Sathya Perla, Subbu Seetharaman, Ajit Khaparde, Jesse Brandeburg,
	Jeff Kirsher, Amir Vadai, Shradha Shah, Shreyas Bhatewara
In-Reply-To: <87wq6uiass.fsf@tassilo.jf.intel.com>

On Sun, 2014-11-16 at 22:46 -0800, Andi Kleen wrote:

> Wouldn't it be better to have some kernel facility to query on what
> port a given mapping would end up rather than exporting
> the key? Similar to querying the rounting table.
> That would be more abstract.

It is already there, few drivers implement

ethtool -x eth0

And all users can read rss key :

$ ethtool -x eth0
RX flow hash indirection table for eth0 with 8 RX ring(s):
    0:      0     1     2     3     4     5     6     7
RSS hash key:
d9:73:69:1e:c5:74:73:38:67:a4:a9:98:78:b1:e2:b6:39:1c:02:f9:30:57:79:84:02:b7:48:90:63:95:10:85:51:bb:f2:9e:f2:c5:85:27

> 
> > +	{
> > +		.procname	= "netdev_rss_key",
> > +		.data		= &netdev_rss_key,
> > +		.maxlen		= sizeof(int),
> > +		.mode		= 0444,
> 
> Surely 0400 ? After all it's a kind a "secret"

Not really. Toeplitz is not cryptographic. A local program can easily
rebuild the key even if kept 'secret' by networking stack.

^ permalink raw reply

* Re: linux-next: manual merge of the tiny tree with the net-next tree
From: Josh Triplett @ 2014-11-17  8:09 UTC (permalink / raw)
  To: Stephen Rothwell
  Cc: David Miller, netdev, linux-next, linux-kernel, Iulia Manda,
	Pravin B Shelar
In-Reply-To: <20141117163503.6543cd21@canb.auug.org.au>

On Mon, Nov 17, 2014 at 04:35:03PM +1100, Stephen Rothwell wrote:
> Hi Josh,
> 
> Today's linux-next merge of the tiny tree got a conflict in
> net/openvswitch/Kconfig between commit 8cd4313aa775 ("openvswitch: Fix
> build failure") from the net-next tree and commit b043d487e255 ("lib:
> Conditionally compile flex_array") from the tiny tree.
> 
> I fixed it up (see below) and can carry the fix as necessary (no action
> is required).

Looks good to me.

> diff --cc net/openvswitch/Kconfig
> index b7d818c59423,1d979cecd66e..000000000000
> --- a/net/openvswitch/Kconfig
> +++ b/net/openvswitch/Kconfig
> @@@ -4,9 -4,8 +4,10 @@@
>   
>   config OPENVSWITCH
>   	tristate "Open vSwitch"
>  +	depends on INET
>   	select LIBCRC32C
>  +	select NET_MPLS_GSO
> + 	select FLEX_ARRAY
>   	---help---
>   	  Open vSwitch is a multilayer Ethernet switch targeted at virtualized
>   	  environments.  In addition to supporting a variety of features

^ permalink raw reply

* Re: [BUG] index is out of range for nfnl_group2type[]
From: Andrey Ryabinin @ 2014-11-17  8:16 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: Patrick McHardy, Jozsef Kadlecsik, David S. Miller,
	netfilter-devel, coreteam, netdev@vger.kernel.org, linux-kernel
In-Reply-To: <20141114174438.GA10644@salvia>

On 11/14/2014 08:44 PM, Pablo Neira Ayuso wrote:
> On Thu, Nov 13, 2014 at 12:00:43PM +0300, Andrey Ryabinin wrote:
>> FYI I've spotted this:
>>
>> [  180.202810] ================================================================================
>> [  180.203600] UBSan: Undefined behaviour in ../net/netfilter/nfnetlink.c:467:28
>> [  180.204249] index 9 is out of range for type 'int [9]'
>> [  180.204697] CPU: 0 PID: 1771 Comm: trinity-main Not tainted 3.18.0-rc4-mm1+ #122
>> [  180.205365] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014
>> [  180.206498]  0000000000000018 0000000000000000 0000000000000009 ffff88007bdf7da8
>> [  180.207220]  ffffffff82b0ef5f 0000000000000092 ffffffff845ae2e0 ffff88007bdf7db8
>> [  180.207887]  ffffffff8199e489 ffff88007bdf7e18 ffffffff8199ea22 0000003900000000
>> [  180.208639] Call Trace:
>> [  180.208857] dump_stack (lib/dump_stack.c:52)
>> [  180.209370] ubsan_epilogue (lib/ubsan.c:174)
>> [  180.209849] __ubsan_handle_out_of_bounds (lib/ubsan.c:400)
>> [  180.210512] nfnetlink_bind (net/netfilter/nfnetlink.c:467)
>> [  180.210986] netlink_bind (net/netlink/af_netlink.c:1483)
>> [  180.211495] SYSC_bind (net/socket.c:1541)
>> [  180.211940] ? security_socket_setsockopt (security/security.c:1208)
>> [  180.212541] ? SyS_setsockopt (net/socket.c:1920 net/socket.c:1900)
>> [  180.213057] ? SyS_write (fs/read_write.c:276 fs/read_write.c:588 fs/read_write.c:577)
>> [  180.213506] SyS_bind (net/socket.c:1527)
>> [  180.213919] system_call_fastpath (arch/x86/kernel/entry_64.S:423)
>> [  180.214479] ================================================================================
> 
> Thanks for reporting. I think the attached patch fixes this problem.
> 

Yep, It's gone away after applying your patch.

Thanks,
Andrey.


^ permalink raw reply

* [PATCH 6/8] net: fec: use swab32s() instead of cpu_to_be32()
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>

when swap_buffer() is being called, we know for sure, that we need to
byte swap the data. Furthermore, this function is called for swapping
data in both directions. Thus cpu_to_be32() is semantically not
correct for all use cases. Use swab32s() to reflect this.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec_main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 3884dd9..f73fee7 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -293,7 +293,7 @@ static void *swap_buffer(void *bufaddr, int len)
 	unsigned int *buf = bufaddr;
 
 	for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++)
-		*buf = cpu_to_be32(*buf);
+		swab32s(buf);
 
 	return bufaddr;
 }
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 3/8] net: fec: properly parenthesize macro args
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>


Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec.h |   31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index e778b9e..27e75cf 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -279,36 +279,37 @@ struct bufdesc_ex {
 #define FEC_ENET_MAX_TX_QS	3
 #define FEC_ENET_MAX_RX_QS	3
 
-#define FEC_R_DES_START(X)	((X == 1) ? FEC_R_DES_START_1 : \
-				((X == 2) ? \
+#define FEC_R_DES_START(X)	(((X) == 1) ? FEC_R_DES_START_1 : \
+				(((X) == 2) ? \
 					FEC_R_DES_START_2 : FEC_R_DES_START_0))
-#define FEC_X_DES_START(X)	((X == 1) ? FEC_X_DES_START_1 : \
-				((X == 2) ? \
+#define FEC_X_DES_START(X)	(((X) == 1) ? FEC_X_DES_START_1 : \
+				(((X) == 2) ? \
 					FEC_X_DES_START_2 : FEC_X_DES_START_0))
-#define FEC_R_DES_ACTIVE(X)	((X == 1) ? FEC_R_DES_ACTIVE_1 : \
-				((X == 2) ? \
+#define FEC_R_DES_ACTIVE(X)	(((X) == 1) ? FEC_R_DES_ACTIVE_1 : \
+				(((X) == 2) ? \
 				   FEC_R_DES_ACTIVE_2 : FEC_R_DES_ACTIVE_0))
-#define FEC_X_DES_ACTIVE(X)	((X == 1) ? FEC_X_DES_ACTIVE_1 : \
-				((X == 2) ? \
+#define FEC_X_DES_ACTIVE(X)	(((X) == 1) ? FEC_X_DES_ACTIVE_1 : \
+				(((X) == 2) ? \
 				   FEC_X_DES_ACTIVE_2 : FEC_X_DES_ACTIVE_0))
 
-#define FEC_DMA_CFG(X)		((X == 2) ? FEC_DMA_CFG_2 : FEC_DMA_CFG_1)
+#define FEC_DMA_CFG(X)		(((X) == 2) ? FEC_DMA_CFG_2 : FEC_DMA_CFG_1)
 
 #define DMA_CLASS_EN		(1 << 16)
-#define FEC_RCMR(X)		((X == 2) ? FEC_RCMR_2 : FEC_RCMR_1)
+#define FEC_RCMR(X)		(((X) == 2) ? FEC_RCMR_2 : FEC_RCMR_1)
 #define IDLE_SLOPE_MASK		0xffff
 #define IDLE_SLOPE_1		0x200 /* BW fraction: 0.5 */
 #define IDLE_SLOPE_2		0x200 /* BW fraction: 0.5 */
-#define IDLE_SLOPE(X)		((X == 1) ? (IDLE_SLOPE_1 & IDLE_SLOPE_MASK) : \
+#define IDLE_SLOPE(X)		(((X) == 1) ?				\
+				(IDLE_SLOPE_1 & IDLE_SLOPE_MASK) :	\
 				(IDLE_SLOPE_2 & IDLE_SLOPE_MASK))
 #define RCMR_MATCHEN		(0x1 << 16)
-#define RCMR_CMP_CFG(v, n)	((v & 0x7) <<  (n << 2))
+#define RCMR_CMP_CFG(v, n)	(((v) & 0x7) <<  (n << 2))
 #define RCMR_CMP_1		(RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \
 				RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3))
 #define RCMR_CMP_2		(RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \
 				RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3))
-#define RCMR_CMP(X)		((X == 1) ? RCMR_CMP_1 : RCMR_CMP_2)
-#define FEC_TX_BD_FTYPE(X)	((X & 0xf) << 20)
+#define RCMR_CMP(X)		(((X) == 1) ? RCMR_CMP_1 : RCMR_CMP_2)
+#define FEC_TX_BD_FTYPE(X)	(((X) & 0xf) << 20)
 
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it it best
@@ -359,7 +360,7 @@ struct bufdesc_ex {
 /* ENET interrupt coalescing macro define */
 #define FEC_ITR_CLK_SEL		(0x1 << 30)
 #define FEC_ITR_EN		(0x1 << 31)
-#define FEC_ITR_ICFT(X)		((X & 0xff) << 20)
+#define FEC_ITR_ICFT(X)		(((X) & 0xff) << 20)
 #define FEC_ITR_ICTT(X)		((X) & 0xffff)
 #define FEC_ITR_ICFT_DEFAULT	200  /* Set 200 frame count threshold */
 #define FEC_ITR_ICTT_DEFAULT	1000 /* Set 1000us timer threshold */
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 4/8] net: fec: change type of 'bufdesc_ex' to bool
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>

fep->bufdesc_ex is treated as a boolean value, thus declare it as
such.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec.h      |    2 +-
 drivers/net/ethernet/freescale/fec_main.c |    4 +---
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 27e75cf..1418813 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -502,7 +502,7 @@ struct fec_enet_private {
 	int	speed;
 	struct	completion mdio_done;
 	int	irq[FEC_IRQ_NUM];
-	int	bufdesc_ex;
+	bool	bufdesc_ex;
 	int	pause_flag;
 
 	struct	napi_struct napi;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 3dca494..f3a6379 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -3184,8 +3184,6 @@ fec_probe(struct platform_device *pdev)
 	fep->pdev = pdev;
 	fep->dev_id = dev_id++;
 
-	fep->bufdesc_ex = 0;
-
 	platform_set_drvdata(pdev, ndev);
 
 	phy_node = of_parse_phandle(np, "phy-handle", 0);
@@ -3243,7 +3241,7 @@ fec_probe(struct platform_device *pdev)
 		pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
 	if (IS_ERR(fep->clk_ptp)) {
 		fep->clk_ptp = NULL;
-		fep->bufdesc_ex = 0;
+		fep->bufdesc_ex = false;
 	}
 
 	ret = fec_enet_clk_enable(ndev, true);
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 1/8] net: fec: indentation cleanup
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>

consistently use TABs for indentation

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec.h |  108 +++++++++++++++++-----------------
 1 file changed, 54 insertions(+), 54 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 9af296a..3047db4 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -213,60 +213,60 @@ struct bufdesc_ex {
  *	The following definitions courtesy of commproc.h, which where
  *	Copyright (c) 1997 Dan Malek (dmalek@jlc.net).
  */
-#define BD_SC_EMPTY     ((ushort)0x8000)        /* Receive is empty */
-#define BD_SC_READY     ((ushort)0x8000)        /* Transmit is ready */
-#define BD_SC_WRAP      ((ushort)0x2000)        /* Last buffer descriptor */
-#define BD_SC_INTRPT    ((ushort)0x1000)        /* Interrupt on change */
-#define BD_SC_CM        ((ushort)0x0200)        /* Continuous mode */
-#define BD_SC_ID        ((ushort)0x0100)        /* Rec'd too many idles */
-#define BD_SC_P         ((ushort)0x0100)        /* xmt preamble */
-#define BD_SC_BR        ((ushort)0x0020)        /* Break received */
-#define BD_SC_FR        ((ushort)0x0010)        /* Framing error */
-#define BD_SC_PR        ((ushort)0x0008)        /* Parity error */
-#define BD_SC_OV        ((ushort)0x0002)        /* Overrun */
-#define BD_SC_CD        ((ushort)0x0001)        /* ?? */
+#define BD_SC_EMPTY	((ushort)0x8000)	/* Receive is empty */
+#define BD_SC_READY	((ushort)0x8000)	/* Transmit is ready */
+#define BD_SC_WRAP	((ushort)0x2000)	/* Last buffer descriptor */
+#define BD_SC_INTRPT	((ushort)0x1000)	/* Interrupt on change */
+#define BD_SC_CM	((ushort)0x0200)	/* Continuous mode */
+#define BD_SC_ID	((ushort)0x0100)	/* Rec'd too many idles */
+#define BD_SC_P		((ushort)0x0100)	/* xmt preamble */
+#define BD_SC_BR	((ushort)0x0020)	/* Break received */
+#define BD_SC_FR	((ushort)0x0010)	/* Framing error */
+#define BD_SC_PR	((ushort)0x0008)	/* Parity error */
+#define BD_SC_OV	((ushort)0x0002)	/* Overrun */
+#define BD_SC_CD	((ushort)0x0001)	/* ?? */
 
 /* Buffer descriptor control/status used by Ethernet receive.
-*/
-#define BD_ENET_RX_EMPTY        ((ushort)0x8000)
-#define BD_ENET_RX_WRAP         ((ushort)0x2000)
-#define BD_ENET_RX_INTR         ((ushort)0x1000)
-#define BD_ENET_RX_LAST         ((ushort)0x0800)
-#define BD_ENET_RX_FIRST        ((ushort)0x0400)
-#define BD_ENET_RX_MISS         ((ushort)0x0100)
-#define BD_ENET_RX_LG           ((ushort)0x0020)
-#define BD_ENET_RX_NO           ((ushort)0x0010)
-#define BD_ENET_RX_SH           ((ushort)0x0008)
-#define BD_ENET_RX_CR           ((ushort)0x0004)
-#define BD_ENET_RX_OV           ((ushort)0x0002)
-#define BD_ENET_RX_CL           ((ushort)0x0001)
-#define BD_ENET_RX_STATS        ((ushort)0x013f)        /* All status bits */
+ */
+#define BD_ENET_RX_EMPTY	((ushort)0x8000)
+#define BD_ENET_RX_WRAP		((ushort)0x2000)
+#define BD_ENET_RX_INTR		((ushort)0x1000)
+#define BD_ENET_RX_LAST		((ushort)0x0800)
+#define BD_ENET_RX_FIRST	((ushort)0x0400)
+#define BD_ENET_RX_MISS		((ushort)0x0100)
+#define BD_ENET_RX_LG		((ushort)0x0020)
+#define BD_ENET_RX_NO		((ushort)0x0010)
+#define BD_ENET_RX_SH		((ushort)0x0008)
+#define BD_ENET_RX_CR		((ushort)0x0004)
+#define BD_ENET_RX_OV		((ushort)0x0002)
+#define BD_ENET_RX_CL		((ushort)0x0001)
+#define BD_ENET_RX_STATS	((ushort)0x013f)	/* All status bits */
 
 /* Enhanced buffer descriptor control/status used by Ethernet receive */
-#define BD_ENET_RX_VLAN         0x00000004
+#define BD_ENET_RX_VLAN		0x00000004
 
 /* Buffer descriptor control/status used by Ethernet transmit.
-*/
-#define BD_ENET_TX_READY        ((ushort)0x8000)
-#define BD_ENET_TX_PAD          ((ushort)0x4000)
-#define BD_ENET_TX_WRAP         ((ushort)0x2000)
-#define BD_ENET_TX_INTR         ((ushort)0x1000)
-#define BD_ENET_TX_LAST         ((ushort)0x0800)
-#define BD_ENET_TX_TC           ((ushort)0x0400)
-#define BD_ENET_TX_DEF          ((ushort)0x0200)
-#define BD_ENET_TX_HB           ((ushort)0x0100)
-#define BD_ENET_TX_LC           ((ushort)0x0080)
-#define BD_ENET_TX_RL           ((ushort)0x0040)
-#define BD_ENET_TX_RCMASK       ((ushort)0x003c)
-#define BD_ENET_TX_UN           ((ushort)0x0002)
-#define BD_ENET_TX_CSL          ((ushort)0x0001)
-#define BD_ENET_TX_STATS        ((ushort)0x0fff)        /* All status bits */
-
-/*enhanced buffer descriptor control/status used by Ethernet transmit*/
-#define BD_ENET_TX_INT          0x40000000
-#define BD_ENET_TX_TS           0x20000000
-#define BD_ENET_TX_PINS         0x10000000
-#define BD_ENET_TX_IINS         0x08000000
+ */
+#define BD_ENET_TX_READY	((ushort)0x8000)
+#define BD_ENET_TX_PAD		((ushort)0x4000)
+#define BD_ENET_TX_WRAP		((ushort)0x2000)
+#define BD_ENET_TX_INTR		((ushort)0x1000)
+#define BD_ENET_TX_LAST		((ushort)0x0800)
+#define BD_ENET_TX_TC		((ushort)0x0400)
+#define BD_ENET_TX_DEF		((ushort)0x0200)
+#define BD_ENET_TX_HB		((ushort)0x0100)
+#define BD_ENET_TX_LC		((ushort)0x0080)
+#define BD_ENET_TX_RL		((ushort)0x0040)
+#define BD_ENET_TX_RCMASK	((ushort)0x003c)
+#define BD_ENET_TX_UN		((ushort)0x0002)
+#define BD_ENET_TX_CSL		((ushort)0x0001)
+#define BD_ENET_TX_STATS	((ushort)0x0fff)	/* All status bits */
+
+/* enhanced buffer descriptor control/status used by Ethernet transmit */
+#define BD_ENET_TX_INT		0x40000000
+#define BD_ENET_TX_TS		0x20000000
+#define BD_ENET_TX_PINS		0x10000000
+#define BD_ENET_TX_IINS		0x08000000
 
 
 /* This device has up to three irqs on some platforms */
@@ -301,7 +301,7 @@ struct bufdesc_ex {
 #define IDLE_SLOPE_2		0x200 /* BW fraction: 0.5 */
 #define IDLE_SLOPE(X)		((X == 1) ? (IDLE_SLOPE_1 & IDLE_SLOPE_MASK) : \
 				(IDLE_SLOPE_2 & IDLE_SLOPE_MASK))
-#define RCMR_MATCHEN            (0x1 << 16)
+#define RCMR_MATCHEN		(0x1 << 16)
 #define RCMR_CMP_CFG(v, n)	((v & 0x7) <<  (n << 2))
 #define RCMR_CMP_1		(RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \
 				RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3))
@@ -326,8 +326,8 @@ struct bufdesc_ex {
 #define TX_RING_SIZE		512	/* Must be power of two */
 #define TX_RING_MOD_MASK	511	/*   for this to work */
 
-#define BD_ENET_RX_INT          0x00800000
-#define BD_ENET_RX_PTP          ((ushort)0x0400)
+#define BD_ENET_RX_INT		0x00800000
+#define BD_ENET_RX_PTP		((ushort)0x0400)
 #define BD_ENET_RX_ICE		0x00000020
 #define BD_ENET_RX_PCR		0x00000010
 #define FLAG_RX_CSUM_ENABLED	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
@@ -364,8 +364,8 @@ struct bufdesc_ex {
 #define FEC_ITR_ICFT_DEFAULT	200  /* Set 200 frame count threshold */
 #define FEC_ITR_ICTT_DEFAULT	1000 /* Set 1000us timer threshold */
 
-#define FEC_VLAN_TAG_LEN       0x04
-#define FEC_ETHTYPE_LEN                0x02
+#define FEC_VLAN_TAG_LEN	0x04
+#define FEC_ETHTYPE_LEN		0x02
 
 /* Controller is ENET-MAC */
 #define FEC_QUIRK_ENET_MAC		(1 << 0)
@@ -390,7 +390,7 @@ struct bufdesc_ex {
  * frames not being transmitted until there is a 0-to-1 transition on
  * ENET_TDAR[TDAR].
  */
-#define FEC_QUIRK_ERR006358            (1 << 7)
+#define FEC_QUIRK_ERR006358		(1 << 7)
 /* ENET IP hw AVB
  *
  * i.MX6SX ENET IP add Audio Video Bridging (AVB) feature support.
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 5/8] net: fec: improve access to quirk flags by copying them into fec_enet_private struct
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>


Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec.h      |    1 +
 drivers/net/ethernet/freescale/fec_main.c |  106 +++++++++++------------------
 2 files changed, 39 insertions(+), 68 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 1418813..7aa9388 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -504,6 +504,7 @@ struct fec_enet_private {
 	int	irq[FEC_IRQ_NUM];
 	bool	bufdesc_ex;
 	int	pause_flag;
+	u32	quirks;
 
 	struct	napi_struct napi;
 	int	csum_flags;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f3a6379..3884dd9 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -361,8 +361,6 @@ fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
 			     struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	struct bufdesc *bdp = txq->cur_tx;
 	struct bufdesc_ex *ebdp;
 	int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -398,7 +396,7 @@ fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
 		}
 
 		if (fep->bufdesc_ex) {
-			if (id_entry->driver_data & FEC_QUIRK_HAS_AVB)
+			if (fep->quirks & FEC_QUIRK_HAS_AVB)
 				estatus |= FEC_TX_BD_FTYPE(queue);
 			if (skb->ip_summed == CHECKSUM_PARTIAL)
 				estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
@@ -410,11 +408,11 @@ fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
 
 		index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
 		if (((unsigned long) bufaddr) & fep->tx_align ||
-			id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+			fep->quirks & FEC_QUIRK_SWAP_FRAME) {
 			memcpy(txq->tx_bounce[index], bufaddr, frag_len);
 			bufaddr = txq->tx_bounce[index];
 
-			if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+			if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
 				swap_buffer(bufaddr, frag_len);
 		}
 
@@ -450,8 +448,6 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
 				   struct sk_buff *skb, struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	int nr_frags = skb_shinfo(skb)->nr_frags;
 	struct bufdesc *bdp, *last_bdp;
 	void *bufaddr;
@@ -490,11 +486,11 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
 	queue = skb_get_queue_mapping(skb);
 	index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
 	if (((unsigned long) bufaddr) & fep->tx_align ||
-		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
 		memcpy(txq->tx_bounce[index], skb->data, buflen);
 		bufaddr = txq->tx_bounce[index];
 
-		if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
 			swap_buffer(bufaddr, buflen);
 	}
 
@@ -529,7 +525,7 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
 			fep->hwts_tx_en))
 			skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 
-		if (id_entry->driver_data & FEC_QUIRK_HAS_AVB)
+		if (fep->quirks & FEC_QUIRK_HAS_AVB)
 			estatus |= FEC_TX_BD_FTYPE(queue);
 
 		if (skb->ip_summed == CHECKSUM_PARTIAL)
@@ -573,8 +569,6 @@ fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb,
 			  int size, bool last_tcp, bool is_last)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc);
 	unsigned short queue = skb_get_queue_mapping(skb);
 	unsigned short status;
@@ -587,11 +581,11 @@ fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb,
 	status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
 
 	if (((unsigned long) data) & fep->tx_align ||
-		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
 		memcpy(txq->tx_bounce[index], data, size);
 		data = txq->tx_bounce[index];
 
-		if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
 			swap_buffer(data, size);
 	}
 
@@ -607,7 +601,7 @@ fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb,
 	bdp->cbd_bufaddr = addr;
 
 	if (fep->bufdesc_ex) {
-		if (id_entry->driver_data & FEC_QUIRK_HAS_AVB)
+		if (fep->quirks & FEC_QUIRK_HAS_AVB)
 			estatus |= FEC_TX_BD_FTYPE(queue);
 		if (skb->ip_summed == CHECKSUM_PARTIAL)
 			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
@@ -635,8 +629,6 @@ fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq,
 			 struct bufdesc *bdp, int index)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
 	struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc);
 	unsigned short queue = skb_get_queue_mapping(skb);
@@ -652,11 +644,11 @@ fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq,
 	bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
 	dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE;
 	if (((unsigned long)bufaddr) & fep->tx_align ||
-		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
 		memcpy(txq->tx_bounce[index], skb->data, hdr_len);
 		bufaddr = txq->tx_bounce[index];
 
-		if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
 			swap_buffer(bufaddr, hdr_len);
 
 		dmabuf = dma_map_single(&fep->pdev->dev, bufaddr,
@@ -673,7 +665,7 @@ fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq,
 	bdp->cbd_datlen = hdr_len;
 
 	if (fep->bufdesc_ex) {
-		if (id_entry->driver_data & FEC_QUIRK_HAS_AVB)
+		if (fep->quirks & FEC_QUIRK_HAS_AVB)
 			estatus |= FEC_TX_BD_FTYPE(queue);
 		if (skb->ip_summed == CHECKSUM_PARTIAL)
 			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
@@ -698,8 +690,6 @@ static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq,
 	struct tso_t tso;
 	unsigned int index = 0;
 	int ret;
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 
 	if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep, txq)) {
 		dev_kfree_skb_any(skb);
@@ -761,7 +751,7 @@ static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq,
 	txq->cur_tx = bdp;
 
 	/* Trigger transmission start */
-	if (!(id_entry->driver_data & FEC_QUIRK_ERR007885) ||
+	if (!(fep->quirks & FEC_QUIRK_ERR007885) ||
 	    !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) ||
 	    !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) ||
 	    !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) ||
@@ -924,8 +914,6 @@ static void
 fec_restart(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	u32 val;
 	u32 temp_mac[2];
 	u32 rcntl = OPT_FRAME_SIZE | 0x04;
@@ -935,7 +923,7 @@ fec_restart(struct net_device *ndev)
 	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
 	 * instead of reset MAC itself.
 	 */
-	if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) {
+	if (fep->quirks & FEC_QUIRK_HAS_AVB) {
 		writel(0, fep->hwp + FEC_ECNTRL);
 	} else {
 		writel(1, fep->hwp + FEC_ECNTRL);
@@ -946,7 +934,7 @@ fec_restart(struct net_device *ndev)
 	 * enet-mac reset will reset mac address registers too,
 	 * so need to reconfigure it.
 	 */
-	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
 		memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
 		writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
 		writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
@@ -992,7 +980,7 @@ fec_restart(struct net_device *ndev)
 	 * The phy interface and speed need to get configured
 	 * differently on enet-mac.
 	 */
-	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
 		/* Enable flow control and length check */
 		rcntl |= 0x40000000 | 0x00000020;
 
@@ -1015,7 +1003,7 @@ fec_restart(struct net_device *ndev)
 		}
 	} else {
 #ifdef FEC_MIIGSK_ENR
-		if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
+		if (fep->quirks & FEC_QUIRK_USE_GASKET) {
 			u32 cfgr;
 			/* disable the gasket and wait */
 			writel(0, fep->hwp + FEC_MIIGSK_ENR);
@@ -1068,7 +1056,7 @@ fec_restart(struct net_device *ndev)
 	writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
 #endif
 
-	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
 		/* enable ENET endian swap */
 		ecntl |= (1 << 8);
 		/* enable ENET store and forward mode */
@@ -1102,8 +1090,6 @@ static void
 fec_stop(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
 
 	/* We cannot expect a graceful transmit stop without link !!! */
@@ -1118,7 +1104,7 @@ fec_stop(struct net_device *ndev)
 	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
 	 * instead of reset MAC itself.
 	 */
-	if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) {
+	if (fep->quirks & FEC_QUIRK_HAS_AVB) {
 		writel(0, fep->hwp + FEC_ECNTRL);
 	} else {
 		writel(1, fep->hwp + FEC_ECNTRL);
@@ -1128,7 +1114,7 @@ fec_stop(struct net_device *ndev)
 	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
 
 	/* We have to keep ENET enabled to have MII interrupt stay working */
-	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
 		writel(2, fep->hwp + FEC_ECNTRL);
 		writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
 	}
@@ -1350,8 +1336,6 @@ static int
 fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	struct fec_enet_priv_rx_q *rxq;
 	struct bufdesc *bdp;
 	unsigned short status;
@@ -1365,7 +1349,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
 	u16	vlan_tag;
 	int	index = 0;
 	bool	is_copybreak;
-	bool	need_swap = id_entry->driver_data & FEC_QUIRK_SWAP_FRAME;
+	bool	need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME;
 
 #ifdef CONFIG_M532x
 	flush_cache_all();
@@ -1880,8 +1864,6 @@ failed_clk_ipg:
 static int fec_enet_mii_probe(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	struct phy_device *phy_dev = NULL;
 	char mdio_bus_id[MII_BUS_ID_SIZE];
 	char phy_name[MII_BUS_ID_SIZE + 3];
@@ -1927,7 +1909,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
 	}
 
 	/* mask with MAC supported features */
-	if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
+	if (fep->quirks & FEC_QUIRK_HAS_GBIT) {
 		phy_dev->supported &= PHY_GBIT_FEATURES;
 		phy_dev->supported &= ~SUPPORTED_1000baseT_Half;
 #if !defined(CONFIG_M5272)
@@ -1955,8 +1937,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
 	static struct mii_bus *fec0_mii_bus;
 	struct net_device *ndev = platform_get_drvdata(pdev);
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	struct device_node *node;
 	int err = -ENXIO, i;
 
@@ -1976,7 +1956,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
 	 * mdio interface in board design, and need to be configured by
 	 * fec0 mii_bus.
 	 */
-	if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
+	if ((fep->quirks & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
 		/* fec1 uses fec0 mii_bus */
 		if (mii_cnt && fec0_mii_bus) {
 			fep->mii_bus = fec0_mii_bus;
@@ -1997,7 +1977,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
 	 * document.
 	 */
 	fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
-	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+	if (fep->quirks & FEC_QUIRK_ENET_MAC)
 		fep->phy_speed--;
 	fep->phy_speed <<= 1;
 	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
@@ -2039,7 +2019,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
 	mii_cnt++;
 
 	/* save fec0 mii_bus */
-	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+	if (fep->quirks & FEC_QUIRK_ENET_MAC)
 		fec0_mii_bus = fep->mii_bus;
 
 	return 0;
@@ -2308,11 +2288,9 @@ static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us)
 static void fec_enet_itr_coal_set(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	int rx_itr, tx_itr;
 
-	if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB))
+	if (!(fep->quirks & FEC_QUIRK_HAS_AVB))
 		return;
 
 	/* Must be greater than zero to avoid unpredictable behavior */
@@ -2347,10 +2325,8 @@ static int
 fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 
-	if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB))
+	if (!(fep->quirks & FEC_QUIRK_HAS_AVB))
 		return -EOPNOTSUPP;
 
 	ec->rx_coalesce_usecs = fep->rx_time_itr;
@@ -2366,12 +2342,9 @@ static int
 fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
-
 	unsigned int cycle;
 
-	if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB))
+	if (!(fep->quirks & FEC_QUIRK_HAS_AVB))
 		return -EOPNOTSUPP;
 
 	if (ec->rx_max_coalesced_frames > 255) {
@@ -2951,8 +2924,6 @@ static const struct net_device_ops fec_netdev_ops = {
 static int fec_enet_init(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	const struct platform_device_id *id_entry =
-				platform_get_device_id(fep->pdev);
 	struct fec_enet_priv_tx_q *txq;
 	struct fec_enet_priv_rx_q *rxq;
 	struct bufdesc *cbd_base;
@@ -3031,11 +3002,11 @@ static int fec_enet_init(struct net_device *ndev)
 	writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
 	netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
 
-	if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN)
+	if (fep->quirks & FEC_QUIRK_HAS_VLAN)
 		/* enable hw VLAN support */
 		ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
 
-	if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
+	if (fep->quirks & FEC_QUIRK_HAS_CSUM) {
 		ndev->gso_max_segs = FEC_MAX_TSO_SEGS;
 
 		/* enable hw accelerator */
@@ -3044,7 +3015,7 @@ static int fec_enet_init(struct net_device *ndev)
 		fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
 	}
 
-	if (id_entry->driver_data & FEC_QUIRK_HAS_AVB) {
+	if (fep->quirks & FEC_QUIRK_HAS_AVB) {
 		fep->tx_align = 0;
 		fep->rx_align = 0x3f;
 	}
@@ -3144,10 +3115,6 @@ fec_probe(struct platform_device *pdev)
 	int num_tx_qs;
 	int num_rx_qs;
 
-	of_id = of_match_device(fec_dt_ids, &pdev->dev);
-	if (of_id)
-		pdev->id_entry = of_id->data;
-
 	fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
 
 	/* Init network device */
@@ -3161,13 +3128,17 @@ fec_probe(struct platform_device *pdev)
 	/* setup board info structure */
 	fep = netdev_priv(ndev);
 
+	of_id = of_match_device(fec_dt_ids, &pdev->dev);
+	if (of_id)
+		pdev->id_entry = of_id->data;
+	fep->quirks = pdev->id_entry->driver_data;
+
 	fep->num_rx_queues = num_rx_qs;
 	fep->num_tx_queues = num_tx_qs;
 
 #if !defined(CONFIG_M5272)
 	/* default enable pause frame auto negotiation */
-	if (pdev->id_entry &&
-	    (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT))
+	if (fep->quirks & FEC_QUIRK_HAS_GBIT)
 		fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
 #endif
 
@@ -3236,9 +3207,8 @@ fec_probe(struct platform_device *pdev)
 	if (IS_ERR(fep->clk_ref))
 		fep->clk_ref = NULL;
 
+	fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX;
 	fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
-	fep->bufdesc_ex =
-		pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
 	if (IS_ERR(fep->clk_ptp)) {
 		fep->clk_ptp = NULL;
 		fep->bufdesc_ex = false;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 2/8] net: fec: consistently use lower case chars as hex digits
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>


Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec.h |   88 +++++++++++++++++-----------------
 1 file changed, 44 insertions(+), 44 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 3047db4..e778b9e 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -38,9 +38,9 @@
 #define FEC_ADDR_LOW		0x0e4 /* Low 32bits MAC address */
 #define FEC_ADDR_HIGH		0x0e8 /* High 16bits MAC address */
 #define FEC_OPD			0x0ec /* Opcode + Pause duration */
-#define FEC_TXIC0		0xF0  /* Tx Interrupt Coalescing for ring 0 */
-#define FEC_TXIC1		0xF4  /* Tx Interrupt Coalescing for ring 1 */
-#define FEC_TXIC2		0xF8  /* Tx Interrupt Coalescing for ring 2 */
+#define FEC_TXIC0		0x0f0 /* Tx Interrupt Coalescing for ring 0 */
+#define FEC_TXIC1		0x0f4 /* Tx Interrupt Coalescing for ring 1 */
+#define FEC_TXIC2		0x0f8 /* Tx Interrupt Coalescing for ring 2 */
 #define FEC_RXIC0		0x100 /* Rx Interrupt Coalescing for ring 0 */
 #define FEC_RXIC1		0x104 /* Rx Interrupt Coalescing for ring 1 */
 #define FEC_RXIC2		0x108 /* Rx Interrupt Coalescing for ring 2 */
@@ -62,7 +62,7 @@
 #define FEC_R_FIFO_RSEM		0x194 /* Receive FIFO section empty threshold */
 #define FEC_R_FIFO_RAEM		0x198 /* Receive FIFO almost empty threshold */
 #define FEC_R_FIFO_RAFL		0x19c /* Receive FIFO almost full threshold */
-#define FEC_RACC		0x1C4 /* Receive Accelerator function */
+#define FEC_RACC		0x1c4 /* Receive Accelerator function */
 #define FEC_RCMR_1		0x1c8 /* Receive classification match ring 1 */
 #define FEC_RCMR_2		0x1cc /* Receive classification match ring 2 */
 #define FEC_DMA_CFG_1		0x1d8 /* DMA class configuration for ring 1 */
@@ -82,57 +82,57 @@
 #define RMON_T_DROP		0x200 /* Count of frames not cntd correctly */
 #define RMON_T_PACKETS		0x204 /* RMON TX packet count */
 #define RMON_T_BC_PKT		0x208 /* RMON TX broadcast pkts */
-#define RMON_T_MC_PKT		0x20C /* RMON TX multicast pkts */
+#define RMON_T_MC_PKT		0x20c /* RMON TX multicast pkts */
 #define RMON_T_CRC_ALIGN	0x210 /* RMON TX pkts with CRC align err */
 #define RMON_T_UNDERSIZE	0x214 /* RMON TX pkts < 64 bytes, good CRC */
 #define RMON_T_OVERSIZE		0x218 /* RMON TX pkts > MAX_FL bytes good CRC */
-#define RMON_T_FRAG		0x21C /* RMON TX pkts < 64 bytes, bad CRC */
+#define RMON_T_FRAG		0x21c /* RMON TX pkts < 64 bytes, bad CRC */
 #define RMON_T_JAB		0x220 /* RMON TX pkts > MAX_FL bytes, bad CRC */
 #define RMON_T_COL		0x224 /* RMON TX collision count */
 #define RMON_T_P64		0x228 /* RMON TX 64 byte pkts */
-#define RMON_T_P65TO127		0x22C /* RMON TX 65 to 127 byte pkts */
+#define RMON_T_P65TO127		0x22c /* RMON TX 65 to 127 byte pkts */
 #define RMON_T_P128TO255	0x230 /* RMON TX 128 to 255 byte pkts */
 #define RMON_T_P256TO511	0x234 /* RMON TX 256 to 511 byte pkts */
 #define RMON_T_P512TO1023	0x238 /* RMON TX 512 to 1023 byte pkts */
-#define RMON_T_P1024TO2047	0x23C /* RMON TX 1024 to 2047 byte pkts */
+#define RMON_T_P1024TO2047	0x23c /* RMON TX 1024 to 2047 byte pkts */
 #define RMON_T_P_GTE2048	0x240 /* RMON TX pkts > 2048 bytes */
 #define RMON_T_OCTETS		0x244 /* RMON TX octets */
 #define IEEE_T_DROP		0x248 /* Count of frames not counted crtly */
-#define IEEE_T_FRAME_OK		0x24C /* Frames tx'd OK */
+#define IEEE_T_FRAME_OK		0x24c /* Frames tx'd OK */
 #define IEEE_T_1COL		0x250 /* Frames tx'd with single collision */
 #define IEEE_T_MCOL		0x254 /* Frames tx'd with multiple collision */
 #define IEEE_T_DEF		0x258 /* Frames tx'd after deferral delay */
-#define IEEE_T_LCOL		0x25C /* Frames tx'd with late collision */
+#define IEEE_T_LCOL		0x25c /* Frames tx'd with late collision */
 #define IEEE_T_EXCOL		0x260 /* Frames tx'd with excesv collisions */
 #define IEEE_T_MACERR		0x264 /* Frames tx'd with TX FIFO underrun */
 #define IEEE_T_CSERR		0x268 /* Frames tx'd with carrier sense err */
-#define IEEE_T_SQE		0x26C /* Frames tx'd with SQE err */
+#define IEEE_T_SQE		0x26c /* Frames tx'd with SQE err */
 #define IEEE_T_FDXFC		0x270 /* Flow control pause frames tx'd */
 #define IEEE_T_OCTETS_OK	0x274 /* Octet count for frames tx'd w/o err */
 #define RMON_R_PACKETS		0x284 /* RMON RX packet count */
 #define RMON_R_BC_PKT		0x288 /* RMON RX broadcast pkts */
-#define RMON_R_MC_PKT		0x28C /* RMON RX multicast pkts */
+#define RMON_R_MC_PKT		0x28c /* RMON RX multicast pkts */
 #define RMON_R_CRC_ALIGN	0x290 /* RMON RX pkts with CRC alignment err */
 #define RMON_R_UNDERSIZE	0x294 /* RMON RX pkts < 64 bytes, good CRC */
 #define RMON_R_OVERSIZE		0x298 /* RMON RX pkts > MAX_FL bytes good CRC */
-#define RMON_R_FRAG		0x29C /* RMON RX pkts < 64 bytes, bad CRC */
-#define RMON_R_JAB		0x2A0 /* RMON RX pkts > MAX_FL bytes, bad CRC */
-#define RMON_R_RESVD_O		0x2A4 /* Reserved */
-#define RMON_R_P64		0x2A8 /* RMON RX 64 byte pkts */
-#define RMON_R_P65TO127		0x2AC /* RMON RX 65 to 127 byte pkts */
-#define RMON_R_P128TO255	0x2B0 /* RMON RX 128 to 255 byte pkts */
-#define RMON_R_P256TO511	0x2B4 /* RMON RX 256 to 511 byte pkts */
-#define RMON_R_P512TO1023	0x2B8 /* RMON RX 512 to 1023 byte pkts */
-#define RMON_R_P1024TO2047	0x2BC /* RMON RX 1024 to 2047 byte pkts */
-#define RMON_R_P_GTE2048	0x2C0 /* RMON RX pkts > 2048 bytes */
-#define RMON_R_OCTETS		0x2C4 /* RMON RX octets */
-#define IEEE_R_DROP		0x2C8 /* Count frames not counted correctly */
-#define IEEE_R_FRAME_OK		0x2CC /* Frames rx'd OK */
-#define IEEE_R_CRC		0x2D0 /* Frames rx'd with CRC err */
-#define IEEE_R_ALIGN		0x2D4 /* Frames rx'd with alignment err */
-#define IEEE_R_MACERR		0x2D8 /* Receive FIFO overflow count */
-#define IEEE_R_FDXFC		0x2DC /* Flow control pause frames rx'd */
-#define IEEE_R_OCTETS_OK	0x2E0 /* Octet cnt for frames rx'd w/o err */
+#define RMON_R_FRAG		0x29c /* RMON RX pkts < 64 bytes, bad CRC */
+#define RMON_R_JAB		0x2a0 /* RMON RX pkts > MAX_FL bytes, bad CRC */
+#define RMON_R_RESVD_O		0x2a4 /* Reserved */
+#define RMON_R_P64		0x2a8 /* RMON RX 64 byte pkts */
+#define RMON_R_P65TO127		0x2ac /* RMON RX 65 to 127 byte pkts */
+#define RMON_R_P128TO255	0x2b0 /* RMON RX 128 to 255 byte pkts */
+#define RMON_R_P256TO511	0x2b4 /* RMON RX 256 to 511 byte pkts */
+#define RMON_R_P512TO1023	0x2b8 /* RMON RX 512 to 1023 byte pkts */
+#define RMON_R_P1024TO2047	0x2bc /* RMON RX 1024 to 2047 byte pkts */
+#define RMON_R_P_GTE2048	0x2c0 /* RMON RX pkts > 2048 bytes */
+#define RMON_R_OCTETS		0x2c4 /* RMON RX octets */
+#define IEEE_R_DROP		0x2c8 /* Count frames not counted correctly */
+#define IEEE_R_FRAME_OK		0x2cc /* Frames rx'd OK */
+#define IEEE_R_CRC		0x2d0 /* Frames rx'd with CRC err */
+#define IEEE_R_ALIGN		0x2d4 /* Frames rx'd with alignment err */
+#define IEEE_R_MACERR		0x2d8 /* Receive FIFO overflow count */
+#define IEEE_R_FDXFC		0x2dc /* Flow control pause frames rx'd */
+#define IEEE_R_OCTETS_OK	0x2e0 /* Octet cnt for frames rx'd w/o err */
 
 #else
 
@@ -170,16 +170,16 @@
 /* Not existed in real chip
  * Just for pass build.
  */
-#define FEC_RCMR_1		0xFFF
-#define FEC_RCMR_2		0xFFF
-#define FEC_DMA_CFG_1		0xFFF
-#define FEC_DMA_CFG_2		0xFFF
-#define FEC_TXIC0		0xFFF
-#define FEC_TXIC1		0xFFF
-#define FEC_TXIC2		0xFFF
-#define FEC_RXIC0		0xFFF
-#define FEC_RXIC1		0xFFF
-#define FEC_RXIC2		0xFFF
+#define FEC_RCMR_1		0xfff
+#define FEC_RCMR_2		0xfff
+#define FEC_DMA_CFG_1		0xfff
+#define FEC_DMA_CFG_2		0xfff
+#define FEC_TXIC0		0xfff
+#define FEC_TXIC1		0xfff
+#define FEC_TXIC2		0xfff
+#define FEC_RXIC0		0xfff
+#define FEC_RXIC1		0xfff
+#define FEC_RXIC2		0xfff
 #endif /* CONFIG_M5272 */
 
 
@@ -296,7 +296,7 @@ struct bufdesc_ex {
 
 #define DMA_CLASS_EN		(1 << 16)
 #define FEC_RCMR(X)		((X == 2) ? FEC_RCMR_2 : FEC_RCMR_1)
-#define IDLE_SLOPE_MASK		0xFFFF
+#define IDLE_SLOPE_MASK		0xffff
 #define IDLE_SLOPE_1		0x200 /* BW fraction: 0.5 */
 #define IDLE_SLOPE_2		0x200 /* BW fraction: 0.5 */
 #define IDLE_SLOPE(X)		((X == 1) ? (IDLE_SLOPE_1 & IDLE_SLOPE_MASK) : \
@@ -308,7 +308,7 @@ struct bufdesc_ex {
 #define RCMR_CMP_2		(RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \
 				RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3))
 #define RCMR_CMP(X)		((X == 1) ? RCMR_CMP_1 : RCMR_CMP_2)
-#define FEC_TX_BD_FTYPE(X)	((X & 0xF) << 20)
+#define FEC_TX_BD_FTYPE(X)	((X & 0xf) << 20)
 
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it it best
@@ -359,8 +359,8 @@ struct bufdesc_ex {
 /* ENET interrupt coalescing macro define */
 #define FEC_ITR_CLK_SEL		(0x1 << 30)
 #define FEC_ITR_EN		(0x1 << 31)
-#define FEC_ITR_ICFT(X)		((X & 0xFF) << 20)
-#define FEC_ITR_ICTT(X)		((X) & 0xFFFF)
+#define FEC_ITR_ICFT(X)		((X & 0xff) << 20)
+#define FEC_ITR_ICTT(X)		((X) & 0xffff)
 #define FEC_ITR_ICFT_DEFAULT	200  /* Set 200 frame count threshold */
 #define FEC_ITR_ICTT_DEFAULT	1000 /* Set 1000us timer threshold */
 
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 8/8] net: fec: remove unused return value from swap_buffer()
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>

The return value of swap_buffer() is not used by any caller, thus
remove it.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec_main.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index dbccdf3..1b6d26b 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -287,15 +287,13 @@ static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep,
 	return entries > 0 ? entries : entries + txq->tx_ring_size;
 }
 
-static void *swap_buffer(void *bufaddr, int len)
+static void swap_buffer(void *bufaddr, int len)
 {
 	int i;
 	unsigned int *buf = bufaddr;
 
 	for (i = 0; i < len; i += 4, buf++)
 		swab32s(buf);
-
-	return bufaddr;
 }
 
 static void swap_buffer2(void *dst_buf, void *src_buf, int len)
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 7/8] net: fec: simplify loop counter handling in swap_buffer()
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>

Eliminate the DIV_ROUND_UP() and change the loop counter increment to
4 instead. This results in saving 6 instructions in the functions
assembly code.

Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
---
 drivers/net/ethernet/freescale/fec_main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f73fee7..dbccdf3 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -292,7 +292,7 @@ static void *swap_buffer(void *bufaddr, int len)
 	int i;
 	unsigned int *buf = bufaddr;
 
-	for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++)
+	for (i = 0; i < len; i += 4, buf++)
 		swab32s(buf);
 
 	return bufaddr;
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 0/8] net: fec: assorted cleanup patches
From: Lothar Waßmann @ 2014-11-17  9:51 UTC (permalink / raw)
  To: David S. Miller
  Cc: Lothar Waßmann, netdev, Frank Li, Fugang Duan, Russell King

This patch series is a followup to:
<1415350967-2238-1-git-send-email-LW@KARO-electronics.de>
[PATCHv4 1/1] net: fec: fix regression on i.MX28 introduced by rx_copybreak support
to apply the cleanup patches that were originally sent along with the
bugfix patch.

^ permalink raw reply

* [PATCH] net: team: expose sysfs attributes for each team option
From: Hamad Kadmany @ 2014-11-17 10:11 UTC (permalink / raw)
  To: jiri; +Cc: netdev

Current code provides only netlink API for user space
to read/write options. Exposing sysfs API is useful for
systems that don't have the required netlink
user space support.

Upon registration of team option, corresponding
sysfs attribute is created.

Signed-off-by: Hamad Kadmany <hkadmany@codeaurora.org>
---
 drivers/net/team/team.c | 282 ++++++++++++++++++++++++++++++++++++++++++++++--
 include/linux/if_team.h |   3 +
 2 files changed, 278 insertions(+), 7 deletions(-)

diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 1222229..afd2f8f 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -110,8 +110,198 @@ struct team_option_inst { /* One for each option instance */
 	struct team_option_inst_info info;
 	bool changed;
 	bool removed;
+	bool dev_attr_file_exist;
+	struct device_attribute dev_attr; /* corresponding sysfs attribute */
 };

+static struct attribute *team_sysfs_attrs[] = {
+	NULL,
+};
+
+static struct attribute_group team_sysfs_attr_group = {
+	.attrs = team_sysfs_attrs,
+};
+
+/* Device attributes (sysfs) */
+
+static ssize_t show_attribute(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	struct team *team = dev_get_drvdata(dev);
+	struct team_option_inst *opt_inst;
+	ssize_t ret;
+	struct team_option *option;
+	struct team_gsetter_ctx ctx;
+
+	if (mutex_lock_interruptible(&team->lock))
+		return -ERESTARTSYS;
+
+	opt_inst = container_of(attr, struct team_option_inst, dev_attr);
+	option = opt_inst->option;
+	if (!option->getter) {
+		ret = -EOPNOTSUPP;
+		netdev_err(team->dev,
+			   "Option %s is write only\n", attr->attr.name);
+		goto exit;
+	}
+
+	ctx.info = &opt_inst->info;
+	/* let the option getter do its job */
+	ret = option->getter(team, &ctx);
+	if (ret)
+		goto exit;
+
+	/* translate option's output into sysfs output */
+	switch (option->type) {
+	case TEAM_OPTION_TYPE_U32:
+		ret = scnprintf(buf, PAGE_SIZE, "%u\n", ctx.data.u32_val);
+		break;
+	case TEAM_OPTION_TYPE_STRING:
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", ctx.data.str_val);
+		break;
+	case TEAM_OPTION_TYPE_BINARY:
+		if (ctx.data.bin_val.len > PAGE_SIZE) {
+			netdev_err(team->dev,
+				   "Option output is too long (%d)\n",
+				   ctx.data.bin_val.len);
+			break;
+		}
+
+		memcpy(buf, ctx.data.bin_val.ptr, ctx.data.bin_val.len);
+		ret = ctx.data.bin_val.len;
+		break;
+	case TEAM_OPTION_TYPE_BOOL:
+		ret = scnprintf(buf, PAGE_SIZE, "%d\n", ctx.data.bool_val);
+		break;
+	case TEAM_OPTION_TYPE_S32:
+		ret = scnprintf(buf, PAGE_SIZE, "%d\n", ctx.data.s32_val);
+		break;
+	default:
+		BUG();
+	}
+
+exit:
+	mutex_unlock(&team->lock);
+
+	return ret;
+}
+
+static int team_nl_send_event_options_get(struct team *team,
+					  struct list_head *sel_opt_inst_list);
+
+static ssize_t set_attribute(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct team_option_inst *opt_inst;
+	struct team *team = dev_get_drvdata(dev);
+	int err = 0;
+	struct team_option *option = NULL;
+	struct team_gsetter_ctx ctx;
+
+	LIST_HEAD(opt_inst_list);
+
+	if (mutex_lock_interruptible(&team->lock))
+		return -ERESTARTSYS;
+
+	opt_inst = container_of(attr, struct team_option_inst, dev_attr);
+	option = opt_inst->option;
+	if (!option->setter) {
+		netdev_err(team->dev,
+			   "Option %s is read only\n", attr->attr.name);
+		err = -EOPNOTSUPP;
+		goto exit;
+	}
+
+	ctx.info = &opt_inst->info;
+
+	/* translate sysfs input into option's input */
+	switch (option->type) {
+	case TEAM_OPTION_TYPE_U32:
+		err = kstrtoint(buf, 0, &ctx.data.u32_val);
+		break;
+	case TEAM_OPTION_TYPE_STRING:
+		if (count > TEAM_STRING_MAX_LEN) {
+			netdev_err(team->dev,
+				   "Input buffer too long (%zu)\n", count);
+			err = -EINVAL;
+			break;
+		}
+		ctx.data.str_val = buf;
+		break;
+	case TEAM_OPTION_TYPE_BINARY:
+		ctx.data.bin_val.len = count;
+		ctx.data.bin_val.ptr = buf;
+		break;
+	case TEAM_OPTION_TYPE_BOOL:
+		err = strtobool(buf, &ctx.data.bool_val);
+		break;
+	case TEAM_OPTION_TYPE_S32:
+		err = kstrtoint(buf, 0, &ctx.data.s32_val);
+		break;
+	default:
+		BUG();
+	}
+
+	if (err) {
+		netdev_err(team->dev, "Failed to translate input buffer\n");
+		goto exit;
+	}
+
+	/* let the option setter do its job */
+	err = option->setter(team, &ctx);
+	if (err)
+		goto exit;
+
+	/* propagate option changed event */
+	opt_inst->changed = true;
+	list_add(&opt_inst->tmp_list, &opt_inst_list);
+	err = team_nl_send_event_options_get(team, &opt_inst_list);
+	if (err == -ESRCH) /* no listeners, not a real error */
+		err = 0;
+
+exit:
+	mutex_unlock(&team->lock);
+
+	if (!err)
+		err = count;
+	return err;
+}
+
+/* create sysfs attribute for each option that is being registered */
+static int __team_option_add_sysfs_attr(struct team *team,
+					struct team_option_inst *opt_inst,
+					bool create_sysfs_file)
+{
+	int err = 0;
+	struct device_attribute *new_dev_attr = &opt_inst->dev_attr;
+
+	new_dev_attr->attr.name = opt_inst->option->name;
+	new_dev_attr->attr.mode = S_IRUGO | S_IWUSR;
+	new_dev_attr->show = show_attribute;
+	new_dev_attr->store = set_attribute;
+
+	if (create_sysfs_file) {
+		err = sysfs_create_file(&team->dev->dev.kobj,
+					&new_dev_attr->attr);
+		if (err)
+			netdev_err(team->dev,
+				   "Failed to create sysfs attribute %s\n",
+				   new_dev_attr->attr.name);
+	}
+
+	return err;
+}
+
+static void __team_option_del_sysfs_attr(struct team *team,
+					 struct team_option_inst *opt_inst)
+{
+	if (opt_inst->dev_attr_file_exist)
+		sysfs_remove_file(&team->dev->dev.kobj,
+				  &opt_inst->dev_attr.attr);
+}
+
 static struct team_option *__team_find_option(struct team *team,
 					      const char *opt_name)
 {
@@ -124,8 +314,10 @@ static struct team_option *__team_find_option(struct team *team,
 	return NULL;
 }

-static void __team_option_inst_del(struct team_option_inst *opt_inst)
+static void __team_option_inst_del(struct team *team,
+				   struct team_option_inst *opt_inst)
 {
+	__team_option_del_sysfs_attr(team, opt_inst);
 	list_del(&opt_inst->list);
 	kfree(opt_inst);
 }
@@ -137,7 +329,7 @@ static void __team_option_inst_del_option(struct team *team,

 	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
 		if (opt_inst->option == option)
-			__team_option_inst_del(opt_inst);
+			__team_option_inst_del(team, opt_inst);
 	}
 }

@@ -162,6 +354,7 @@ static int __team_option_inst_add(struct team *team, struct team_option *option,
 		opt_inst->info.array_index = i;
 		opt_inst->changed = true;
 		opt_inst->removed = false;
+		opt_inst->dev_attr_file_exist = false;
 		list_add_tail(&opt_inst->list, &team->option_inst_list);
 		if (option->init) {
 			err = option->init(team, &opt_inst->info);
@@ -170,6 +363,20 @@ static int __team_option_inst_add(struct team *team, struct team_option *option,
 		}

 	}
+
+	/* add sysfs attribute. per-port and array options are skipped */
+	if (!option->per_port && !option->array_size) {
+		/* create the sysfs file only if our state allows it */
+		bool create_sysfs_file = device_is_registered(&team->dev->dev);
+
+		err = __team_option_add_sysfs_attr(team, opt_inst,
+						   create_sysfs_file);
+		if (err)
+			return err;
+
+		opt_inst->dev_attr_file_exist = true;
+	}
+
 	return 0;
 }

@@ -218,7 +425,7 @@ static void __team_option_inst_del_port(struct team *team,
 	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
 		if (opt_inst->option->per_port &&
 		    opt_inst->info.port == port)
-			__team_option_inst_del(opt_inst);
+			__team_option_inst_del(team, opt_inst);
 	}
 }

@@ -337,6 +544,51 @@ static void __team_options_unregister(struct team *team,

 static void __team_options_change_check(struct team *team);

+static void team_attr_grp_free(struct team *team)
+{
+	kfree(team->attr_grp.attrs);
+}
+
+/* allocate attribute group for creating sysfs for team's own options */
+static int team_attr_grp_alloc(struct team *team)
+{
+	struct attribute **attributes;
+	struct team_option_inst *opt_inst;
+	int num_attr = 0;
+	struct team_option *option;
+
+	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+		option = opt_inst->option;
+		/* per-port and array options currently not supported as
+		 * sysfs attributes
+		 */
+		if (option->per_port || option->array_size)
+			continue;
+
+		num_attr++;
+	}
+
+	/* +1 for having NULL as last item in the array */
+	attributes = kzalloc((num_attr + 1) * sizeof(*attributes), GFP_KERNEL);
+	if (!attributes)
+		return -ENOMEM;
+	team->attr_grp.attrs = attributes;
+
+	num_attr = 0;
+	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+		option = opt_inst->option;
+		/* per-port and array options currently not supported as
+		 * sysfs attributes
+		 */
+		if (option->per_port || option->array_size)
+			continue;
+
+		attributes[num_attr++] = &opt_inst->dev_attr.attr;
+	}
+
+	return 0;
+}
+
 int team_options_register(struct team *team,
 			  const struct team_option *option,
 			  size_t option_count)
@@ -1380,15 +1632,28 @@ static int team_init(struct net_device *dev)

 	INIT_LIST_HEAD(&team->option_list);
 	INIT_LIST_HEAD(&team->option_inst_list);
-	err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
+
+	err = team_options_register(team, team_options,
+				    ARRAY_SIZE(team_options));
 	if (err)
 		goto err_options_register;
 	netif_carrier_off(dev);

 	team_set_lockdep_class(dev);

+	/* store team context, to be used by Device attributes getter/setter */
+	dev_set_drvdata(&dev->dev, team);
+
+	/* allocate and register sysfs attributes for team's own options */
+	err = team_attr_grp_alloc(team);
+	if (err)
+		goto err_grp_alloc;
+	dev->sysfs_groups[0] = &team->attr_grp;
+
 	return 0;

+err_grp_alloc:
+	team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
 err_options_register:
 	team_queue_override_fini(team);
 err_team_queue_override_init:
@@ -1407,9 +1672,15 @@ static void team_uninit(struct net_device *dev)
 	list_for_each_entry_safe(port, tmp, &team->port_list, list)
 		team_port_del(team, port->dev);

+	sysfs_remove_group(&team->dev->dev.kobj, &team->attr_grp);
+	team_attr_grp_free(team);
+	/* set to dummy group to avoid double free by core */
+	dev->sysfs_groups[0] = &team_sysfs_attr_group;
+
 	__team_change_mode(team, NULL); /* cleanup */
 	__team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
 	team_queue_override_fini(team);
+
 	mutex_unlock(&team->lock);
 }

@@ -2194,9 +2465,6 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
 	return err;
 }

-static int team_nl_send_event_options_get(struct team *team,
-					  struct list_head *sel_opt_inst_list);
-
 static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
 {
 	struct team *team;
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index 25b8b15..2e9fb2a 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -188,6 +188,9 @@ struct team {
 	struct list_head option_list;
 	struct list_head option_inst_list; /* list of option instances */

+	/* attribute group for registering team's own options at init */
+	struct attribute_group attr_grp;
+
 	const struct team_mode *mode;
 	struct team_mode_ops ops;
 	bool user_carrier_enabled;
-- 
1.8.5.2
-- 
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related

* Re: /proc/net/sockstat invalid memory accounting or memory leak in latest kernels? (trying to debug)
From: Denys Fedoryshchenko @ 2014-11-17 10:22 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: Neal Cardwell, Yuchung Cheng, netdev
In-Reply-To: <1416200496.24093.5.camel@edumazet-glaptop2.roam.corp.google.com>

On 2014-11-17 07:01, Eric Dumazet wrote:
> On Sun, 2014-11-16 at 12:16 -0800, Eric Dumazet wrote:
> 
>> Thanks Denys !
>> 
>> Could you try following patch ?
>> 
>> Thanks !
> 
> Hmm.... I have an updated patch, sorry.
> 
> (A memcpy_fromiovec() has to be memcpy_fromiovecend() )
> 
> diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
> index a3d453b94747..c2bbfcd9c0db 100644
> --- a/net/ipv4/tcp_output.c
> +++ b/net/ipv4/tcp_output.c
> @@ -2998,7 +2998,7 @@ static int tcp_send_syn_data(struct sock *sk,
> struct sk_buff *syn)
>  {
>  	struct tcp_sock *tp = tcp_sk(sk);
>  	struct tcp_fastopen_request *fo = tp->fastopen_req;
> -	int syn_loss = 0, space, i, err = 0, iovlen = fo->data->msg_iovlen;
> +	int syn_loss = 0, space, err = 0;
>  	struct sk_buff *syn_data = NULL, *data;
>  	unsigned long last_syn_loss = 0;
> 
> @@ -3031,25 +3031,19 @@ static int tcp_send_syn_data(struct sock *sk,
> struct sk_buff *syn)
>  	/* limit to order-0 allocations */
>  	space = min_t(size_t, space, SKB_MAX_HEAD(MAX_TCP_HEADER));
> 
> -	syn_data = skb_copy_expand(syn, MAX_TCP_HEADER, space,
> -				   sk->sk_allocation);
> -	if (syn_data == NULL)
> +	syn_data = sk_stream_alloc_skb(sk, space, sk->sk_allocation);
> +	if (!syn_data)
>  		goto fallback;
> 
> -	for (i = 0; i < iovlen && syn_data->len < space; ++i) {
> -		struct iovec *iov = &fo->data->msg_iov[i];
> -		unsigned char __user *from = iov->iov_base;
> -		int len = iov->iov_len;
> -
> -		if (syn_data->len + len > space)
> -			len = space - syn_data->len;
> -		else if (i + 1 == iovlen)
> -			/* No more data pending in inet_wait_for_connect() */
> -			fo->data = NULL;
> +	syn_data->ip_summed = CHECKSUM_PARTIAL;
> +	memcpy(syn_data->cb, syn->cb, sizeof(syn->cb));
> +	if (memcpy_fromiovecend(skb_put(syn_data, space),
> +				fo->data->msg_iov, 0, space))
> +		goto fallback;
> 
> -		if (skb_add_data(syn_data, from, len))
> -			goto fallback;
> -	}
> +	/* No more data pending in inet_wait_for_connect() */
> +	if (space == fo->size)
> +		fo->data = NULL;
> 
>  	/* Queue a data-only packet after the regular SYN for retransmission 
> */
>  	data = pskb_copy(syn_data, sk->sk_allocation);
> @@ -3101,13 +3095,10 @@ int tcp_connect(struct sock *sk)
>  		return 0;
>  	}
> 
> -	buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);
> -	if (unlikely(buff == NULL))
> +	buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
> +	if (unlikely(!buff))
>  		return -ENOBUFS;
> 
> -	/* Reserve space for headers. */
> -	skb_reserve(buff, MAX_TCP_HEADER);
> -
>  	tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
>  	tp->retrans_stamp = tcp_time_stamp;
>  	tcp_connect_queue_skb(sk, buff);

Installed patch, but will have to wait a while (usually at least 
24hours), to be sure if it is stable.

Thanks a lot!

^ permalink raw reply

* RE: [PATCH 0/8] net: fec: assorted cleanup patches
From: fugang.duan @ 2014-11-17 10:52 UTC (permalink / raw)
  To: Lothar Waßmann, David S. Miller
  Cc: netdev@vger.kernel.org, Frank.Li@freescale.com, Russell King
In-Reply-To: <1416217884-20911-1-git-send-email-LW@KARO-electronics.de>

From: Lothar Waßmann <LW@KARO-electronics.de> Sent: Monday, November 17, 2014 5:51 PM
>To: David S. Miller
>Cc: Lothar Waßmann; netdev@vger.kernel.org; Li Frank-B20596; Duan Fugang-
>B38611; Russell King
>Subject: [PATCH 0/8] net: fec: assorted cleanup patches
>
>This patch series is a followup to:
><1415350967-2238-1-git-send-email-LW@KARO-electronics.de>
>[PATCHv4 1/1] net: fec: fix regression on i.MX28 introduced by
>rx_copybreak support to apply the cleanup patches that were originally
>sent along with the bugfix patch.

Thanks for your code clean!

Acked-by: Fugang Duan <B38611@freescale.com>

Regards,
Andy

^ permalink raw reply

* Investment and Management
From: Mrs.Suzanne Mubarak @ 2014-11-17  9:49 UTC (permalink / raw)



Mrs.Suzanne Mubarak
Email: famsuz_mubarak@draze.com
Address:34, Misr Helwan
Maadi,Cairo Egypt

http://www.bbc.co.uk/news/world-africa-13398644
http://www.bbc.co.uk/news/world-middle-east-25446660
http://www.bbc.co.uk/news/world-middle-east-23801010

Re: Investment and Management of 25 million united states.for my  
family in Abroad

N.B. All you need to do is to send your details-telephone,Full name  
and fax number to my lawyer Barrister Alex Thomas on email:
barralexthomas@outlook.com or telephone number+17862540754. He will be  
the one to give you details on the procedure to follow.

Yours faithfully,
MRS SUZANNE MUBARAK

^ permalink raw reply

* Re: [PATCH v4 3/8] net: can: c_can: Add RAMINIT register information to driver data
From: Roger Quadros @ 2014-11-17 11:17 UTC (permalink / raw)
  To: Marc Kleine-Budde, wg
  Cc: wsa, tony, tglx, mugunthanvnm, george.cherian, balbi, nsekhar, nm,
	sergei.shtylyov, linux-omap, linux-can, netdev
In-Reply-To: <54664212.8080206@pengutronix.de>

On 11/14/2014 07:55 PM, Marc Kleine-Budde wrote:
> On 11/07/2014 03:49 PM, Roger Quadros wrote:
>> Some platforms (e.g. TI) need special RAMINIT register handling.
>> Provide a way to store RAMINIT register description in driver data.
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>> ---
>>  drivers/net/can/c_can/c_can.h          | 6 ++++++
>>  drivers/net/can/c_can/c_can_platform.c | 1 +
>>  2 files changed, 7 insertions(+)
>>
>> diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h
>> index 26c975d..3c305a1 100644
>> --- a/drivers/net/can/c_can/c_can.h
>> +++ b/drivers/net/can/c_can/c_can.h
>> @@ -171,6 +171,12 @@ enum c_can_dev_id {
>>  
>>  struct c_can_driver_data {
>>  	enum c_can_dev_id id;
>> +
>> +	/* RAMINIT register description. Optional. */
>> +	u8 num_can;		/* Number of CAN instances on the SoC */
>> +	u8 *raminit_start_bits;	/* Array of START bit positions */
>> +	u8 *raminit_done_bits;	/* Array of DONE bit positions */
> 
> What do you think about making this a struct:
> 
> +struct raminit_bits {
> +       u8 start;
> +       u8 done;
> +};
> 
>  struct c_can_driver_data {
>         enum c_can_dev_id id;
> +
> +       /* RAMINIT register description. Optional. */
> +       const struct raminit_bits *raminit_bits; /* Array of START/DONE bit positions */
> +       u8 raminit_num;         /* Number of CAN instances on the SoC */
> +       bool raminit_pulse;     /* If set, sets and clears START bit (pulse) */
>  };
> 
> The driver data looks like this:
> 
> +static const struct raminit_bits dra7_raminit_bits[] = {
> +       [0] = { .start = 3, .done = 1, },
> +       [1] = { .start = 5, .done = 2, },
> +};
> +
> +static const struct c_can_driver_data dra7_dcan_drvdata = {
> +       .id = BOSCH_D_CAN,
> +       .raminit_num = ARRAY_SIZE(dra7_raminit_bits),
> +       .raminit_bits = dra7_raminit_bits,
> +       .raminit_pulse = true,
> +};
> 
> I'll send an updated series.

Looks better. Thanks.

cheers,
-roger

^ permalink raw reply

* Re: [PATCH v4 3/8] net: can: c_can: Add RAMINIT register information to driver data
From: Marc Kleine-Budde @ 2014-11-17 11:22 UTC (permalink / raw)
  To: Roger Quadros, wg
  Cc: wsa, tony, tglx, mugunthanvnm, george.cherian, balbi, nsekhar, nm,
	sergei.shtylyov, linux-omap, linux-can, netdev
In-Reply-To: <5469D964.8020804@ti.com>

[-- Attachment #1: Type: text/plain, Size: 469 bytes --]

On 11/17/2014 12:17 PM, Roger Quadros wrote:
>> I'll send an updated series.
> 
> Looks better. Thanks.

Can you make a quick test with the new series on real hardware.

Thanks,
Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* Re: [PATCH v4 3/8] net: can: c_can: Add RAMINIT register information to driver data
From: Roger Quadros @ 2014-11-17 11:29 UTC (permalink / raw)
  To: Marc Kleine-Budde, wg
  Cc: wsa, tony, tglx, mugunthanvnm, george.cherian, balbi, nsekhar, nm,
	sergei.shtylyov, linux-omap, linux-can, netdev
In-Reply-To: <5469DA69.7030602@pengutronix.de>

On 11/17/2014 01:22 PM, Marc Kleine-Budde wrote:
> On 11/17/2014 12:17 PM, Roger Quadros wrote:
>>> I'll send an updated series.
>>
>> Looks better. Thanks.
> 
> Can you make a quick test with the new series on real hardware.

Yes. I'll let you know in a while and respond on the v8 thread.

cheers,
-roger

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox