Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: Mahesh Bandewar @ 2014-11-11 22:29 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 | 100 +++++
 drivers/net/Kconfig                 |  18 +
 drivers/net/Makefile                |   1 +
 drivers/net/ipvlan/Makefile         |   7 +
 drivers/net/ipvlan/ipvlan.h         | 164 +++++++
 drivers/net/ipvlan/ipvlan_core.c    | 634 +++++++++++++++++++++++++++
 drivers/net/ipvlan/ipvlan_main.c    | 828 ++++++++++++++++++++++++++++++++++++
 drivers/net/ipvlan/ipvlan_sysfs.c   | 119 ++++++
 include/linux/netdevice.h           |   4 +
 include/uapi/linux/if_link.h        |  15 +
 10 files changed, 1890 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..7eeb90eb8b96
--- /dev/null
+++ b/Documentation/networking/ipvlan.txt
@@ -0,0 +1,100 @@
+
+                            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 (slected) mode. The RX mode is almost identical except 
+that in L3 mode the slaves wont receive any multicast / broadcast traffic.
+
+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. 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..5eae943bb1c5
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -0,0 +1,164 @@
+/*
+ * 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/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/rculist.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_arp.h>
+#include <linux/if_link.h>
+#include <linux/atomic.h>
+#include <linux/if_vlan.h>
+#include <linux/inet.h>
+#include <linux/hash.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..b38c1b8e5031
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -0,0 +1,634 @@
+/* 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(struct arphdr))))
+			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(struct iphdr))))
+			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(struct iphdr))))
+			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 = NULL;
+	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 = NULL;
+		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 = NULL;
+		__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 = NULL;
+		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)) {
+		err = PTR_ERR(dst);
+		dst = NULL;
+		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 = NULL;
+	struct ipvl_addr *addr = NULL;
+	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 = NULL;
+	void *lyr3h = NULL;
+	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 RX_HANDLER_CONSUMED;
+
+		/* 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:
+	return RX_HANDLER_ANOTHER;
+}
+
+static bool ipvlan_external_frame(struct sk_buff *skb, struct ipvl_port *port)
+{
+	struct ethhdr *eth = eth_hdr(skb);
+	struct ipvl_addr *addr = NULL;
+	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 = NULL;
+	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 multi-cast 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 = NULL;
+
+		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;
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
+		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..e87b6eb01060
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -0,0 +1,828 @@
+/* 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;
+	}
+
+	/* TODO will someone try creating ipvlan-dev on an ipvlan-virtual dev?*/
+	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);
+		list_del(&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 = NULL;
+
+	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_ATOMIC)) == 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 = NULL;
+
+	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 = NULL;
+
+	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_ATOMIC)) == 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 = NULL;
+
+	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 888d5513fa4a..0b290c04a469 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 net-next 0/5] net: phy: bcm7xxx: workaround updates
From: Florian Fainelli @ 2014-11-11 22:55 UTC (permalink / raw)
  To: netdev; +Cc: davem, Florian Fainelli

Hi David,

This patch series contains some updates to the Broadcom BCM7xxx internal
PHY driver, including:

- removing an annonying print that would appear during interface up/down and
  suspend/resume cycles
- drop a workaround sequence for a non-production PHY revision
- add new workarounds for the latest and greatest PHY devices found out ther

Thanks!

Florian Fainelli (5):
  net: phy: bcm7xxx: only show PHY revision once
  net: phy: bcm7xxx: drop A0 revision workaround and fix B0 workaround
  net: phy: bcm7xxx: introduce r_rc_cal_reset helper
  net: phy: bcm7xxx: add PHY revision D0 workaround sequence
  net: phy: bcm7xxx: add workaround for PHY revision E0 and F0

 drivers/net/phy/bcm7xxx.c | 134 +++++++++++++++++++++++++++++++---------------
 1 file changed, 90 insertions(+), 44 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [PATCH net-next 1/5] net: phy: bcm7xxx: only show PHY revision once
From: Florian Fainelli @ 2014-11-11 22:55 UTC (permalink / raw)
  To: netdev; +Cc: davem, Florian Fainelli
In-Reply-To: <1415746514-11189-1-git-send-email-f.fainelli@gmail.com>

bcm7xxx_28nm_config_init() can be called as frequently as needed by the
PHY library upon suspend/resume cycles and interface bring up/down, just
print the PHY revision once and for all in order not to spam kernel
logs.

Fixes: d8ebfed3f11b ("net: phy: bcm7xxx: utilize PHY revision in config_init")
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/phy/bcm7xxx.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 1d211d369039..25b07a4d31f8 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -200,7 +200,8 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
 	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
 	int ret = 0;
 
-	dev_info(&phydev->dev, "PHY revision: 0x%02x, patch: %d\n", rev, patch);
+	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
+		     dev_name(&phydev->dev), phydev->drv->name, rev, patch);
 
 	switch (rev) {
 	case 0xa0:
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 2/5] net: phy: bcm7xxx: drop A0 revision workaround and fix B0 workaround
From: Florian Fainelli @ 2014-11-11 22:55 UTC (permalink / raw)
  To: netdev; +Cc: davem, Florian Fainelli
In-Reply-To: <1415746514-11189-1-git-send-email-f.fainelli@gmail.com>

bcm7445_config_init() was working around non-production version of the
PHY HW block, so just remove it entirely.

bcm7xxx_28nm_afe_config_init() was running for all PHY revisions greater
than B0, but this workaround sequence is really specific to the B0 PHY
revision, so rename the function accordingly and update the GPHY macro
to use the generic config_init callback.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/phy/bcm7xxx.c | 41 +++--------------------------------------
 1 file changed, 3 insertions(+), 38 deletions(-)

diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 25b07a4d31f8..417e16c618fa 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -45,39 +45,6 @@
 
 #define CORE_EXPB0			0xb0
 
-static int bcm7445_config_init(struct phy_device *phydev)
-{
-	int ret;
-	const struct bcm7445_regs {
-		int reg;
-		u16 value;
-	} bcm7445_regs_cfg[] = {
-		/* increases ADC latency by 24ns */
-		{ MII_BCM54XX_EXP_SEL, 0x0038 },
-		{ MII_BCM54XX_EXP_DATA, 0xAB95 },
-		/* increases internal 1V LDO voltage by 5% */
-		{ MII_BCM54XX_EXP_SEL, 0x2038 },
-		{ MII_BCM54XX_EXP_DATA, 0xBB22 },
-		/* reduce RX low pass filter corner frequency */
-		{ MII_BCM54XX_EXP_SEL, 0x6038 },
-		{ MII_BCM54XX_EXP_DATA, 0xFFC5 },
-		/* reduce RX high pass filter corner frequency */
-		{ MII_BCM54XX_EXP_SEL, 0x003a },
-		{ MII_BCM54XX_EXP_DATA, 0x2002 },
-	};
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(bcm7445_regs_cfg); i++) {
-		ret = phy_write(phydev,
-				bcm7445_regs_cfg[i].reg,
-				bcm7445_regs_cfg[i].value);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-
 static void phy_write_exp(struct phy_device *phydev,
 					u16 reg, u16 value)
 {
@@ -102,7 +69,7 @@ static void phy_write_misc(struct phy_device *phydev,
 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
 }
 
-static int bcm7xxx_28nm_afe_config_init(struct phy_device *phydev)
+static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
 {
 	/* Increase VCO range to prevent unlocking problem of PLL at low
 	 * temp
@@ -204,12 +171,10 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
 		     dev_name(&phydev->dev), phydev->drv->name, rev, patch);
 
 	switch (rev) {
-	case 0xa0:
 	case 0xb0:
-		ret = bcm7445_config_init(phydev);
+		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
 		break;
 	default:
-		ret = bcm7xxx_28nm_afe_config_init(phydev);
 		break;
 	}
 
@@ -337,7 +302,7 @@ static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
 	.features	= PHY_GBIT_FEATURES |				\
 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,	\
 	.flags		= PHY_IS_INTERNAL,				\
-	.config_init	= bcm7xxx_28nm_afe_config_init,			\
+	.config_init	= bcm7xxx_28nm_config_init,			\
 	.config_aneg	= genphy_config_aneg,				\
 	.read_status	= genphy_read_status,				\
 	.resume		= bcm7xxx_28nm_resume,				\
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 3/5] net: phy: bcm7xxx: introduce r_rc_cal_reset helper
From: Florian Fainelli @ 2014-11-11 22:55 UTC (permalink / raw)
  To: netdev; +Cc: davem, Florian Fainelli
In-Reply-To: <1415746514-11189-1-git-send-email-f.fainelli@gmail.com>

This function performs a R/RC calibration reset and will start being
used by more than one function in the next patches, create a helper
function to factor code.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/phy/bcm7xxx.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 417e16c618fa..82824dde7f5f 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -69,6 +69,15 @@ static void phy_write_misc(struct phy_device *phydev,
 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
 }
 
+static void r_rc_cal_reset(struct phy_device *phydev)
+{
+	/* Reset R_CAL/RC_CAL Engine */
+	phy_write_exp(phydev, 0x00b0, 0x0010);
+
+	/* Disable Reset R_AL/RC_CAL Engine */
+	phy_write_exp(phydev, 0x00b0, 0x0000);
+}
+
 static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
 {
 	/* Increase VCO range to prevent unlocking problem of PLL at low
@@ -90,11 +99,7 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
 	/* Switch to CORE_BASE1E */
 	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
 
-	/* Reset R_CAL/RC_CAL Engine */
-	phy_write_exp(phydev, CORE_EXPB0, 0x0010);
-
-	/* Disable Reset R_CAL/RC_CAL Engine */
-	phy_write_exp(phydev, CORE_EXPB0, 0x0000);
+	r_rc_cal_reset(phydev);
 
 	/* write AFE_RXCONFIG_0 */
 	phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 4/5] net: phy: bcm7xxx: add PHY revision D0 workaround sequence
From: Florian Fainelli @ 2014-11-11 22:55 UTC (permalink / raw)
  To: netdev; +Cc: davem, Florian Fainelli
In-Reply-To: <1415746514-11189-1-git-send-email-f.fainelli@gmail.com>

PHY revision D0 requires a specific workaround sequence which needs to
be applied to get the HW to behave properly in all corner cases
conditions. Do this based on the revision we just read out of the HW
using a specific function.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/phy/bcm7xxx.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 82824dde7f5f..0ce527744513 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -39,8 +39,11 @@
 
 #define AFE_RXCONFIG_0			MISC_ADDR(0x38, 0)
 #define AFE_RXCONFIG_1			MISC_ADDR(0x38, 1)
+#define AFE_RXCONFIG_2			MISC_ADDR(0x38, 2)
 #define AFE_RX_LP_COUNTER		MISC_ADDR(0x38, 3)
 #define AFE_TX_CONFIG			MISC_ADDR(0x39, 0)
+#define AFE_VDCA_ICTRL_0		MISC_ADDR(0x39, 1)
+#define AFE_VDAC_OTHERS_0		MISC_ADDR(0x39, 3)
 #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0)
 
 #define CORE_EXPB0			0xb0
@@ -119,6 +122,46 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
 	return 0;
 }
 
+static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
+{
+	/* AFE_RXCONFIG_0 */
+	phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
+
+	/* AFE_RXCONFIG_1 */
+	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
+
+	/* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
+	phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
+
+	/* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
+	phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
+
+	/* AFE_TX_CONFIG, set 1000BT Cfeed=110 for all ports */
+	phy_write_misc(phydev, AFE_TX_CONFIG, 0x0061);
+
+	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
+	phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
+
+	/* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
+	phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
+
+	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
+	 * offset for HT=0 code
+	 */
+	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
+
+	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
+	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010);
+
+	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
+	phy_write_misc(phydev, DSP_TAP10, 0x011b);
+
+	/* Reset R_CAL/RC_CAL engine */
+	r_rc_cal_reset(phydev);
+
+	return 0;
+}
+
 static int bcm7xxx_apd_enable(struct phy_device *phydev)
 {
 	int val;
@@ -179,6 +222,9 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
 	case 0xb0:
 		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
 		break;
+	case 0xd0:
+		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
+		break;
 	default:
 		break;
 	}
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 5/5] net: phy: bcm7xxx: add workaround for PHY revision E0 and F0
From: Florian Fainelli @ 2014-11-11 22:55 UTC (permalink / raw)
  To: netdev; +Cc: davem, Florian Fainelli
In-Reply-To: <1415746514-11189-1-git-send-email-f.fainelli@gmail.com>

PHY revisions E0 and F0 share the same shorter workaround initialization
sequence. Dedicate a special function for these two PHY revisions to
perform the needed workaround sequence.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/phy/bcm7xxx.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 0ce527744513..1b5f3c1a5093 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -162,6 +162,31 @@ static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
 	return 0;
 }
 
+static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
+{
+	/* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
+	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
+
+	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
+	phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
+
+	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
+	 * offset for HT=0 code
+	 */
+	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
+
+	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
+	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010);
+
+	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
+	phy_write_misc(phydev, DSP_TAP10, 0x011b);
+
+	/* Reset R_CAL/RC_CAL engine */
+	r_rc_cal_reset(phydev);
+
+	return 0;
+}
+
 static int bcm7xxx_apd_enable(struct phy_device *phydev)
 {
 	int val;
@@ -225,6 +250,10 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
 	case 0xd0:
 		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
 		break;
+	case 0xe0:
+	case 0xf0:
+		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
+		break;
 	default:
 		break;
 	}
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH] irda: Remove IRDA_<TYPE> logging macros
From: David Miller @ 2014-11-11 23:11 UTC (permalink / raw)
  To: joe; +Cc: samuel, netdev
In-Reply-To: <1415741850.16070.13.camel@perches.com>

From: Joe Perches <joe@perches.com>
Date: Tue, 11 Nov 2014 13:37:30 -0800

> And use the more common mechanisms directly.
> 
> Other miscellanea:
> 
> o Coalesce formats
> o Add missing newlines
> o Realign arguments
> o Remove unnecessary OOM message logging as
>   there's a generic stack dump already on OOM.
> 
> Signed-off-by: Joe Perches <joe@perches.com>
> ---
> 
> Some long line warnings still exist for individual variables.
> switch/case style was also not changed.

Looks good, applied, thanks Joe.

^ permalink raw reply

* Re: [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: Cong Wang @ 2014-11-11 23:12 UTC (permalink / raw)
  To: Mahesh Bandewar
  Cc: netdev, Eric Dumazet, Maciej Zenczykowski, Laurent Chavey,
	Tim Hockin, David Miller, Brandon Philips, Pavel Emelianov
In-Reply-To: <1415744984-25802-1-git-send-email-maheshb@google.com>

On Tue, Nov 11, 2014 at 2:29 PM, Mahesh Bandewar <maheshb@google.com> wrote:
> 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.

Why do we need this from the beginning?
IOW, what problem does this solve while macvlan doesn't?


>
> 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.
>

Which virtual device is still not aware of netns now? I'd be surprised.

I _guess_ you mean the if_link of macvlan, that is unfortunately
not just macvlan, but because of netns isolates from L2, it is purely
a display problem, macvlan should work well, just its netlink dump
is confusing.

^ permalink raw reply

* Re: [PATCH V2] net: qualcomm: Fix dependency
From: David Miller @ 2014-11-11 23:13 UTC (permalink / raw)
  To: stefan.wahren; +Cc: netdev
In-Reply-To: <1415745480-23692-1-git-send-email-stefan.wahren@i2se.com>

From: Stefan Wahren <stefan.wahren@i2se.com>
Date: Tue, 11 Nov 2014 22:38:00 +0000

> This patch removes the dependency of the VENDOR entry and fixes
> the QCA7000 one.
> 
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> ---
> 
> change in V2: remove dependency of the VENDOR entry suggested by
> David Miller

Yep, looks good, applied.

^ permalink raw reply

* Re: [patch net-next 2/2] sched: introduce vlan action
From: Cong Wang @ 2014-11-11 23:18 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, David Miller, Jamal Hadi Salim, Pravin B Shelar,
	Tom Herbert, Eric Dumazet, willemb, Daniel Borkmann, mst,
	Florian Westphal, Paul.Durrant, Thomas Graf
In-Reply-To: <1415700789-9171-2-git-send-email-jiri@resnulli.us>

On Tue, Nov 11, 2014 at 2:13 AM, Jiri Pirko <jiri@resnulli.us> wrote:
> This tc action allows to work with vlan tagged skbs. Two supported
> sub-actions are header pop and header push.
>

Can we add this to skbedit instead of adding a new action?

I know vlan tag is not exactly the skb metadata, but still seems
fits in skbedit for me.

^ permalink raw reply

* Re: [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: David Miller @ 2014-11-11 23:19 UTC (permalink / raw)
  To: cwang
  Cc: maheshb, netdev, edumazet, maze, chavey, thockin, brandon.philips,
	xemul
In-Reply-To: <CAHA+R7OZxCLFYetGHONWkL7uzGur=xSc=ASBT4i5EEeS2Xzwog@mail.gmail.com>

From: Cong Wang <cwang@twopensource.com>
Date: Tue, 11 Nov 2014 15:12:27 -0800

> On Tue, Nov 11, 2014 at 2:29 PM, Mahesh Bandewar <maheshb@google.com> wrote:
>> 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.
> 
> Why do we need this from the beginning?
> IOW, what problem does this solve while macvlan doesn't?

macvlan has several built-in limitations, which IP VLAN absolutely
does not have.

Eric Dumazet spoke about this at the networking track at the kernel
summit in Chicago, maybe he or another person working on this can
chime in.

^ permalink raw reply

* Re: [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: Hannes Frederic Sowa @ 2014-11-11 23:22 UTC (permalink / raw)
  To: Cong Wang
  Cc: Mahesh Bandewar, netdev, Eric Dumazet, Maciej Zenczykowski,
	Laurent Chavey, Tim Hockin, David Miller, Brandon Philips,
	Pavel Emelianov
In-Reply-To: <CAHA+R7OZxCLFYetGHONWkL7uzGur=xSc=ASBT4i5EEeS2Xzwog@mail.gmail.com>

On Di, 2014-11-11 at 15:12 -0800, Cong Wang wrote:
> On Tue, Nov 11, 2014 at 2:29 PM, Mahesh Bandewar <maheshb@google.com> wrote:
> > 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.
> 
> Why do we need this from the beginning?
> IOW, what problem does this solve while macvlan doesn't?

I think it is good to reduce the number of mac addresses before a NIC
switches into promisc mode.

Bye,
Hannes

^ permalink raw reply

* Re: [PATCH net 0/2] net: bcmgenet: power management related fixes
From: David Miller @ 2014-11-11 23:24 UTC (permalink / raw)
  To: f.fainelli; +Cc: netdev
In-Reply-To: <1415671581-2835-1-git-send-email-f.fainelli@gmail.com>

From: Florian Fainelli <f.fainelli@gmail.com>
Date: Mon, 10 Nov 2014 18:06:19 -0800

> These two patches fixes issues seen while testing power management on
> platforms using the GENET driver.
> 
> First patch fixes an issue with the PHY state machine queuing work after
> resume since we are not properly detached from it.
> 
> Second patch fixes an issue with GENET interfaces that were not properly
> restored to a working state after a S3 suspend/resume cycle.

Series applied, thanks.

^ permalink raw reply

* [PATCH net-next] hyperv: Add processing of MTU reduced by the host
From: Haiyang Zhang @ 2014-11-11 23:27 UTC (permalink / raw)
  To: davem, netdev; +Cc: olaf, jasowang, driverdev-devel, linux-kernel, haiyangz

If the host uses packet encapsulation feature, the MTU may be reduced by the
host due to headroom reservation for encapsulation. This patch handles this
new MTU value.

Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
---
 drivers/net/hyperv/netvsc.c       |    3 ++-
 drivers/net/hyperv/netvsc_drv.c   |    4 ++--
 drivers/net/hyperv/rndis_filter.c |    9 +++++++++
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 7d76c95..6b46311 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -440,7 +440,8 @@ static int negotiate_nvsp_ver(struct hv_device *device,
 	/* NVSPv2 only: Send NDIS config */
 	memset(init_packet, 0, sizeof(struct nvsp_message));
 	init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
-	init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu;
+	init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu +
+						       ETH_HLEN;
 	init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1;
 
 	ret = vmbus_sendpacket(device->channel, init_packet,
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 3295e4e..b689f96 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -699,9 +699,9 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
 		return -ENODEV;
 
 	if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2)
-		limit = NETVSC_MTU;
+		limit = NETVSC_MTU - ETH_HLEN;
 
-	if (mtu < 68 || mtu > limit)
+	if (mtu < ETH_DATA_LEN || mtu > limit)
 		return -EINVAL;
 
 	nvdev->start_remove = true;
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index ccce6f2..7b2c5d1 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -998,6 +998,7 @@ int rndis_filter_device_add(struct hv_device *dev,
 	int t;
 	struct ndis_recv_scale_cap rsscap;
 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
+	u32 mtu, size;
 
 	rndis_device = get_rndis_device();
 	if (!rndis_device)
@@ -1029,6 +1030,14 @@ int rndis_filter_device_add(struct hv_device *dev,
 		return ret;
 	}
 
+	/* Get the MTU from the host */
+	size = sizeof(u32);
+	ret = rndis_filter_query_device(rndis_device,
+					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
+					&mtu, &size);
+	if (ret == 0 && size == sizeof(u32))
+		net_device->ndev->mtu = mtu;
+
 	/* Get the mac address */
 	ret = rndis_filter_query_device_mac(rndis_device);
 	if (ret != 0) {
-- 
1.7.1

^ permalink raw reply related

* Re: [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: Eric Dumazet @ 2014-11-11 23:28 UTC (permalink / raw)
  To: Mahesh Bandewar
  Cc: netdev, Eric Dumazet, Maciej Zenczykowski, Laurent Chavey,
	Tim Hockin, David Miller, Brandon Philips, Pavel Emelianov
In-Reply-To: <1415744984-25802-1-git-send-email-maheshb@google.com>

On Tue, 2014-11-11 at 14:29 -0800, Mahesh Bandewar wrote:

...

> +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(struct arphdr))))
> +			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(struct iphdr))))
> +			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(struct iphdr))))

	sizeof(struct ipv6hdr) or 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;
> +}

...
> +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)) {
> +		err = PTR_ERR(dst);
> +		dst = NULL;

dst = NULL; seems not needed.

> +		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 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 multi-cast 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 = NULL;


= NULL; not needed.

> +
> +		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;
> +
> +	if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))

This looks strange. 

Here we are sure ethernet header was already pulled by eth_type_trans()

> +		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..e87b6eb01060
> --- /dev/null
> +++ b/drivers/net/ipvlan/ipvlan_main.c
> @@ -0,0 +1,828 @@
> +/* 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.
> + *
> + */
> +
> +
...

> +static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
> +{
> +	struct ipvl_addr *addr = NULL;
> +
> +	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_ATOMIC)) == NULL)

Why is GFP_ATOMIC used here ?

> +		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 = NULL;
> +
> +	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 = NULL;
> +
> +	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_ATOMIC)) == NULL)

Same issue here ? GFP_KERNEL should be OK.

^ permalink raw reply

* Re: [PATCH] smsc911x: power-up phydev before doing a software reset.
From: David Miller @ 2014-11-11 23:30 UTC (permalink / raw)
  To: eballetbo; +Cc: netdev, steve.glendinning, javier, ebutera
In-Reply-To: <1415643789-5367-1-git-send-email-eballetbo@iseebcn.com>

From: Enric Balletbo i Serra <eballetbo@iseebcn.com>
Date: Mon, 10 Nov 2014 19:23:09 +0100

> With commit be9dad1f9f26604fb ("net: phy: suspend phydev when going
> to HALTED"), the PHY device will be put in a low-power mode using
> BMCR_PDOWN if the the interface is set down. The smsc911x driver does
> a software_reset opening the device driver (ndo_open). In such case,
> the PHY must be powered-up before access to any register and before
> calling the software_reset function. Otherwise, as the PHY is powered
> down the software reset fails and the interface can not be enabled
> again.
> 
> This patch fixes this scenario that is easy to reproduce setting down
> the network interface and setting up again.
> 
>     $ ifconfig eth0 down
>     $ ifconfig eth0 up
>     ifconfig: SIOCSIFFLAGS: Input/output error
> 
> Signed-off-by: Enric Balletbo i Serra <eballetbo@iseebcn.com>
 ...
> +		mdelay(1);

As per Javier's feedback, please convert this to usleep_range()
if you agree that this can only be invoked from process context.

Thanks.

^ permalink raw reply

* Re: "asix: Don't reset PHY on if_up for ASIX 88772" breaks net on arndale platform
From: Ben Hutchings @ 2014-11-12  0:23 UTC (permalink / raw)
  To: Charles Keepax
  Cc: Stam, Michel [FINT], Riku Voipio, davem, linux-usb, netdev,
	linux-kernel, linux-samsung-soc
In-Reply-To: <20141104200914.GN23178@opensource.wolfsonmicro.com>

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

On Tue, 2014-11-04 at 20:09 +0000, Charles Keepax wrote:
> On Tue, Nov 04, 2014 at 11:23:06AM +0100, Stam, Michel [FINT] wrote:
> > Hello Riku,
> > 
> > >Fixing a bug (ethtool support) must not cause breakage elsewhere (in
> > this case on arndale). This is now a regression of functionality from
> > 3.17.
> > >
> > >I think it would better to revert the change now and with less hurry
> > introduce a ethtool fix that doesn't break arndale.
> > 
> > I don't fully agree here; 
> > I would like to point out that this commit is a revert itself. Fixing
> > the armdale will then cause breakage in other implementations, such as
> > ours. Blankly reverting breaks other peoples' implementations.
> > 
> > The PHY reset is the thing that breaks ethtool support, so any fix that
> > appeases all would have to take existing PHY state into account. 
[...]
> --- a/drivers/net/usb/asix_devices.c
> +++ b/drivers/net/usb/asix_devices.c
> @@ -299,6 +299,7 @@ static int ax88772_reset(struct usbnet *dev)
>  {
>         struct asix_data *data = (struct asix_data *)&dev->data;
>         int ret, embd_phy;
> +       int reg;
>         u16 rx_ctl;
> 
>         ret = asix_write_gpio(dev,
> @@ -359,8 +360,10 @@ static int ax88772_reset(struct usbnet *dev)
>         msleep(150);
> 
>         asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
> -       asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
> -                       ADVERTISE_ALL | ADVERTISE_CSMA);
> +       reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_ADVERTISE);
> +       if (!reg)
> +               reg = ADVERTISE_ALL | ADVERTISE_CSMA;
> +       asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, reg);
[...]

Why is there no sleep after setting the RESET bit?  Doesn't that make
the following register writes unreliable?

Ben.

-- 
Ben Hutchings
Experience is directly proportional to the value of equipment destroyed.
                                                         - Carolyn Scheppner

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 811 bytes --]

^ permalink raw reply

* Re: [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: Cong Wang @ 2014-11-12  0:37 UTC (permalink / raw)
  To: David Miller
  Cc: Mahesh Bandewar, netdev, Eric Dumazet, Maciej Żenczykowski,
	Laurent Chavey, Tim Hockin, Brandon Philips, Pavel Emelyanov
In-Reply-To: <20141111.181900.1713364166494134373.davem@davemloft.net>

On Tue, Nov 11, 2014 at 3:19 PM, David Miller <davem@davemloft.net> wrote:
> From: Cong Wang <cwang@twopensource.com>
> Date: Tue, 11 Nov 2014 15:12:27 -0800
>
>> On Tue, Nov 11, 2014 at 2:29 PM, Mahesh Bandewar <maheshb@google.com> wrote:
>>> 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.
>>
>> Why do we need this from the beginning?
>> IOW, what problem does this solve while macvlan doesn't?
>
> macvlan has several built-in limitations, which IP VLAN absolutely
> does not have.
>
> Eric Dumazet spoke about this at the networking track at the kernel
> summit in Chicago, maybe he or another person working on this can
> chime in.

Either you need to publish it or document it in this changelog,
otherwise too much information is missing.

^ permalink raw reply

* Re: [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: Cong Wang @ 2014-11-12  0:39 UTC (permalink / raw)
  To: Hannes Frederic Sowa
  Cc: Mahesh Bandewar, netdev, Eric Dumazet, Maciej Zenczykowski,
	Laurent Chavey, Tim Hockin, David Miller, Brandon Philips,
	Pavel Emelianov
In-Reply-To: <1415748160.2292.8.camel@localhost>

On Tue, Nov 11, 2014 at 3:22 PM, Hannes Frederic Sowa <hannes@redhat.com> wrote:
> On Di, 2014-11-11 at 15:12 -0800, Cong Wang wrote:
>> On Tue, Nov 11, 2014 at 2:29 PM, Mahesh Bandewar <maheshb@google.com> wrote:
>> > 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.
>>
>> Why do we need this from the beginning?
>> IOW, what problem does this solve while macvlan doesn't?
>
> I think it is good to reduce the number of mac addresses before a NIC
> switches into promisc mode.
>

Sounds like over-kill to have a new device just for not worrying about mac.
Or you mean our neigh table doesn't scale?

^ permalink raw reply

* Re: [PATCH] brcmfmac: unlink URB when request timed out
From: Mathy Vanhoef @ 2014-11-12  0:40 UTC (permalink / raw)
  To: Arend van Spriel, brudley, frankyl, meuleman, linville, pieterpg,
	linux-wireless, brcm80211-dev-list, netdev, Oliver Neukum
In-Reply-To: <54624EB4.5060501@broadcom.com>

On 11/11/2014 01:00 PM, Arend van Spriel wrote:
> On 11-11-14 18:35, Mathy Vanhoef wrote:
>> Using usb_kill_urb() instead of usb_unlink_urb() seems to work without any problems.
> 
> Ok, as usb_kill_urb() assures the completion handler is called there is
> no need to check the ctl_completed flag, ie. my patch. Go ahead and
> resubmit your patch replacing usb_unlink_urb() by usb_kill_urb().
> 
> Do you mean you end up with a working usb device providing a wireless
> interface? Or does probe still fail, but you do not get a crash.

I end up with a working device which can connect to networks.

Kind regards,
Mathy

> Regards,
> Arend
> 
>> On 11/11/2014 06:05 AM, Arend van Spriel wrote:
>>> On 10-11-14 17:08, Mathy Vanhoef wrote:
>>>> On 11/10/2014 06:18 AM, Arend van Spriel wrote:
>>>>> On 09-11-14 19:10, Mathy Vanhoef wrote:
>>>>>> From: Mathy Vanhoef <vanhoefm@gmail.com>
>>>>>>
>>>>>> Unlink the submitted URB in brcmf_usb_dl_cmd if the request timed out. This
>>>>>> assures the URB is never submitted twice, preventing a driver crash.
>>>>>
>>>>> Hi Mathy,
>>>>>
>>>>> What driver crash are you referring to? The log only shows the WARNING
>>>>> ending in a USB disconnect but no actual crash. Does your patch get the
>>>>> driver running properly or does it only avoid the warning.
>>>>
>>>> Hi Arend,
>>>>
>>>> It shows a warning, after which the device doesn't work (but the computer is
>>>> still usable). But I've noticed that when *unplugging* the USB cable the OS may
>>>> freeze. This doesn't always happen though, sometimes unplugging works OK. The
>>>> patch both avoids the warning, and gets the device/driver running properly
>>>> (unplugging also works OK).
>>>>
>>>>>
>>>>> With that said, it seems there is some need for improvement, but I also
>>>>> notice you are running this on a virtual machine so could that affect
>>>>> the timeout to kick in before completion. Could you try to increase the
>>>>> timeout. Still when a timeout occurs this needs to be handled properly.
>>>>> Could you also try the following patch?
>>>>
>>>> I did a few additional tests:
>>>>
>>>> 1. When increasing IOCTL_RESP_TIMEOUT to 20000 (ten times the normal value) the
>>>>    timeout and warning still occur. Device/driver doesn't work.
>>>> 2. When increasing BRCMF_USB_RESET_GETVER_SPINWAIT to 1000 (ten timers the
>>>>    normal value) everything works. Device/driver works.
>>>
>>> This means the ctl_urb completes on your system within 3sec, but not
>>> within 2.1sec. After discussing this with my colleague, we think you
>>> should use usb_kill_urb() instead of usb_unlink_urb() as it assures the
>>> completion handler is called. Could you retest that and let us know.
>>>
>>> Regards,
>>> Arend
>>>
>>>> 3. Quick test using backports-3.18-rc1-1 (aka unpatched driver) on a non-
>>>>    virtualized Linux install: In that case everything worked fine. So the bug
>>>>    may only be triggered in a virtualized environment / VMWare.
>>>> 4. When applying your patch, the driver stops early during initialization of
>>>>    the device. I included a WARN_ONCE before returning EINVAL and got the
>>>>    output below.
>>>>
>>>> Kind regards,
>>>> Mathy
>>>> ---
>>>> [  220.955647] usb 1-1: new high-speed USB device number 3 using ehci-pci
>>>> [  221.487797] usb 1-1: New USB device found, idVendor=043e, idProduct=3004
>>>> [  221.487802] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
>>>> [  221.487804] usb 1-1: Product: Remote Download Wireless Adapter
>>>> [  221.487806] usb 1-1: Manufacturer: Broadcom
>>>> [  221.487808] usb 1-1: SerialNumber: 000000000001
>>>> [  221.490472] brcmfmac: brcmf_usb_probe Enter 0x043e:0x3004
>>>> [  221.490476] brcmfmac: brcmf_usb_probe Broadcom high speed USB WLAN interface detected
>>>> [  221.490477] brcmfmac: brcmf_usb_probe_cb Enter
>>>> [  221.490480] brcmfmac: brcmf_usb_attach Enter
>>>> [  221.490494] brcmfmac: brcmf_usb_dlneeded Enter
>>>> [  221.490495] ------------[ cut here ]------------
>>>> [  221.490503] WARNING: CPU: 0 PID: 100 at drivers/net/wireless/brcm80211/brcmfmac/usb.c:716 brcmf_usb_dl_cmd+0x75/0x1a0 [brcmfmac]()
>>>> [  221.490505] EINVAL devinfo=c0044000 ctl_rub=ef898380 completed=0
>>>> [  221.490506] Modules linked in: brcmfmac brcmutil vmw_pvscsi pcnet32 mptspi mptscsih mptbase
>>>> [  221.490514] CPU: 0 PID: 100 Comm: kworker/0:1 Not tainted 3.18.0-rc3-wl+ #2
>>>> [  221.490515] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/31/2013
>>>> [  221.490528] Workqueue: usb_hub_wq hub_event
>>>> [  221.490530]  00000000 00000000 eecffb58 c1711f4a eecffb98 eecffb88 c103edaf f11cbc58
>>>> [  221.490534]  eecffbb4 00000064 f11cbc84 000002cc f11c1595 f11c1595 c0044000 ffffffea
>>>> [  221.490537]  ef726000 eecffba0 c103ee4e 00000009 eecffb98 f11cbc58 eecffbb4 eecffbd0
>>>> [  221.490541] Call Trace:
>>>> [  221.490550]  [<c1711f4a>] dump_stack+0x41/0x52
>>>> [  221.490558]  [<c103edaf>] warn_slowpath_common+0x7f/0xa0
>>>> [  221.490563]  [<f11c1595>] ? brcmf_usb_dl_cmd+0x75/0x1a0 [brcmfmac]
>>>> [  221.490567]  [<f11c1595>] ? brcmf_usb_dl_cmd+0x75/0x1a0 [brcmfmac]
>>>> [  221.490570]  [<c103ee4e>] warn_slowpath_fmt+0x2e/0x30
>>>> [  221.490575]  [<f11c1595>] brcmf_usb_dl_cmd+0x75/0x1a0 [brcmfmac]
>>>> [  221.490580]  [<f11c2cd8>] brcmf_usb_probe+0x3c8/0x640 [brcmfmac]
>>>> [  221.490583]  [<c1717d53>] ? mutex_lock+0x13/0x32
>>>> [  221.490586]  [<c1493ae3>] usb_probe_interface+0xa3/0x180
>>>> [  221.490590]  [<c13f5690>] ? __driver_attach+0x90/0x90
>>>> [  221.490592]  [<c13f546e>] driver_probe_device+0x5e/0x1f0
>>>> [  221.490595]  [<c13f5690>] ? __driver_attach+0x90/0x90
>>>> [  221.490597]  [<c13f56c9>] __device_attach+0x39/0x50
>>>> [  221.490600]  [<c13f3d84>] bus_for_each_drv+0x34/0x70
>>>> [  221.490602]  [<c13f53db>] device_attach+0x7b/0x90
>>>> [  221.490604]  [<c13f5690>] ? __driver_attach+0x90/0x90
>>>> [  221.490607]  [<c13f4b8f>] bus_probe_device+0x6f/0x90
>>>> [  221.490609]  [<c13f3256>] device_add+0x426/0x520
>>>> [  221.490611]  [<c1491503>] ? usb_control_msg+0xb3/0xd0
>>>> [  221.490614]  [<c1717d53>] ? mutex_lock+0x13/0x32
>>>> [  221.490627]  [<c14922f8>] usb_set_configuration+0x3f8/0x700
>>>> [  221.490630]  [<c13f5690>] ? __driver_attach+0x90/0x90
>>>> [  221.490633]  [<c149ac7b>] generic_probe+0x2b/0x90
>>>> [  221.490637]  [<c1188bc0>] ? sysfs_create_link+0x20/0x40
>>>> [  221.490639]  [<c1492bec>] usb_probe_device+0xc/0x10
>>>> [  221.490641]  [<c13f546e>] driver_probe_device+0x5e/0x1f0
>>>> [  221.490644]  [<c13f5690>] ? __driver_attach+0x90/0x90
>>>> [  221.490646]  [<c13f56c9>] __device_attach+0x39/0x50
>>>> [  221.490649]  [<c13f3d84>] bus_for_each_drv+0x34/0x70
>>>> [  221.490651]  [<c13f53db>] device_attach+0x7b/0x90
>>>> [  221.490653]  [<c13f5690>] ? __driver_attach+0x90/0x90
>>>> [  221.490656]  [<c13f4b8f>] bus_probe_device+0x6f/0x90
>>>> [  221.490658]  [<c13f3256>] device_add+0x426/0x520
>>>> [  221.490661]  [<c148aa2e>] ? usb_new_device+0x16e/0x3a0
>>>> [  221.490663]  [<c148aad7>] usb_new_device+0x217/0x3a0
>>>> [  221.490666]  [<c148bff7>] hub_event+0xa17/0xda0
>>>> [  221.490668]  [<c1716918>] ? __schedule+0x2f8/0x710
>>>> [  221.490672]  [<c105127c>] ? pwq_dec_nr_in_flight+0x3c/0x90
>>>> [  221.490674]  [<c10513ee>] process_one_work+0x11e/0x360
>>>> [  221.490677]  [<c1051750>] worker_thread+0xf0/0x3c0
>>>> [  221.490680]  [<c106e14a>] ? __wake_up_locked+0x1a/0x20
>>>> [  221.490682]  [<c1051660>] ? process_scheduled_works+0x30/0x30
>>>> [  221.490685]  [<c1055b56>] kthread+0x96/0xb0
>>>> [  221.490687]  [<c1050000>] ? put_unbound_pool+0x110/0x170
>>>> [  221.490691]  [<c1719c81>] ret_from_kernel_thread+0x21/0x30
>>>> [  221.490693]  [<c1055ac0>] ? kthread_worker_fn+0x110/0x110
>>>> [  221.490695] ---[ end trace 9befd914693f3083 ]---
>>>> [  221.490697] brcmfmac: brcmf_usb_dlneeded chip 57005 rev 0xf11cfcec
>>>> [  221.490699] brcmfmac: brcmf_fw_get_firmwares enter: dev=1-1
>>>>
>>>> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>> index 5265aa7..15b1aa7 100644
>>>> --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>> +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>> @@ -709,8 +709,13 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
>>>>  	char *tmpbuf;
>>>>  	u16 size;
>>>>  
>>>> -	if ((!devinfo) || (devinfo->ctl_urb == NULL))
>>>> +	if (!devinfo || !devinfo->ctl_urb || !devinfo->ctl_completed) {
>>>> +		WARN_ONCE(1, "EINVAL devinfo=%p ctl_rub=%p completed=%d\n",
>>>> +			devinfo,
>>>> +			devinfo ? devinfo->ctl_urb : NULL,
>>>> +			devinfo ? devinfo->ctl_completed : -1);
>>>>  		return -EINVAL;
>>>> +	}
>>>>  
>>>>  	tmpbuf = kmalloc(buflen, GFP_ATOMIC);
>>>>  	if (!tmpbuf)
>>>>
>>>>
>>>>>
>>>>> Regards,
>>>>> Arend
>>>>> ---
>>>>>  drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 +-
>>>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>>> b/drivers/net/wireles
>>>>> index dc13591..786c40b 100644
>>>>> --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>>> +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>>> @@ -640,7 +640,7 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info
>>>>> *devinf
>>>>>         char *tmpbuf;
>>>>>         u16 size;
>>>>>
>>>>> -       if ((!devinfo) || (devinfo->ctl_urb == NULL))
>>>>> +       if (!devinfo || !devinfo->ctl_urb || !devinfo->ctl_completed)
>>>>>                 return -EINVAL;
>>>>>
>>>>>         tmpbuf = kmalloc(buflen, GFP_ATOMIC);
>>>>>
>>>>>> Signed-off-by: Mathy Vanhoef <vanhoefm@gmail.com>
>>>>>> ---
>>>>>> Currently brcmfmac may crash when a USB device is attached (tested with a LG
>>>>>> TWFM-B003D). In particular it fails on the second call to brcmf_usb_dl_cmd in
>>>>>> the while loop of brcmf_usb_resetcfg. The problem is that an URB is being
>>>>>> submitted twice:
>>>>>>
>>>>>> [  169.861800] brcmfmac: brcmf_usb_dl_writeimage Enter, fw f14db000, len 348160
>>>>>> [  171.787791] brcmfmac: brcmf_usb_dl_writeimage Exit, err=0
>>>>>> [  171.787797] brcmfmac: brcmf_usb_dlstart Exit, err=0
>>>>>> [  171.787799] brcmfmac: brcmf_usb_dlrun Enter
>>>>>> [  171.791794] brcmfmac: brcmf_usb_resetcfg Enter
>>>>>> [  173.988072] ------------[ cut here ]------------
>>>>>> [  173.988083] WARNING: CPU: 0 PID: 369 at drivers/usb/core/urb.c:339 usb_submit_urb+0x4e6/0x500()
>>>>>> [  173.988085] URB eaf45f00 submitted while active
>>>>>> [  173.988086] Modules linked in: brcmfmac brcmutil vmw_pvscsi pcnet32 mptspi mptscsih mptbase
>>>>>> [  173.988100] CPU: 0 PID: 369 Comm: kworker/0:2 Not tainted 3.18.0-rc3-wl #1
>>>>>> [  173.988102] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/31/2013
>>>>>> [  173.988106] Workqueue: events request_firmware_work_func
>>>>>> [  173.988108]  00000000 00000000 ee747db8 c1711f4a ee747df8 ee747de8 c103edaf c18d1e10
>>>>>> [  173.988112]  ee747e14 00000171 c18a8b29 00000153 c1490556 c1490556 eaf45f00 eafdc660
>>>>>> [  173.988115]  f14b8fa0 ee747e00 c103ee4e 00000009 ee747df8 c18d1e10 ee747e14 ee747e50
>>>>>> [  173.988119] Call Trace:
>>>>>> [  173.988129]  [<c1711f4a>] dump_stack+0x41/0x52
>>>>>> [  173.988136]  [<c103edaf>] warn_slowpath_common+0x7f/0xa0
>>>>>> [  173.988139]  [<c1490556>] ? usb_submit_urb+0x4e6/0x500
>>>>>> [  173.988141]  [<c1490556>] ? usb_submit_urb+0x4e6/0x500
>>>>>> [  173.988147]  [<f14b8fa0>] ? brcmf_usb_ioctl_resp_wake+0x40/0x40 [brcmfmac]
>>>>>> [  173.988150]  [<c103ee4e>] warn_slowpath_fmt+0x2e/0x30
>>>>>> [  173.988152]  [<c1490556>] usb_submit_urb+0x4e6/0x500
>>>>>> [  173.988156]  [<c1123de1>] ? __kmalloc+0x21/0x140
>>>>>> [  173.988161]  [<f14b91c3>] ? brcmf_usb_dl_cmd+0x33/0x120 [brcmfmac]
>>>>>> [  173.988166]  [<f14b9243>] brcmf_usb_dl_cmd+0xb3/0x120 [brcmfmac]
>>>>>> [  173.988170]  [<f14ba6c4>] brcmf_usb_probe_phase2+0x4e4/0x640 [brcmfmac]
>>>>>> [  173.988176]  [<f14b4900>] brcmf_fw_request_code_done+0xd0/0xf0 [brcmfmac]
>>>>>> [  173.988178]  [<c1400876>] request_firmware_work_func+0x26/0x50
>>>>>> [  173.988182]  [<c10513ee>] process_one_work+0x11e/0x360
>>>>>> [  173.988184]  [<c1051750>] worker_thread+0xf0/0x3c0
>>>>>> [  173.988205]  [<c106e14a>] ? __wake_up_locked+0x1a/0x20
>>>>>> [  173.988208]  [<c1051660>] ? process_scheduled_works+0x30/0x30
>>>>>> [  173.988211]  [<c1055b56>] kthread+0x96/0xb0
>>>>>> [  173.988214]  [<c1719c81>] ret_from_kernel_thread+0x21/0x30
>>>>>> [  173.988217]  [<c1055ac0>] ? kthread_worker_fn+0x110/0x110
>>>>>> [  173.988219] ---[ end trace 0c88bf46801de083 ]---
>>>>>> [  173.988221] brcmf_usb_dl_cmd: usb_submit_urb failed -16
>>>>>> [  173.988396] brcmfmac: brcmf_usb_probe_phase2 failed: dev=1-1, err=-19
>>>>>> [  173.989503] brcmfmac: brcmf_usb_disconnect Enter
>>>>>>
>>>>>> This patch fixes the brcmf_usb_dl_cmd function to prevent an URB from being
>>>>>> submitted twice. Tested using a LG TWFM-B003D, which now works properly.
>>>>>>
>>>>>>
>>>>>>  drivers/net/wireless/brcm80211/brcmfmac/usb.c |    6 ++++--
>>>>>>  1 file changed, 4 insertions(+), 2 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>>>> index 5265aa7..1bc7858 100644
>>>>>> --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>>>> +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>>>>>> @@ -738,10 +738,12 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
>>>>>>  		goto finalize;
>>>>>>  	}
>>>>>>  
>>>>>> -	if (!brcmf_usb_ioctl_resp_wait(devinfo))
>>>>>> +	if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
>>>>>> +		usb_unlink_urb(devinfo->ctl_urb);
>>>>>>  		ret = -ETIMEDOUT;
>>>>>> -	else
>>>>>> +	} else {
>>>>>>  		memcpy(buffer, tmpbuf, buflen);
>>>>>> +	}
>>>>>>  
>>>>>>  finalize:
>>>>>>  	kfree(tmpbuf);
>>>>>>
>>>>>
>>>
> 

^ permalink raw reply

* Re: [PATCH] brcmfmac: unlink URB when request timed out
From: Mathy Vanhoef @ 2014-11-12  1:02 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: brudley, Arend van Spriel, Franky Lin, meuleman, John Linville,
	pieterpg, linux-wireless, brcm80211-dev-list, netdev,
	linux-kernel
In-Reply-To: <1415610506.16488.20.camel@linux-0dmf.site>

On 11/10/2014 04:08 AM, Oliver Neukum wrote:
> On Sun, 2014-11-09 at 13:10 -0500, Mathy Vanhoef wrote:
>> From: Mathy Vanhoef <vanhoefm@gmail.com>
>>
>> Unlink the submitted URB in brcmf_usb_dl_cmd if the request timed out. This
>> assures the URB is never submitted twice, preventing a driver crash.
>
> Hi,
>
> I am afrad this patch is no good. The diagnosis is good,
> but the fix introduces serious problems.
>
>> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>> index 5265aa7..1bc7858 100644
>> --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>> +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
>> @@ -738,10 +738,12 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
>>   goto finalize;
>>   }
>>  
>> - if (!brcmf_usb_ioctl_resp_wait(devinfo))
>> + if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
>> + usb_unlink_urb(devinfo->ctl_urb);
>
> This is the asynchronous unlink. You have no guarantee it is finished
> after this point.
>
>>   ret = -ETIMEDOUT;
>> - else
>> + } else {
>>   memcpy(buffer, tmpbuf, buflen);
>> + }
>>  
>>  finalize:
>>   kfree(tmpbuf);
>
> Which means that you are freeing memory that may still be used by DMA
> at this time.
> In addition you have no guarantee that the unlink is indeed finished
> by the time the URB is reused.
> If you wish to take this approach you better forget about this URB
> and allocate a new one and free the buffer from the callback.

Hi Oliver,

Good catch. I think the DMA issue is also present in the current driver: it
frees the buffer without unlinking/killing the URB at all. Can a malicious USB
device force a timeout to occur (i.e. delay the call to the completion
handler)? If so this might be a use-after-free vulnerability.

It seems using usb_kill_urb instead of usb_unlink_urb in the patch prevents any
possible use-after-free. Can someone double check?

Kind regards,
Mathy

>
> Regards
> Oliver
>

^ permalink raw reply

* Re: [PATCH v2 07/19] selftests/firmware: add install target to enable installing test
From: Shuah Khan @ 2014-11-12  1:06 UTC (permalink / raw)
  To: Kees Cook
  Cc: Greg KH, Andrew Morton, Michal Marek, David S. Miller, Phong Tran,
	David Herrmann, Hugh Dickins, pranith kumar, Eric W. Biederman,
	Serge E. Hallyn, linux-kbuild, LKML, Linux API,
	Network Development
In-Reply-To: <CAGXu5j+yLqH0xj=5N90LRmK9F1xPLVWBy+cyUCzjvKJH7gE7AA@mail.gmail.com>

On 11/11/2014 02:29 PM, Kees Cook wrote:
> Hi,
> 
> Sorry, I still really don't like this approach. While it is all in one
> place (thank you for that), I think it isn't a form that is very
> workable for the people maintaining the self tests. How about this,
> instead of per-Makefile customization, why not define an execution
> framework for these tests instead.

If I understand correctly, sounds like you don't like the way
install target is implemented in the individual test Makefiles
and the changes I made to run_tests targets to address the code
duplication concern.

At the moment there is no duplicate code in this patch series
between install and run_tests targets. This is a  first step
towards standardizing the framework and a definite improvement
over what we have currently. As I mentioned earlier, my goal
is to make it easier for developers to install and run the
existing tests and evolve the framework as we go.

Assuming my understanding is correct that:

-- install and run_tests targets in individual tests can be
   refined and automated with a common Makefile approach you
   proposed.
-- the rest of the user-interface kselftest_install and kselftest
   are good.

I would like to propose that we get started with the current
implementation and refine it based on the following ideas you
suggested. The refinements you are recommending are confined
to selftests and can be made after the kselftest_install
gets added. Adding kselftest_install makes it easier to make
the refinements as it defines overall UI.

> 
> For example, how about every test directory must have a Makefile with
> the needed binary targets. A common makefile could be included that
> defines the "run_tests" target that calls the script "run_tests.sh"
> that is a shell script in the current directory. (For inspiration, see
> how kernel modules can be built out of tree.)
> 
> The "run_tests.sh" scripts could all include a common shell script,
> say "../common.sh" that provides any common variables, functions, etc
> (e.g. things like "Start $name test ..." should be in common.sh
> instead of repeated in every script, the installation logic can be in
> once place instead of repeated).
> 
> Then along side common.sh could be "run_installed_tests.sh" or
> something, used on the installed target, that would traverse each
> directory, etc. From this, we can have a much more data-driven
> framework, and a common approach to running tests.
> 
> As such, we should declare up front how tests should behave on
> failure. And the top-level test runner can do things like count the
> number of tests, failures, etc.
> 
> Then, instead of splitting up the patches by test directory, you can
> split them up by logical changes (e.g. defining the common "run_tests"
> target, and then removing the target from all the tests by including
> the common makefile stub that defines it).
> 

These are good ideas and I am with on evolving the framework to make
it easier to maintain individual tests. Patches are welcome.

thanks,
-- Shuah


-- 
Shuah Khan
Sr. Linux Kernel Developer
Samsung Research America (Silicon Valley)
shuahkh@osg.samsung.com | (970) 217-8978

^ permalink raw reply

* RE: [PATCH net-next 2/2] r8152: adjust rtl_start_rx
From: Hayes Wang @ 2014-11-12  1:45 UTC (permalink / raw)
  To: David Miller
  Cc: netdev@vger.kernel.org, nic_swsd, linux-kernel@vger.kernel.org,
	linux-usb@vger.kernel.org
In-Reply-To: <20141107.113522.837502028522211960.davem@davemloft.net>

David Miller [mailto:davem@davemloft.net] 
> Sent: Saturday, November 08, 2014 12:35 AM
[...]
> Does this even work?
> 
> If you leave a hole in the ring, the device is going to stop there
> anyways.
> 
> So better to replenish the next time you call into this function
> rather than leaving gaps in your receive ring.

Excuse me. Is this still unacceptable?
Should I remove this patch for keeping the original flow?
 
Best Regards,
Hayes

^ permalink raw reply

* [PATCH net-next v3 0/3] Code adjustment
From: Hayes Wang @ 2014-11-12  2:05 UTC (permalink / raw)
  To: netdev; +Cc: nic_swsd, linux-kernel, linux-usb, Hayes Wang
In-Reply-To: <1394712342-15778-79-Taiwan-albertk@realtek.com>

v3:
 Remove the test_bit for patch #2.

v2:
 Correct the spelling error for the comment of patch #3.

v1:
Adjust some codes to make them more reasonable.

Hayes Wang (3):
  r8152: remove the duplicate init for the list of rx_done
  r8152: clear the flag of SCHEDULE_TASKLET in tasklet
  r8152: check RTL8152_UNPLUG and netif_running before autoresume

 drivers/net/usb/r8152.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

-- 
1.9.3

^ 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