* [PATCH] carl9170: Convert byte_rev_table uses to bitrev8
From: Wang, Yalin @ 2014-11-14 5:16 UTC (permalink / raw)
To: 'chunkeey@googlemail.com',
'linville@tuxdriver.com',
'linux-wireless@vger.kernel.org',
'netdev@vger.kernel.org',
'linux-kernel@vger.kernel.org', 'joe@perches.com'
Use the inline function instead of directly indexing the array.
This allows some architectures with hardware instructions for bit
reversals to eliminate the array.
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: Yalin Wang <yalin.wang@sonymobile.com>
---
drivers/net/wireless/ath/carl9170/phy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index b80b213..dca6df1 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -994,7 +994,7 @@ static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
refsel0 = 0;
refsel1 = 1;
}
- chansel = byte_rev_table[chansel];
+ chansel = bitrev8(chansel);
} else {
if (freq == 2484) {
chansel = 10 + (freq - 2274) / 5;
@@ -1002,7 +1002,7 @@ static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
} else
chansel = 16 + (freq - 2272) / 5;
chansel *= 4;
- chansel = byte_rev_table[chansel];
+ chansel = bitrev8(chansel);
}
d1 = chansel;
--
2.1.1
^ permalink raw reply related
* Re: [PATCH] carl9170: Convert byte_rev_table uses to bitrev8
From: Joe Perches @ 2014-11-14 5:32 UTC (permalink / raw)
To: Wang, Yalin
Cc: 'chunkeey@googlemail.com',
'linville@tuxdriver.com',
'linux-wireless@vger.kernel.org',
'netdev@vger.kernel.org',
'linux-kernel@vger.kernel.org'
In-Reply-To: <35FD53F367049845BC99AC72306C23D103E010D1829D@CNBJMBX05.corpusers.net>
On Fri, 2014-11-14 at 13:16 +0800, Wang, Yalin wrote:
> Use the inline function instead of directly indexing the array.
>
> This allows some architectures with hardware instructions for bit
> reversals to eliminate the array.
This one is already in -next
commit 7a1283d8f5298437a454ec477384dcd9f9f88bac
Author: Joe Perches <joe@perches.com>
Date: Tue Oct 28 14:18:58 2014 -0700
carl9170: Convert byte_rev_table uses to bitrev8
Use the inline function instead of directly indexing the array.
This allows some architectures with hardware instructions
for bit reversals to eliminate the array.
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
^ permalink raw reply
* RE: [PATCH] carl9170: Convert byte_rev_table uses to bitrev8
From: Wang, Yalin @ 2014-11-14 5:37 UTC (permalink / raw)
To: 'Joe Perches'
Cc: 'chunkeey@googlemail.com',
'linville@tuxdriver.com',
'linux-wireless@vger.kernel.org',
'netdev@vger.kernel.org',
'linux-kernel@vger.kernel.org'
In-Reply-To: <1415943164.5912.4.camel@perches.com>
> From: Joe Perches [mailto:joe@perches.com]
> Sent: Friday, November 14, 2014 1:33 PM
> To: Wang, Yalin
> Cc: 'chunkeey@googlemail.com'; 'linville@tuxdriver.com'; 'linux-
> wireless@vger.kernel.org'; 'netdev@vger.kernel.org'; 'linux-
> kernel@vger.kernel.org'
> Subject: Re: [PATCH] carl9170: Convert byte_rev_table uses to bitrev8
>
> On Fri, 2014-11-14 at 13:16 +0800, Wang, Yalin wrote:
> > Use the inline function instead of directly indexing the array.
> >
> > This allows some architectures with hardware instructions for bit
> > reversals to eliminate the array.
>
> This one is already in -next
>
> commit 7a1283d8f5298437a454ec477384dcd9f9f88bac
> Author: Joe Perches <joe@perches.com>
> Date: Tue Oct 28 14:18:58 2014 -0700
>
> carl9170: Convert byte_rev_table uses to bitrev8
>
> Use the inline function instead of directly indexing the array.
>
> This allows some architectures with hardware instructions
> for bit reversals to eliminate the array.
>
> Signed-off-by: Joe Perches <joe@perches.com>
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
>
Got it ,
So I need wait for your another patch to be accepted.
Thanks!
^ permalink raw reply
* Re: [PATCH net-next 1/1] ipvlan: Initial check-in of the IPVLAN driver.
From: Mahesh Bandewar @ 2014-11-14 5:47 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: netdev, Eric Dumazet, Maciej Zenczykowski, Laurent Chavey,
Tim Hockin, David Miller, Brandon Philips, Pavel Emelianov
In-Reply-To: <CAADnVQJn4+0pCOeSu3oT0fvaJ=CtvD3wmczm4Jn6KjWgZoFrLQ@mail.gmail.com>
On Thu, Nov 13, 2014 at 3:25 PM, Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Tue, Nov 11, 2014 at 2:29 PM, Mahesh Bandewar <maheshb@google.com> wrote:
>> 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.
>
> great stuff. would be interesting to see a 'typical use'
> scenario of l2 vs l3 mode. Why users would pick one
> or another?
> I can only think of different default ip in different ns
> would force l2. Anything else?
>
The primary difference is the ability to TX/RX multicast/broadcast as
well as control of routing in L2 mode while in L3 mode that belongs to
the default-ns and that means it can not be controlled from the client
namespace. L3 mode would be more restrictive of the two modes because
of that. Your use case would mostly define the mode to choose.
> Few comments:
>
>> +++ b/drivers/net/ipvlan/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>
>
> I don't think it's a good style to put all headers that all
> .c need into common .h
> Rather put them into individual .c
>
I don't know why it's wrong (also this a driver-private include and
not expecting anyone else to include) but I can definitely see few
advantages in this - (a) by including in the header file it's
available to all the .c files and does not have to specified
separately. (b) This means probably I can avoid some duplication
meaning less lines of include (c) even if I include some extra
definitions, there is no runtime cost that has to be paid since this
is sorted out during compile.
>> +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;
>> + }
> ...
>
>> +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);
>
> imo it looks very artificial to split logically single
> lookup function into two: get() that returns 'type'/
> 'void * lyr3h' and lookup() that uses them.
> It feels error prone.
> Also everywhere lookup() follows get() immediately.
> I think single lookup() would be much cleaner.
I feel it's clean in the current form. One function is looking into
the packet / frame while the other one is dealing with the hash-table
and making one do both could be error prone. I guess it's the
perspective and probably no one is wrong!
^ permalink raw reply
* [PATCH net-next] openvswitch: Fix build failure.
From: Pravin B Shelar @ 2014-11-14 6:21 UTC (permalink / raw)
To: davem; +Cc: netdev, Pravin B Shelar
Add dependency on INET to fix following build error. I have also
fixed MPLS dependency.
ERROR: "ip_route_output_flow" [net/openvswitch/openvswitch.ko]
undefined!
make[1]: *** [__modpost] Error 1
Reported-by: Jim Davis <jim.epost@gmail.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
---
net/openvswitch/Kconfig | 6 ++----
1 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig
index 454ce12..b7d818c 100644
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@ -4,7 +4,9 @@
config OPENVSWITCH
tristate "Open vSwitch"
+ depends on INET
select LIBCRC32C
+ select NET_MPLS_GSO
---help---
Open vSwitch is a multilayer Ethernet switch targeted at virtualized
environments. In addition to supporting a variety of features
@@ -30,8 +32,6 @@ config OPENVSWITCH
config OPENVSWITCH_GRE
tristate "Open vSwitch GRE tunneling support"
- select NET_MPLS_GSO
- depends on INET
depends on OPENVSWITCH
depends on NET_IPGRE_DEMUX
default OPENVSWITCH
@@ -45,7 +45,6 @@ config OPENVSWITCH_GRE
config OPENVSWITCH_VXLAN
tristate "Open vSwitch VXLAN tunneling support"
- depends on INET
depends on OPENVSWITCH
depends on VXLAN
default OPENVSWITCH
@@ -58,7 +57,6 @@ config OPENVSWITCH_VXLAN
config OPENVSWITCH_GENEVE
tristate "Open vSwitch Geneve tunneling support"
- depends on INET
depends on OPENVSWITCH
depends on GENEVE
default OPENVSWITCH
--
1.7.1
^ permalink raw reply related
* Re: [PATCH net-next] openvswitch: Fix build failure.
From: David Miller @ 2014-11-14 6:24 UTC (permalink / raw)
To: pshelar; +Cc: netdev
In-Reply-To: <1415946090-1504-1-git-send-email-pshelar@nicira.com>
From: Pravin B Shelar <pshelar@nicira.com>
Date: Thu, 13 Nov 2014 22:21:30 -0800
> Add dependency on INET to fix following build error. I have also
> fixed MPLS dependency.
>
> ERROR: "ip_route_output_flow" [net/openvswitch/openvswitch.ko]
> undefined!
> make[1]: *** [__modpost] Error 1
>
> Reported-by: Jim Davis <jim.epost@gmail.com>
> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Applied, thanks Pravin.
^ permalink raw reply
* [PATCH net-next v1] ipvlan: Initial check-in of the IPVLAN driver.
From: Mahesh Bandewar @ 2014-11-14 6: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 | 114 +++++
drivers/net/Kconfig | 18 +
drivers/net/Makefile | 1 +
drivers/net/ipvlan/Makefile | 7 +
drivers/net/ipvlan/ipvlan.h | 157 +++++++
drivers/net/ipvlan/ipvlan_core.c | 629 +++++++++++++++++++++++++++
drivers/net/ipvlan/ipvlan_main.c | 827 ++++++++++++++++++++++++++++++++++++
drivers/net/ipvlan/ipvlan_sysfs.c | 119 ++++++
include/linux/netdevice.h | 4 +
include/uapi/linux/if_link.h | 15 +
10 files changed, 1891 insertions(+)
create mode 100644 Documentation/networking/ipvlan.txt
create mode 100644 drivers/net/ipvlan/Makefile
create mode 100644 drivers/net/ipvlan/ipvlan.h
create mode 100644 drivers/net/ipvlan/ipvlan_core.c
create mode 100644 drivers/net/ipvlan/ipvlan_main.c
create mode 100644 drivers/net/ipvlan/ipvlan_sysfs.c
diff --git a/Documentation/networking/ipvlan.txt b/Documentation/networking/ipvlan.txt
new file mode 100644
index 000000000000..1f5d34cb4841
--- /dev/null
+++ b/Documentation/networking/ipvlan.txt
@@ -0,0 +1,114 @@
+
+ IPVLAN Driver HOWTO
+
+Initial Release:
+ Mahesh Bandewar <maheshb AT google.com>
+
+1. Introduction:
+ This is conceptually very similar to the macvlan driver with one major
+exception of using L3 for mux-ing /demux-ing among slaves. This property makes
+the master device share the L2 with it's slave devices. I have developed this
+driver in conjuntion with network namespaces and not sure if there is use case
+outside of it.
+
+
+2. Building and Installation:
+ In order to build the driver, please select the config item CONFIG_IPVLAN.
+The driver can be built into the kernel (CONFIG_IPVLAN=y) or as a module
+(CONFIG_IPVLAN=m).
+
+
+3. Configuration:
+ There are no module parameters for this driver and it can be configured
+using IProute2/ip utility.
+
+ ip link add link <master-dev> <slave-dev> type ipvlan mode { l2 | L3 }
+
+ e.g. ip link add link ipvl0 eth0 type ipvlan mode l2
+
+
+4. Operating modes:
+ IPvlan has two modes of operation - L2 and L3. For a given master device,
+you can select one of these two modes and all slaves on that master will
+operate in the same (selected) mode. The RX mode is almost identical except
+that in L3 mode the slaves wont receive any multicast / broadcast traffic.
+L3 mode is more restrictive since routing is controlled from the other (mostly)
+default namespace.
+
+4.1 L2 mode:
+ In this mode TX processing happens on the stack instance attached to the
+slave device and packets are switched and queued to the master device to send
+out. In this mode the slaves will RX/TX multicast and broadcast (if applicable)
+as well.
+
+4.2 L3 mode:
+ In this mode TX processing upto L3 happens on the stack instance attached
+to the slave device and packets are switched to the stack instance of the
+master device for the L2 processing and routing from that instance will be
+used before packets are queued on the outbound device. In this mode the slaves
+will not receive nor can send multicast / broadcast traffic.
+
+
+5. Sysfs interface:
+ Currently the mode of operation is available at -
+ /sys/class/net/<master>/ipvlan/mode
+The value can be 0 or 1; where 0 :=> L2, 1 := L3 mode
+
+
+6. What to choose (macvlan vs. ipvlan)?
+ These two devices are very similar in many regards and the specific use
+case could very well define which device to choose. if one of the following
+situations defines your use case then you can choose to use ipvlan -
+ (a) The Linux host that is connected to the external switch / router has
+policy configured that allows only one mac per port.
+ (b) No of virtual devices created on a master exceed the mac capacity and
+puts the NIC in promiscous mode and degraded performance is a concern.
+ (c) If the slave device is to be put into the hostile / untrusted network
+namespace where L2 on the slave could be changed / misused.
+
+
+7. Example configuration:
+
+ +=============================================================+
+ | Host: host1 |
+ | |
+ | +----------------------+ +----------------------+ |
+ | | NS:ns0 | | NS:ns1 | |
+ | | | | | |
+ | | | | | |
+ | | ipvl0 | | ipvl1 | |
+ | +----------#-----------+ +-----------#----------+ |
+ | # # |
+ | ################################ |
+ | # eth0 |
+ +==============================#==============================+
+
+
+ (a) Create two network namespaces - ns0, ns1
+ ip netns add ns0
+ ip netns add ns1
+
+ (b) Create two ipvlan slaves on eth0 (master device)
+ ip link add link eth0 ipvl0 type ipvlan mode l2
+ ip link add link eth0 ipvl1 type ipvlan mode l2
+
+ (c) Assign slaves to the respective network namespaces
+ ip link set dev ipvl0 netns ns0
+ ip link set dev ipvl1 netns ns1
+
+ (d) Now switch to the namespace (ns0 or ns1) to configure the slave devices
+ - For ns0
+ (1) ip netns exec ns0 bash
+ (2) ip link set dev ipvl0 up
+ (3) ip link set dev lo up
+ (4) ip -4 addr add 127.0.0.1 dev lo
+ (5) ip -4 addr add $IPADDR dev ipvl0
+ (6) ip -4 route add default via $ROUTER dev ipvl0
+ - For ns1
+ (1) ip netns exec ns1 bash
+ (2) ip link set dev ipvl1 up
+ (3) ip link set dev lo up
+ (4) ip -4 addr add 127.0.0.1 dev lo
+ (5) ip -4 addr add $IPADDR dev ipvl1
+ (6) ip -4 route add default via $ROUTER dev ipvl1
+
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index f9009be3f307..b6d64f546574 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -145,6 +145,24 @@ config MACVTAP
To compile this driver as a module, choose M here: the module
will be called macvtap.
+
+config IPVLAN
+ tristate "IP-VLAN support"
+ ---help---
+ This allows one to create virtual devices off of a main interface
+ and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
+ on packets. All interfaces (including the main interface) share L2
+ making it transparent to the connected L2 switch.
+
+ Ipvlan devices can be added using the "ip" command from the
+ iproute2 package starting with the iproute2-X.Y.ZZ release:
+
+ "ip link add link <main-dev> [ NAME ] type ipvlan"
+
+ To compile this driver as a module, choose M here: the module
+ will be called ipvlan.
+
+
config VXLAN
tristate "Virtual eXtensible Local Area Network (VXLAN)"
depends on INET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 61aefdd1e173..e25fdd7d905e 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -6,6 +6,7 @@
# Networking Core Drivers
#
obj-$(CONFIG_BONDING) += bonding/
+obj-$(CONFIG_IPVLAN) += ipvlan/
obj-$(CONFIG_DUMMY) += dummy.o
obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
diff --git a/drivers/net/ipvlan/Makefile b/drivers/net/ipvlan/Makefile
new file mode 100644
index 000000000000..2efff4e9bb40
--- /dev/null
+++ b/drivers/net/ipvlan/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Ethernet Ipvlan driver
+#
+
+obj-$(CONFIG_IPVLAN) += ipvlan.o
+
+ipvlan-objs := ipvlan_core.o ipvlan_main.o ipvlan_sysfs.o
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
new file mode 100644
index 000000000000..78bb1ee9bc68
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+#ifndef __IPVLAN_H
+#define __IPVLAN_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rculist.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_link.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <net/rtnetlink.h>
+#include <net/gre.h>
+#include <net/route.h>
+#include <net/addrconf.h>
+
+#define IPVLAN_DRV "ipvlan"
+#define IPV_DRV_VER "0.1"
+
+#define IPVLAN_HASH_SIZE (1 << BITS_PER_BYTE)
+#define IPVLAN_HASH_MASK (IPVLAN_HASH_SIZE - 1)
+
+#define IPVLAN_MAC_FILTER_BITS 8
+#define IPVLAN_MAC_FILTER_SIZE (1 << IPVLAN_MAC_FILTER_BITS)
+#define IPVLAN_MAC_FILTER_MASK (IPVLAN_MAC_FILTER_SIZE - 1)
+
+/* Define IPVL_DEBUG and set the appropriate dbg_level for debugging. */
+#ifdef IPVL_DEBUG
+/*
+ * 1 : non-datapath debugging
+ * 2 : Custom
+ * 3 : function enters and exists.
+ * 4 : printk in data path (be careful!)
+ */
+#define IPVL_DBG_LEVEL 1
+#define ipvlan_dbg(level, msg...) do { \
+ if (level <= IPVL_DBG_LEVEL) \
+ printk(KERN_DEBUG msg); \
+ } while (0)
+#else
+#define ipvlan_dbg(level, msg...) do { ; } while (0)
+#endif
+
+typedef enum {
+ IPVL_IPV6 = 0,
+ IPVL_ICMPV6,
+ IPVL_IPV4,
+ IPVL_ARP,
+} ipvl_hdr_type;
+
+struct ipvl_pcpu_stats {
+ u64 rx_pkts;
+ u64 rx_bytes;
+ u64 rx_mcast;
+ u64 tx_pkts;
+ u64 tx_bytes;
+ struct u64_stats_sync syncp;
+ u32 rx_errs;
+ u32 tx_drps;
+};
+
+/* Forward declaration */
+struct ipvl_port;
+
+struct ipvl_dev {
+ struct net_device *dev;
+ struct list_head pnode;
+ struct ipvl_port *port;
+ struct net_device *phy_dev;
+ struct list_head addrs;
+ int ipv4cnt;
+ int ipv6cnt;
+ struct ipvl_pcpu_stats *pcpu_stats;
+ DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
+ netdev_features_t sfeatures;
+ u16 mtu_adj;
+};
+
+struct ipvl_addr {
+ struct ipvl_dev *master; /* Back pointer to master */
+ union {
+ struct in6_addr ip6; /* IPv6 address on logical interface */
+ struct in_addr ip4; /* IPv4 address on logical interface */
+ } ipu;
+#define ip6addr ipu.ip6
+#define ip4addr ipu.ip4
+ struct hlist_node hlnode; /* Hash-table linkage */
+ struct list_head anode; /* logical-interface linkage */
+ struct rcu_head rcu;
+ ipvl_hdr_type atype;
+};
+
+struct ipvl_port {
+ struct net_device *dev;
+ struct hlist_head hlhead[IPVLAN_HASH_SIZE];
+ struct list_head ipvlans;
+ struct rcu_head rcu;
+ int count;
+ struct kobject kobj;
+ u16 mode;
+};
+
+static inline struct ipvl_port *ipvlan_port_get_rcu(const struct net_device *d)
+{
+ return rcu_dereference(d->rx_handler_data);
+}
+
+static inline struct ipvl_port *ipvlan_port_get_rtnl(const struct net_device *d)
+{
+ return rtnl_dereference(d->rx_handler_data);
+}
+
+static inline bool ipvlan_dev_master(struct net_device *d)
+{
+ return d->priv_flags & IFF_IPVLAN_MASTER;
+}
+
+static inline bool ipvlan_dev_slave(struct net_device *d)
+{
+ return d->priv_flags & IFF_IPVLAN_SLAVE;
+}
+
+/* ---- Prototype declarations ---- */
+/* ---- ipvlan_main.c ---- */
+void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev);
+void ipvlan_set_port_mode(struct ipvl_port *port, u32 nval);
+
+/* ---- ipvlan_sysfs.c ---- */
+int ipvlan_add_per_master_sysfs_mode(struct ipvl_port *port,
+ struct net_device *dev);
+void ipvlan_del_per_master_sysfs_mode(struct ipvl_port *port);
+
+/* ---- ipvlan_core.c ---- */
+void ipvlan_init_secret(void);
+unsigned int ipvlan_mac_hash(const unsigned char *addr);
+rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb);
+int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
+void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
+bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6);
+struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
+ const void *iaddr, bool is_v6);
+void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync);
+#endif /* __IPVLAN_H */
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
new file mode 100644
index 000000000000..24d5ccc96eae
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -0,0 +1,629 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+
+#include "ipvlan.h"
+
+static u32 ipvlan_jhash_secret;
+
+void ipvlan_init_secret(void)
+{
+ net_get_random_once(&ipvlan_jhash_secret, sizeof(ipvlan_jhash_secret));
+}
+
+static void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
+ unsigned int len, bool success, bool mcast)
+{
+ if (!ipvlan)
+ return;
+
+ if (likely(success)) {
+ struct ipvl_pcpu_stats *pcptr;
+
+ pcptr = this_cpu_ptr(ipvlan->pcpu_stats);
+ u64_stats_update_begin(&pcptr->syncp);
+ pcptr->rx_pkts++;
+ pcptr->rx_bytes += len;
+ if (mcast)
+ pcptr->rx_mcast++;
+ u64_stats_update_end(&pcptr->syncp);
+ } else {
+ this_cpu_inc(ipvlan->pcpu_stats->rx_errs);
+ }
+}
+
+static u8 ipvlan_get_v6_hash(const void *iaddr)
+{
+ const struct in6_addr *ip6_addr = iaddr;
+
+ return __ipv6_addr_jhash(ip6_addr, ipvlan_jhash_secret)
+ & IPVLAN_HASH_MASK;
+}
+
+static u8 ipvlan_get_v4_hash(const void *iaddr)
+{
+ const struct in_addr *ip4_addr = iaddr;
+ return jhash_1word(ip4_addr->s_addr, ipvlan_jhash_secret)
+ & IPVLAN_HASH_MASK;
+}
+
+struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
+ const void *iaddr, bool is_v6)
+{
+ struct ipvl_addr *addr;
+ u8 hash = is_v6 ? ipvlan_get_v6_hash(iaddr) :
+ ipvlan_get_v4_hash(iaddr);
+
+ hlist_for_each_entry_rcu(addr, &port->hlhead[hash], hlnode) {
+ if (is_v6 && addr->atype == IPVL_IPV6 &&
+ ipv6_addr_equal(&addr->ip6addr, iaddr))
+ return addr;
+ else if (!is_v6 && addr->atype == IPVL_IPV4 &&
+ addr->ip4addr.s_addr ==
+ ((struct in_addr *)iaddr)->s_addr)
+ return addr;
+ }
+ return NULL;
+}
+
+void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr)
+{
+ struct ipvl_port *port = ipvlan->port;
+ u8 hash = (addr->atype == IPVL_IPV6) ?
+ ipvlan_get_v6_hash(&addr->ip6addr) :
+ ipvlan_get_v4_hash(&addr->ip4addr);
+
+ hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
+}
+
+void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync)
+{
+ hlist_del_rcu(&addr->hlnode);
+ if (sync)
+ synchronize_rcu();
+}
+
+bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
+{
+ struct ipvl_port *port = ipvlan->port;
+ struct ipvl_addr *addr;
+
+ list_for_each_entry(addr, &ipvlan->addrs, anode) {
+ if ((is_v6 && addr->atype == IPVL_IPV6 &&
+ ipv6_addr_equal(&addr->ip6addr, iaddr))
+ || (!is_v6 && addr->atype == IPVL_IPV4 &&
+ addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr)
+ )
+ return true;
+ }
+
+ if (ipvlan_ht_addr_lookup(port, iaddr, is_v6))
+ return true;
+
+ return false;
+}
+
+static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type)
+{
+ void *lyr3h = NULL;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_ARP): {
+ struct arphdr *arph;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*arph))))
+ return NULL;
+
+ arph = arp_hdr(skb);
+ *type = IPVL_ARP;
+ lyr3h = arph;
+ break;
+ }
+
+ case htons(ETH_P_IP): {
+ u32 pktlen;
+ struct iphdr *ip4h;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ip4h))))
+ return NULL;
+
+ ip4h = ip_hdr(skb);
+ pktlen = ntohs(ip4h->tot_len);
+ if (ip4h->ihl < 5 || ip4h->version != 4)
+ return NULL;
+ if (skb->len < pktlen || pktlen < (ip4h->ihl * 4))
+ return NULL;
+
+ *type = IPVL_IPV4;
+ lyr3h = ip4h;
+ break;
+ }
+ case htons(ETH_P_IPV6): {
+ struct ipv6hdr *ip6h;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h))))
+ return NULL;
+
+ ip6h = ipv6_hdr(skb);
+ if (ip6h->version != 6)
+ return NULL;
+
+ *type = IPVL_IPV6;
+ lyr3h = ip6h;
+ /* Only Neighbour Solicitation pkts need different treatment */
+ if (ipv6_addr_any(&ip6h->saddr) &&
+ ip6h->nexthdr == NEXTHDR_ICMP) {
+ /* Get to the ICMPv6 header */
+ *type = IPVL_ICMPV6;
+ lyr3h = ip6h + 1;
+ }
+ break;
+ }
+ default:
+ return NULL;
+ }
+
+ return lyr3h;
+}
+
+unsigned int ipvlan_mac_hash(const unsigned char *addr)
+{
+ u32 hash = jhash_1word(__get_unaligned_cpu32(addr+2),
+ ipvlan_jhash_secret);
+ return hash & IPVLAN_MAC_FILTER_MASK;
+}
+
+static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb,
+ const struct ipvl_dev *in_dev, bool local)
+{
+ struct ethhdr *eth = eth_hdr(skb);
+ struct ipvl_dev *ipvlan = 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))
+ 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;
+
+ addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
+ if (addr) {
+ ipvlan_dbg(4, "%s[%d]L2:Ucast Recv:[%s], PROT=[%x]\n",
+ __func__, __LINE__, addr->master->dev->name,
+ ntohs(skb->protocol));
+ ret = ipvlan_rcv_frame(addr, skb, false);
+ }
+ }
+
+ return ret;
+}
+
+rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+ struct ipvl_port *port = ipvlan_port_get_rcu(skb->dev);
+
+ if (!port)
+ goto out;
+
+ switch (port->mode) {
+ case IPVLAN_MODE_L2:
+ return ipvlan_handle_mode_l2(pskb, port);
+ case IPVLAN_MODE_L3:
+ return ipvlan_handle_mode_l3(pskb, port);
+ }
+
+ /* Should not reach here */
+ BUG();
+out:
+ return RX_HANDLER_PASS;
+}
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
new file mode 100644
index 000000000000..b4fd6e786316
--- /dev/null
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -0,0 +1,827 @@
+/* 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);
+ 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_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ ipvlan_dbg(1, "%s[%d]: Adding IPv6=%x:%x:%x:%x address for %s intf\n",
+ __func__, __LINE__, ip6_addr->s6_addr32[0],
+ ip6_addr->s6_addr32[1], ip6_addr->s6_addr32[2],
+ ip6_addr->s6_addr32[3], ipvlan->dev->name);
+ addr->master = ipvlan;
+ memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr));
+ addr->atype = IPVL_IPV6;
+ list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ ipvlan->ipv6cnt++;
+ ipvlan_ht_addr_add(ipvlan, addr);
+
+ return 0;
+}
+
+static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
+{
+ struct ipvl_addr *addr = 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_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ ipvlan_dbg(1, "%s[%d]: Adding IPv4=%x address for %s intf.\n",
+ __func__, __LINE__, ip4_addr->s_addr, ipvlan->dev->name);
+ addr->master = ipvlan;
+ memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr));
+ addr->atype = IPVL_IPV4;
+ list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ ipvlan->ipv4cnt++;
+ ipvlan_ht_addr_add(ipvlan, addr);
+ ipvlan_set_broadcast_mac_filter(ipvlan, true);
+
+ return 0;
+}
+
+static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
+{
+ struct ipvl_addr *addr = 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 Iproute2 next] ip link: Add ipvlan support to the iproute2/ip util
From: Mahesh Bandewar @ 2014-11-14 6:56 UTC (permalink / raw)
To: netdev, Stephen Hemminger
Cc: Eric Dumazet, Maciej Zenczykowski, Laurent Chavey, Tim Hockin,
David Miller, Brandon Philips, Pavel Emelianov, Mahesh Bandewar
Adding basic support to create virtual devices using 'ip'
utility. Following is the syntax -
ip link add link <master> <virtual> mode [ l2 | l3 ]
e.g. ip link add link eth0 ipvl0 mode l3
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Maciej Żenczykowski <maze@google.com>
Cc: Laurent Chavey <chavey@google.com>
Cc: Tim Hockin <thockin@google.com>
Cc: Brandon Philips <brandon.philips@coreos.com>
Cc: Pavel Emelianov <xemul@parallels.com>
---
include/linux/if_link.h | 14 ++++++++
ip/Makefile | 2 +-
ip/iplink.c | 2 +-
ip/iplink_ipvlan.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 101 insertions(+), 2 deletions(-)
create mode 100644 ip/iplink_ipvlan.c
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 47320636361c..ef1e9f73fb15 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -325,6 +325,20 @@ enum macvlan_macaddr_mode {
MACVLAN_MACADDR_SET,
};
+/* IPVLAN section */
+enum {
+ IFLA_IPVLAN_UNSPEC,
+ IFLA_IPVLAN_MODE,
+ __IFLA_IPVLAN_MAX,
+};
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+enum ipvlan_mode {
+ IPVLAN_MODE_L2 = 0, /* Process packets all the way upto L2 */
+ IPVLAN_MODE_L3 = 1, /* Process Packets all the way upto L3 */
+};
+
#define MACVLAN_FLAG_NOPROMISC 1
/* VXLAN section */
diff --git a/ip/Makefile b/ip/Makefile
index fdc82f7286a0..01901bc8571c 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -6,7 +6,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o link_vti6.o \
iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \
- iplink_bridge.o iplink_bridge_slave.o ipfou.o
+ iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o
RTMONOBJ=rtmon.o
diff --git a/ip/iplink.c b/ip/iplink.c
index 43b26f4cea08..2fc7fa2b47c8 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -90,7 +90,7 @@ void iplink_usage(void)
fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n");
fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n");
fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n");
- fprintf(stderr, " bond_slave }\n");
+ fprintf(stderr, " bond_slave | ipvlan }\n");
}
exit(-1);
}
diff --git a/ip/iplink_ipvlan.c b/ip/iplink_ipvlan.c
new file mode 100644
index 000000000000..6712fdb92fd4
--- /dev/null
+++ b/ip/iplink_ipvlan.c
@@ -0,0 +1,85 @@
+/* iplink_ipvlan.c IPVLAN device support
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Mahesh Bandewar <maheshb@google.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/if_link.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... ipvlan [ mode { l2 | l3 } ]\n");
+}
+
+static int mode_arg(void)
+{
+ fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", "
+ "or \"l3\"\n");
+ return -1;
+}
+
+static int ipvlan_parse_opt(struct link_util *lu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ while (argc > 0) {
+ if (matches(*argv, "mode") == 0) {
+ __u16 mode = 0;
+ NEXT_ARG();
+
+ if (strcmp(*argv, "l2") == 0)
+ mode = IPVLAN_MODE_L2;
+ else if (strcmp(*argv, "l3") == 0)
+ mode = IPVLAN_MODE_L3;
+ else
+ mode_arg();
+
+ addattr16(n, 1024, IFLA_IPVLAN_MODE, mode);
+ } else if (matches(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "ipvlan: unknown option \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--, argv++;
+ }
+
+ return 0;
+}
+
+static void ipvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+
+ if (!tb)
+ return;
+
+ if (tb[IFLA_IPVLAN_MODE]) {
+ if (RTA_PAYLOAD(tb[IFLA_IPVLAN_MODE]) == sizeof(__u16)) {
+ __u16 mode = rta_getattr_u16(tb[IFLA_IPVLAN_MODE]);
+
+ fprintf(f, " mode %s ",
+ mode == IPVLAN_MODE_L2 ? "l2"
+ : mode == IPVLAN_MODE_L3 ? "l3" : "unknown");
+ }
+ }
+}
+
+struct link_util ipvlan_link_util = {
+ .id = "ipvlan",
+ .maxattr = IFLA_IPVLAN_MAX,
+ .parse_opt = ipvlan_parse_opt,
+ .print_opt = ipvlan_print_opt,
+};
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* Re: [PATCH net-next v1] ipvlan: Initial check-in of the IPVLAN driver.
From: Eric Dumazet @ 2014-11-14 7:16 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: <1415946547-25811-1-git-send-email-maheshb@google.com>
On Thu, 2014-11-13 at 22:29 -0800, Mahesh Bandewar wrote:
> +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;
> +}
RX_HANDLER_ANOTHER makes no sense here...
Also you are leaking skb.
kfree_skb(skb);
return NET_XMIT_DROP;
^ permalink raw reply
* Re: [PATCH net-next v1] ipvlan: Initial check-in of the IPVLAN driver.
From: Eric Dumazet @ 2014-11-14 7:19 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: <1415946547-25811-1-git-send-email-maheshb@google.com>
On Thu, 2014-11-13 at 22:29 -0800, Mahesh Bandewar wrote:
> +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;
Same problem here. RX_HANDLER_CONSUMED makes no sense.
> +
> + /* 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);
> +}
> +
^ permalink raw reply
* Re: Device Tree Binding for Marvell DSA Switch on imx28 board over Mdio Interface
From: Oliver Graute @ 2014-11-14 7:39 UTC (permalink / raw)
To: Florian Fainelli; +Cc: netdev
In-Reply-To: <54650E9C.7080708@gmail.com>
On Thu, Nov 13, 2014 at 9:03 PM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> On 11/13/2014 07:15 AM, Oliver Graute wrote:
>> Hello Florian,
>>
>> On Wed, Nov 12, 2014 at 8:19 PM, Florian Fainelli <f.fainelli@gmail.com> wrote:
>>> On 11/12/2014 05:07 AM, Oliver Graute wrote:
>>>> Hello,
>>>>
>>>> how do I specify the DSA node and the MDIO node in the Device Tree
>>>> Binding to integrate a Marvell 88e6071 switch with a imx28 board?
>>>>
>>>> On my board the Marvell switch 88e6071 is connected via phy1 (on a
>>>> imx28 PCB) to phy5 on the Marvell switch (on a Switch PCB). All phys
>>>> are connected via the same MDIO Bus.
>>>>
>>>> I enabled the Marvell DSA Support Driver, Gianfar Ethernet Driver and
>>>> Freescale PQ MDIO Driver in the Kernel (I' am not sure if this is the
>>>> right choice for imx28 fec ethernet controller is it?)
>>>>
>>
>> I changed my DeviceTree according to your proposal. Now I got a ENODEV 19
>> in dsa_of_probe. Because of_find_device_by_node(ethernet) is returning 0.
>> Is my ethernet setting still wrong?
>
> Is your ethernet driver also modular? If so, you will need it to be
> loaded *before* dsa. of_find_device_by_node() also needs the ethernet
> driver to be a platform_driver.
No my Freescale FEC PHY driver is not a module. FEC is a imx28/arm
platform driver or not?
I loaded the DSA as a Kernel module to make sure that the DSA probing
is happening when the switch is really on. I enable the SWITCH ON Pin
on bootup with a systemd started script. Then I write some registers
on the switch with a userspace mii tool. This manually writing of some
switch registers works fine via the MII Bus using ioctl(SIOCGMIIPHY).
But i would like to integrate the switch with a full dsa driver.
currently its failing with dsa_of_probe returns=-19
> NB: I have a patch that looks up a net_device based on the struct
> device_node that might be better to use, since it makes no assumption
> about whether that is a platform_device/pci_device etc...
can you give me a link to this patch?
Best Regards,
Oliver
^ permalink raw reply
* Re: [PATCH net-next] net: introduce SO_INCOMING_CPU
From: Michael Kerrisk @ 2014-11-14 8:05 UTC (permalink / raw)
To: Eric Dumazet
Cc: David Miller, netdev, Ying Cai, Willem de Bruijn, Neal Cardwell,
Linux API
In-Reply-To: <1415393472.13896.119.camel-XN9IlZ5yJG9HTL0Zs8A6p/gx64E7kk8eUsxypvmhUTTZJqsBc5GL+g@public.gmane.org>
Hi Eric,
Since this is an API change ( Documentation/SubmitChecklist),
linux-api@ should be CCed.
Thanks,
Michael
On Fri, Nov 7, 2014 at 9:51 PM, Eric Dumazet <eric.dumazet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> From: Eric Dumazet <edumazet-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
>
> Alternative to RPS/RFS is to use hardware support for multi queue.
>
> Then split a set of million of sockets into worker threads, each
> one using epoll() to manage events on its own socket pool.
>
> Ideally, we want one thread per RX/TX queue/cpu, but we have no way to
> know after accept() or connect() on which queue/cpu a socket is managed.
>
> We normally use one cpu per RX queue (IRQ smp_affinity being properly
> set), so remembering on socket structure which cpu delivered last packet
> is enough to solve the problem.
>
> After accept(), connect(), or even file descriptor passing around
> processes, applications can use :
>
> int cpu;
> socklen_t len = sizeof(cpu);
>
> getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len);
>
> And use this information to put the socket into the right silo
> for optimal performance, as all networking stack should run
> on the appropriate cpu, without need to send IPI (RPS/RFS).
>
> Signed-off-by: Eric Dumazet <edumazet-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> ---
> arch/alpha/include/uapi/asm/socket.h | 2 ++
> arch/avr32/include/uapi/asm/socket.h | 2 ++
> arch/cris/include/uapi/asm/socket.h | 2 ++
> arch/frv/include/uapi/asm/socket.h | 2 ++
> arch/ia64/include/uapi/asm/socket.h | 2 ++
> arch/m32r/include/uapi/asm/socket.h | 2 ++
> arch/mips/include/uapi/asm/socket.h | 2 ++
> arch/mn10300/include/uapi/asm/socket.h | 2 ++
> arch/parisc/include/uapi/asm/socket.h | 2 ++
> arch/powerpc/include/uapi/asm/socket.h | 2 ++
> arch/s390/include/uapi/asm/socket.h | 2 ++
> arch/sparc/include/uapi/asm/socket.h | 2 ++
> arch/xtensa/include/uapi/asm/socket.h | 2 ++
> include/net/sock.h | 12 ++++++++++++
> include/uapi/asm-generic/socket.h | 2 ++
> net/core/sock.c | 5 +++++
> net/ipv4/tcp_ipv4.c | 1 +
> net/ipv4/udp.c | 1 +
> net/ipv6/tcp_ipv6.c | 1 +
> net/ipv6/udp.c | 1 +
> net/sctp/ulpqueue.c | 5 +++--
> 21 files changed, 52 insertions(+), 2 deletions(-)
>
> diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
> index 3de1394bcab821984674e89a3ee022cc6dd5f0f2..e2fe0700b3b442bffc1f606b1b8b0bb7759aa157 100644
> --- a/arch/alpha/include/uapi/asm/socket.h
> +++ b/arch/alpha/include/uapi/asm/socket.h
> @@ -87,4 +87,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _UAPI_ASM_SOCKET_H */
> diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h
> index 6e6cd159924b1855aa5f1811ad4e4c60b403c431..92121b0f5b989a61c008e0be24030725bab88e36 100644
> --- a/arch/avr32/include/uapi/asm/socket.h
> +++ b/arch/avr32/include/uapi/asm/socket.h
> @@ -80,4 +80,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _UAPI__ASM_AVR32_SOCKET_H */
> diff --git a/arch/cris/include/uapi/asm/socket.h b/arch/cris/include/uapi/asm/socket.h
> index ed94e5ed0a238c2750e677ccb806a6bc0a94041a..60f60f5b9b35bd219d7a9834fe5394e8ac5fdbab 100644
> --- a/arch/cris/include/uapi/asm/socket.h
> +++ b/arch/cris/include/uapi/asm/socket.h
> @@ -82,6 +82,8 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
>
>
> diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h
> index ca2c6e6f31c6817780d31a246652adcc9847e373..2c6890209ea60c149bf097c2a1b369519cb8c301 100644
> --- a/arch/frv/include/uapi/asm/socket.h
> +++ b/arch/frv/include/uapi/asm/socket.h
> @@ -80,5 +80,7 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
>
> diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h
> index a1b49bac7951929127ed08db549218c2c16ccf89..09a93fb566f6c6c6fe29c10c95b931881843d1cd 100644
> --- a/arch/ia64/include/uapi/asm/socket.h
> +++ b/arch/ia64/include/uapi/asm/socket.h
> @@ -89,4 +89,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_IA64_SOCKET_H */
> diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h
> index 6c9a24b3aefa3a4f3048c17a7fa06d97b585ec14..e8589819c2743c6e112b15a245fc3ebd146e6313 100644
> --- a/arch/m32r/include/uapi/asm/socket.h
> +++ b/arch/m32r/include/uapi/asm/socket.h
> @@ -80,4 +80,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_M32R_SOCKET_H */
> diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
> index a14baa218c76f14de988ef106bdac5dadc48aceb..2e9ee8c55a103a0337d9f80f71fe9ef28be1154b 100644
> --- a/arch/mips/include/uapi/asm/socket.h
> +++ b/arch/mips/include/uapi/asm/socket.h
> @@ -98,4 +98,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _UAPI_ASM_SOCKET_H */
> diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h
> index 6aa3ce1854aa9523d46bc28851eddabd59edeb37..f3492e8c9f7009c33e07168df916f7337bef3929 100644
> --- a/arch/mn10300/include/uapi/asm/socket.h
> +++ b/arch/mn10300/include/uapi/asm/socket.h
> @@ -80,4 +80,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
> diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
> index fe35ceacf0e72cad69a43d9b1ce7b8f5ec3da98a..7984a1cab3da980f1f810827967b4b67616eb89b 100644
> --- a/arch/parisc/include/uapi/asm/socket.h
> +++ b/arch/parisc/include/uapi/asm/socket.h
> @@ -79,4 +79,6 @@
>
> #define SO_BPF_EXTENSIONS 0x4029
>
> +#define SO_INCOMING_CPU 0x402A
> +
> #endif /* _UAPI_ASM_SOCKET_H */
> diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h
> index a9c3e2e18c054a1e952fe33599401de57c6a6544..3474e4ef166df4a573773916b325d0fa9f3b45d0 100644
> --- a/arch/powerpc/include/uapi/asm/socket.h
> +++ b/arch/powerpc/include/uapi/asm/socket.h
> @@ -87,4 +87,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_POWERPC_SOCKET_H */
> diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h
> index e031332096d7c7b23b5953680289e8f3bcc3b378..8457636c33e1b67a9b7804daa05627839035a8fb 100644
> --- a/arch/s390/include/uapi/asm/socket.h
> +++ b/arch/s390/include/uapi/asm/socket.h
> @@ -86,4 +86,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
> diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
> index 54d9608681b6947ae25dab008f808841d96125c0..4a8003a9416348006cfa85d5bcdf7553c8d23958 100644
> --- a/arch/sparc/include/uapi/asm/socket.h
> +++ b/arch/sparc/include/uapi/asm/socket.h
> @@ -76,6 +76,8 @@
>
> #define SO_BPF_EXTENSIONS 0x0032
>
> +#define SO_INCOMING_CPU 0x0033
> +
> /* Security levels - as per NRL IPv6 - don't actually do anything */
> #define SO_SECURITY_AUTHENTICATION 0x5001
> #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
> diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h
> index 39acec0cf0b1d500c1c40f9b523ef3a9a142c2f1..c46f6a696849c6f7f8a34b2cc522b48e04b17380 100644
> --- a/arch/xtensa/include/uapi/asm/socket.h
> +++ b/arch/xtensa/include/uapi/asm/socket.h
> @@ -91,4 +91,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _XTENSA_SOCKET_H */
> diff --git a/include/net/sock.h b/include/net/sock.h
> index 6767d75ecb17693eb59a99b8218da4319854ccc0..7789b59c0c400eb99f65d1f0e03cd9773664cf93 100644
> --- a/include/net/sock.h
> +++ b/include/net/sock.h
> @@ -273,6 +273,7 @@ struct cg_proto;
> * @sk_rcvtimeo: %SO_RCVTIMEO setting
> * @sk_sndtimeo: %SO_SNDTIMEO setting
> * @sk_rxhash: flow hash received from netif layer
> + * @sk_incoming_cpu: record cpu processing incoming packets
> * @sk_txhash: computed flow hash for use on transmit
> * @sk_filter: socket filtering instructions
> * @sk_protinfo: private area, net family specific, when not using slab
> @@ -350,6 +351,12 @@ struct sock {
> #ifdef CONFIG_RPS
> __u32 sk_rxhash;
> #endif
> + u16 sk_incoming_cpu;
> + /* 16bit hole
> + * Warned : sk_incoming_cpu can be set from softirq,
> + * Do not use this hole without fully understanding possible issues.
> + */
> +
> __u32 sk_txhash;
> #ifdef CONFIG_NET_RX_BUSY_POLL
> unsigned int sk_napi_id;
> @@ -833,6 +840,11 @@ static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
> return sk->sk_backlog_rcv(sk, skb);
> }
>
> +static inline void sk_incoming_cpu_update(struct sock *sk)
> +{
> + sk->sk_incoming_cpu = raw_smp_processor_id();
> +}
> +
> static inline void sock_rps_record_flow_hash(__u32 hash)
> {
> #ifdef CONFIG_RPS
> diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
> index ea0796bdcf88404ef0f127eb6e64ba00c16ea856..f541ccefd4acbeb4ad757be9dbf4b67f204bf21d 100644
> --- a/include/uapi/asm-generic/socket.h
> +++ b/include/uapi/asm-generic/socket.h
> @@ -82,4 +82,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* __ASM_GENERIC_SOCKET_H */
> diff --git a/net/core/sock.c b/net/core/sock.c
> index ac56dd06c306f3712e57ce8e4724c79565589499..0725cf0cb685787b2122606437da53299fb24621 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1213,6 +1213,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
> v.val = sk->sk_max_pacing_rate;
> break;
>
> + case SO_INCOMING_CPU:
> + v.val = sk->sk_incoming_cpu;
> + break;
> +
> default:
> return -ENOPROTOOPT;
> }
> @@ -1517,6 +1521,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
>
> newsk->sk_err = 0;
> newsk->sk_priority = 0;
> + newsk->sk_incoming_cpu = raw_smp_processor_id();
> /*
> * Before updating sk_refcnt, we must commit prior changes to memory
> * (Documentation/RCU/rculist_nulls.txt for details)
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index 9c7d7621466b1241f404a5ca11de809dcff2d02a..3893f51972f28271a6d27a763c05495c5c2554f7 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -1662,6 +1662,7 @@ process:
> goto discard_and_relse;
>
> sk_mark_napi_id(sk, skb);
> + sk_incoming_cpu_update(sk);
> skb->dev = NULL;
>
> bh_lock_sock_nested(sk);
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index df19027f44f3d6fbe13dec78d3b085968dbf2329..f52b6081158e87caa5df32e8e5d27dbf314a01b1 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -1445,6 +1445,7 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
> if (inet_sk(sk)->inet_daddr) {
> sock_rps_save_rxhash(sk, skb);
> sk_mark_napi_id(sk, skb);
> + sk_incoming_cpu_update(sk);
> }
>
> rc = sock_queue_rcv_skb(sk, skb);
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index ace29b60813cf8a1d7182ad2262cbcbd21810fa7..ac40d23204b5e55da5172c80dafd1d4854b370d5 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -1455,6 +1455,7 @@ process:
> goto discard_and_relse;
>
> sk_mark_napi_id(sk, skb);
> + sk_incoming_cpu_update(sk);
> skb->dev = NULL;
>
> bh_lock_sock_nested(sk);
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index 9b6809232b178c16d699ce3d152196b8c4cb096b..0125ca3daf47a4a3333e7462a11550d3e2f96875 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -577,6 +577,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
> if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
> sock_rps_save_rxhash(sk, skb);
> sk_mark_napi_id(sk, skb);
> + sk_incoming_cpu_update(sk);
> }
>
> rc = sock_queue_rcv_skb(sk, skb);
> diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
> index d49dc2ed30adb97a809eb37902b9956c366a2862..ce469d648ffbe166f9ae1c5650f481256f31a7f8 100644
> --- a/net/sctp/ulpqueue.c
> +++ b/net/sctp/ulpqueue.c
> @@ -205,9 +205,10 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
> if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN))
> goto out_free;
>
> - if (!sctp_ulpevent_is_notification(event))
> + if (!sctp_ulpevent_is_notification(event)) {
> sk_mark_napi_id(sk, skb);
> -
> + sk_incoming_cpu_update(sk);
> + }
> /* Check if the user wishes to receive this event. */
> if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
> goto out_free;
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/
^ permalink raw reply
* Re: [PATCH v2 net-next 0/2] net: SO_INCOMING_CPU support
From: Michael Kerrisk @ 2014-11-14 8:06 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S. Miller, netdev, Neal Cardwell, Willem de Bruijn,
Ying Cai, Linux API
In-Reply-To: <1415714068-21028-1-git-send-email-edumazet@google.com>
[CC += linux-api@]
On Tue, Nov 11, 2014 at 2:54 PM, Eric Dumazet <edumazet@google.com> wrote:
> SO_INCOMING_CPU socket option (read by getsockopt()) provides
> an alternative to RPS/RFS for high performance servers using
> multi queues NIC.
>
> TCP should use sk_mark_napi_id() for established sockets only.
>
> Eric Dumazet (2):
> tcp: move sk_mark_napi_id() at the right place
> net: introduce SO_INCOMING_CPU
>
> arch/alpha/include/uapi/asm/socket.h | 2 ++
> arch/avr32/include/uapi/asm/socket.h | 2 ++
> arch/cris/include/uapi/asm/socket.h | 2 ++
> arch/frv/include/uapi/asm/socket.h | 2 ++
> arch/ia64/include/uapi/asm/socket.h | 2 ++
> arch/m32r/include/uapi/asm/socket.h | 2 ++
> arch/mips/include/uapi/asm/socket.h | 2 ++
> arch/mn10300/include/uapi/asm/socket.h | 2 ++
> arch/parisc/include/uapi/asm/socket.h | 2 ++
> arch/powerpc/include/uapi/asm/socket.h | 2 ++
> arch/s390/include/uapi/asm/socket.h | 2 ++
> arch/sparc/include/uapi/asm/socket.h | 2 ++
> arch/xtensa/include/uapi/asm/socket.h | 2 ++
> include/net/sock.h | 12 ++++++++++++
> include/uapi/asm-generic/socket.h | 2 ++
> net/core/sock.c | 5 +++++
> net/ipv4/tcp_ipv4.c | 4 +++-
> net/ipv4/udp.c | 1 +
> net/ipv6/tcp_ipv6.c | 4 +++-
> net/ipv6/udp.c | 1 +
> net/sctp/ulpqueue.c | 5 +++--
> 21 files changed, 56 insertions(+), 4 deletions(-)
>
> --
> 2.1.0.rc2.206.gedb03e5
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/
^ permalink raw reply
* Re: [PATCH v2 net-next 2/2] net: introduce SO_INCOMING_CPU
From: Michael Kerrisk @ 2014-11-14 8:07 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S. Miller, netdev, Neal Cardwell, Willem de Bruijn,
Ying Cai, Linux API
In-Reply-To: <1415714068-21028-3-git-send-email-edumazet-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
[CC += linux-api@]
On Tue, Nov 11, 2014 at 2:54 PM, Eric Dumazet <edumazet-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> wrote:
> Alternative to RPS/RFS is to use hardware support for multiple
> queues.
>
> Then split a set of million of sockets into worker threads, each
> one using epoll() to manage events on its own socket pool.
>
> Ideally, we want one thread per RX/TX queue/cpu, but we have no way to
> know after accept() or connect() on which queue/cpu a socket is managed.
>
> We normally use one cpu per RX queue (IRQ smp_affinity being properly
> set), so remembering on socket structure which cpu delivered last packet
> is enough to solve the problem.
>
> After accept(), connect(), or even file descriptor passing around
> processes, applications can use :
>
> int cpu;
> socklen_t len = sizeof(cpu);
>
> getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len);
>
> And use this information to put the socket into the right silo
> for optimal performance, as all networking stack should run
> on the appropriate cpu, without need to send IPI (RPS/RFS).
>
> Signed-off-by: Eric Dumazet <edumazet-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> ---
> arch/alpha/include/uapi/asm/socket.h | 2 ++
> arch/avr32/include/uapi/asm/socket.h | 2 ++
> arch/cris/include/uapi/asm/socket.h | 2 ++
> arch/frv/include/uapi/asm/socket.h | 2 ++
> arch/ia64/include/uapi/asm/socket.h | 2 ++
> arch/m32r/include/uapi/asm/socket.h | 2 ++
> arch/mips/include/uapi/asm/socket.h | 2 ++
> arch/mn10300/include/uapi/asm/socket.h | 2 ++
> arch/parisc/include/uapi/asm/socket.h | 2 ++
> arch/powerpc/include/uapi/asm/socket.h | 2 ++
> arch/s390/include/uapi/asm/socket.h | 2 ++
> arch/sparc/include/uapi/asm/socket.h | 2 ++
> arch/xtensa/include/uapi/asm/socket.h | 2 ++
> include/net/sock.h | 12 ++++++++++++
> include/uapi/asm-generic/socket.h | 2 ++
> net/core/sock.c | 5 +++++
> net/ipv4/tcp_ipv4.c | 1 +
> net/ipv4/udp.c | 1 +
> net/ipv6/tcp_ipv6.c | 1 +
> net/ipv6/udp.c | 1 +
> net/sctp/ulpqueue.c | 5 +++--
> 21 files changed, 52 insertions(+), 2 deletions(-)
>
> diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
> index 3de1394bcab8..e2fe0700b3b4 100644
> --- a/arch/alpha/include/uapi/asm/socket.h
> +++ b/arch/alpha/include/uapi/asm/socket.h
> @@ -87,4 +87,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _UAPI_ASM_SOCKET_H */
> diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h
> index 6e6cd159924b..92121b0f5b98 100644
> --- a/arch/avr32/include/uapi/asm/socket.h
> +++ b/arch/avr32/include/uapi/asm/socket.h
> @@ -80,4 +80,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _UAPI__ASM_AVR32_SOCKET_H */
> diff --git a/arch/cris/include/uapi/asm/socket.h b/arch/cris/include/uapi/asm/socket.h
> index ed94e5ed0a23..60f60f5b9b35 100644
> --- a/arch/cris/include/uapi/asm/socket.h
> +++ b/arch/cris/include/uapi/asm/socket.h
> @@ -82,6 +82,8 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
>
>
> diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h
> index ca2c6e6f31c6..2c6890209ea6 100644
> --- a/arch/frv/include/uapi/asm/socket.h
> +++ b/arch/frv/include/uapi/asm/socket.h
> @@ -80,5 +80,7 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
>
> diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h
> index a1b49bac7951..09a93fb566f6 100644
> --- a/arch/ia64/include/uapi/asm/socket.h
> +++ b/arch/ia64/include/uapi/asm/socket.h
> @@ -89,4 +89,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_IA64_SOCKET_H */
> diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h
> index 6c9a24b3aefa..e8589819c274 100644
> --- a/arch/m32r/include/uapi/asm/socket.h
> +++ b/arch/m32r/include/uapi/asm/socket.h
> @@ -80,4 +80,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_M32R_SOCKET_H */
> diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
> index a14baa218c76..2e9ee8c55a10 100644
> --- a/arch/mips/include/uapi/asm/socket.h
> +++ b/arch/mips/include/uapi/asm/socket.h
> @@ -98,4 +98,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _UAPI_ASM_SOCKET_H */
> diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h
> index 6aa3ce1854aa..f3492e8c9f70 100644
> --- a/arch/mn10300/include/uapi/asm/socket.h
> +++ b/arch/mn10300/include/uapi/asm/socket.h
> @@ -80,4 +80,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
> diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
> index fe35ceacf0e7..7984a1cab3da 100644
> --- a/arch/parisc/include/uapi/asm/socket.h
> +++ b/arch/parisc/include/uapi/asm/socket.h
> @@ -79,4 +79,6 @@
>
> #define SO_BPF_EXTENSIONS 0x4029
>
> +#define SO_INCOMING_CPU 0x402A
> +
> #endif /* _UAPI_ASM_SOCKET_H */
> diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h
> index a9c3e2e18c05..3474e4ef166d 100644
> --- a/arch/powerpc/include/uapi/asm/socket.h
> +++ b/arch/powerpc/include/uapi/asm/socket.h
> @@ -87,4 +87,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_POWERPC_SOCKET_H */
> diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h
> index e031332096d7..8457636c33e1 100644
> --- a/arch/s390/include/uapi/asm/socket.h
> +++ b/arch/s390/include/uapi/asm/socket.h
> @@ -86,4 +86,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _ASM_SOCKET_H */
> diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
> index 54d9608681b6..4a8003a94163 100644
> --- a/arch/sparc/include/uapi/asm/socket.h
> +++ b/arch/sparc/include/uapi/asm/socket.h
> @@ -76,6 +76,8 @@
>
> #define SO_BPF_EXTENSIONS 0x0032
>
> +#define SO_INCOMING_CPU 0x0033
> +
> /* Security levels - as per NRL IPv6 - don't actually do anything */
> #define SO_SECURITY_AUTHENTICATION 0x5001
> #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
> diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h
> index 39acec0cf0b1..c46f6a696849 100644
> --- a/arch/xtensa/include/uapi/asm/socket.h
> +++ b/arch/xtensa/include/uapi/asm/socket.h
> @@ -91,4 +91,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* _XTENSA_SOCKET_H */
> diff --git a/include/net/sock.h b/include/net/sock.h
> index 7db3db112baa..ff2c3f11fb8f 100644
> --- a/include/net/sock.h
> +++ b/include/net/sock.h
> @@ -273,6 +273,7 @@ struct cg_proto;
> * @sk_rcvtimeo: %SO_RCVTIMEO setting
> * @sk_sndtimeo: %SO_SNDTIMEO setting
> * @sk_rxhash: flow hash received from netif layer
> + * @sk_incoming_cpu: record cpu processing incoming packets
> * @sk_txhash: computed flow hash for use on transmit
> * @sk_filter: socket filtering instructions
> * @sk_protinfo: private area, net family specific, when not using slab
> @@ -350,6 +351,12 @@ struct sock {
> #ifdef CONFIG_RPS
> __u32 sk_rxhash;
> #endif
> + u16 sk_incoming_cpu;
> + /* 16bit hole
> + * Warned : sk_incoming_cpu can be set from softirq,
> + * Do not use this hole without fully understanding possible issues.
> + */
> +
> __u32 sk_txhash;
> #ifdef CONFIG_NET_RX_BUSY_POLL
> unsigned int sk_napi_id;
> @@ -833,6 +840,11 @@ static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
> return sk->sk_backlog_rcv(sk, skb);
> }
>
> +static inline void sk_incoming_cpu_update(struct sock *sk)
> +{
> + sk->sk_incoming_cpu = raw_smp_processor_id();
> +}
> +
> static inline void sock_rps_record_flow_hash(__u32 hash)
> {
> #ifdef CONFIG_RPS
> diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
> index ea0796bdcf88..f541ccefd4ac 100644
> --- a/include/uapi/asm-generic/socket.h
> +++ b/include/uapi/asm-generic/socket.h
> @@ -82,4 +82,6 @@
>
> #define SO_BPF_EXTENSIONS 48
>
> +#define SO_INCOMING_CPU 49
> +
> #endif /* __ASM_GENERIC_SOCKET_H */
> diff --git a/net/core/sock.c b/net/core/sock.c
> index 15e0c67b1069..14998b161035 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1213,6 +1213,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
> v.val = sk->sk_max_pacing_rate;
> break;
>
> + case SO_INCOMING_CPU:
> + v.val = sk->sk_incoming_cpu;
> + break;
> +
> default:
> return -ENOPROTOOPT;
> }
> @@ -1517,6 +1521,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
>
> newsk->sk_err = 0;
> newsk->sk_priority = 0;
> + newsk->sk_incoming_cpu = raw_smp_processor_id();
> /*
> * Before updating sk_refcnt, we must commit prior changes to memory
> * (Documentation/RCU/rculist_nulls.txt for details)
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index 8893598a4124..2c6a955fd5c3 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -1663,6 +1663,7 @@ process:
> if (sk_filter(sk, skb))
> goto discard_and_relse;
>
> + sk_incoming_cpu_update(sk);
> skb->dev = NULL;
>
> bh_lock_sock_nested(sk);
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index cd0db5471bb5..52235ca1f352 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -1445,6 +1445,7 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
> if (inet_sk(sk)->inet_daddr) {
> sock_rps_save_rxhash(sk, skb);
> sk_mark_napi_id(sk, skb);
> + sk_incoming_cpu_update(sk);
> }
>
> rc = sock_queue_rcv_skb(sk, skb);
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index fd8e50b380e7..1985b4933a6b 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -1456,6 +1456,7 @@ process:
> if (sk_filter(sk, skb))
> goto discard_and_relse;
>
> + sk_incoming_cpu_update(sk);
> skb->dev = NULL;
>
> bh_lock_sock_nested(sk);
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index f6ba535b6feb..2c7790c9ac65 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -577,6 +577,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
> if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
> sock_rps_save_rxhash(sk, skb);
> sk_mark_napi_id(sk, skb);
> + sk_incoming_cpu_update(sk);
> }
>
> rc = sock_queue_rcv_skb(sk, skb);
> diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
> index d49dc2ed30ad..ce469d648ffb 100644
> --- a/net/sctp/ulpqueue.c
> +++ b/net/sctp/ulpqueue.c
> @@ -205,9 +205,10 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
> if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN))
> goto out_free;
>
> - if (!sctp_ulpevent_is_notification(event))
> + if (!sctp_ulpevent_is_notification(event)) {
> sk_mark_napi_id(sk, skb);
> -
> + sk_incoming_cpu_update(sk);
> + }
> /* Check if the user wishes to receive this event. */
> if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
> goto out_free;
> --
> 2.1.0.rc2.206.gedb03e5
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/
^ permalink raw reply
* [PATCH v2] can: Fix bug in suspend/resume
From: Kedareswara rao Appana @ 2014-11-14 8:16 UTC (permalink / raw)
To: wg, mkl, michal.simek, soren.brinkmann, grant.likely, robh+dt
Cc: linux-can, netdev, linux-arm-kernel, linux-kernel, devicetree,
Kedareswara rao Appana
The drvdata in the suspend/resume is of type struct net_device,
not the platform device.Enable the clocks in the suspend before
accessing the registers of the CAN.
Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v2:
- Removed the struct platform_device* from suspend/resume
as suggest by Lothar.
- The clocks are getting disabled and un prepared at the end of the probe.
In the suspend the driver is doing a register write.In order
To do that register write we have to again enable and prepare the clocks.
drivers/net/can/xilinx_can.c | 20 ++++++++++++++++----
1 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index 5e8b560..485262f 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -972,15 +972,28 @@ static const struct net_device_ops xcan_netdev_ops = {
*/
static int __maybe_unused xcan_suspend(struct device *dev)
{
- struct platform_device *pdev = dev_get_drvdata(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct xcan_priv *priv = netdev_priv(ndev);
+ int ret;
if (netif_running(ndev)) {
netif_stop_queue(ndev);
netif_device_detach(ndev);
}
+ ret = clk_prepare_enable(priv->can_clk);
+ if (ret) {
+ dev_err(dev, "unable to enable device clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->bus_clk);
+ if (ret) {
+ dev_err(dev, "unable to enable bus clock\n");
+ clk_disable_unprepare(priv->can_clk);
+ return ret;
+ }
+
priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
priv->can.state = CAN_STATE_SLEEPING;
@@ -999,8 +1012,7 @@ static int __maybe_unused xcan_suspend(struct device *dev)
*/
static int __maybe_unused xcan_resume(struct device *dev)
{
- struct platform_device *pdev = dev_get_drvdata(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
--
1.7.4
^ permalink raw reply related
* Re: [PATCH v2] can: Fix bug in suspend/resume
From: Marc Kleine-Budde @ 2014-11-14 8:54 UTC (permalink / raw)
To: Kedareswara rao Appana, wg, michal.simek, soren.brinkmann,
grant.likely, robh+dt
Cc: linux-can, netdev, linux-arm-kernel, linux-kernel, devicetree,
Kedareswara rao Appana
In-Reply-To: <e7070431858e49e29cd03d76fd7a66c3@BN1AFFO11FD055.protection.gbl>
[-- Attachment #1: Type: text/plain, Size: 1517 bytes --]
On 11/14/2014 09:16 AM, Kedareswara rao Appana wrote:
> The drvdata in the suspend/resume is of type struct net_device,
> not the platform device.Enable the clocks in the suspend before
> accessing the registers of the CAN.
>
> Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
> ---
> Changes for v2:
> - Removed the struct platform_device* from suspend/resume
> as suggest by Lothar.
> - The clocks are getting disabled and un prepared at the end of the probe.
> In the suspend the driver is doing a register write.In order
> To do that register write we have to again enable and prepare the clocks.
Please look the at suspend/resume code and count the
clock_enable/disable manually. After a suspend/resume cycle, you have
enabled the clock twice, but disabled it once.
I think you have to abstract the clock handling behind runtime PM. I
haven't done this myself yet, but the strong feeling that this is a
possible solution to your problem. These links might help:
http://lwn.net/Articles/505683/
http://www.slideshare.net/linaroorg/runtime-pm
http://www.slideshare.net/linaroorg/lca14-407-deployingruntimepmonarmsocs
http://www.slideshare.net/SamsungOSG/shuah-khan-lpcpmops
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH] brcmfmac: kill URB when request timed out
From: Arend van Spriel @ 2014-11-14 8:57 UTC (permalink / raw)
To: Mathy Vanhoef, brudley, frankyl, meuleman, John Linville,
pieterpg, linux-wireless, brcm80211-dev-list, netdev,
linux-kernel
In-Reply-To: <5464187E.5060208@gmail.com>
On 13-11-14 03:33, Mathy Vanhoef wrote:
> Kill the submitted URB in brcmf_usb_dl_cmd if the request timed out. This
> assures the URB is never submitted twice. It also prevents a possible
> use-after-free of the URB transfer buffer if a timeout occurs.
>
Acked-by: Arend van Spriel <arend@broadcom.com>
> Signed-off-by: Mathy Vanhoef <vanhoefm@gmail.com>
> ---
> For a discussion about this patch and the underlying problem, see the mails
> with as subject "[PATCH] brcmfmac: unlink URB when request timed out".
>
> 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..4572def 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_kill_urb(devinfo->ctl_urb);
> ret = -ETIMEDOUT;
> - else
> + } else {
> memcpy(buffer, tmpbuf, buflen);
> + }
>
> finalize:
> kfree(tmpbuf);
>
^ permalink raw reply
* Re: [PATCH V4 2/3] can: m_can: update to support CAN FD features
From: Marc Kleine-Budde @ 2014-11-14 9:24 UTC (permalink / raw)
To: Oliver Hartkopp, Dong Aisheng, linux-can
Cc: wg, varkabhadram, netdev, linux-arm-kernel
In-Reply-To: <5464E2D7.7010906@hartkopp.net>
[-- Attachment #1: Type: text/plain, Size: 1656 bytes --]
On 11/13/2014 05:56 PM, Oliver Hartkopp wrote:
> On 11/13/2014 11:10 AM, Marc Kleine-Budde wrote:
>> On 11/07/2014 09:45 AM, Dong Aisheng wrote:
>
>>>
>>> - if (id & RX_BUF_RTR) {
>>> + if (id & RX_BUF_ESI) {
>>> + cf->flags |= CANFD_ESI;
>>> + netdev_dbg(dev, "ESI Error\n");
>>> + }
>>> +
>>> + if (!(dlc & RX_BUF_EDL) && (id & RX_BUF_RTR)) {
>>> cf->can_id |= CAN_RTR_FLAG;
>>
>> I just noticed, that you don't set the cf->dlc (or cf->len) in the RTR
>> case. Please create a separate patch that fixes this problem.
>>
>>> } else {
>>> id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC);
>>> - cf->can_dlc = get_can_dlc((id >> 16) & 0x0F);
>>> - *(u32 *)(cf->data + 0) = m_can_fifo_read(priv, fgi,
>>> - M_CAN_FIFO_DATA(0));
>>> - *(u32 *)(cf->data + 4) = m_can_fifo_read(priv, fgi,
>>> - M_CAN_FIFO_DATA(1));
>>> + if (dlc & RX_BUF_EDL)
>>> + cf->len = can_dlc2len((id >> 16) & 0x0F);
>>> + else
>>> + cf->len = get_can_dlc((id >> 16) & 0x0F);
>>> +
>
> Grr. I missed that one too :-(
>
> Thanks for catching it.
>
> As you committed patch 1 & 3 you expect a new single patch containing the
> (fixed) content of this patch 2, right?
No, please make it two patches:
First the Bugfix: One setting the cf->dlc in the RTR case, too.
Then the new feature: The other one adding CAN-FD support.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v4 8/8] net: can: c_can: Add support for TI am3352 DCAN
From: Marc Kleine-Budde @ 2014-11-14 9:25 UTC (permalink / raw)
To: Wolfram Sang, Roger Quadros
Cc: wg, tony, tglx, mugunthanvnm, george.cherian, balbi, nsekhar, nm,
sergei.shtylyov, linux-omap, linux-can, netdev
In-Reply-To: <20141113160658.GB1275@katana>
[-- Attachment #1: Type: text/plain, Size: 639 bytes --]
On 11/13/2014 05:06 PM, Wolfram Sang wrote:
> On Fri, Nov 07, 2014 at 04:49:22PM +0200, Roger Quadros wrote:
>> AM3352 SoC has 2 DCAN modules. Add compatible id and
>> raminit driver data for am3352 DCAN.
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>
> Acked-by: Wolfram Sang <wsa@the-dreams.de>
For the whole series or just this patch?
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* loan offer apply now at 2%, only serious replies needed
From: Barr. Mark Lewis @ 2014-11-14 0:50 UTC (permalink / raw)
Are you in need of any type of loan? of yes, kindly contact barrmarklewis13@gmail.com for more information. Thanks
^ permalink raw reply
* RE: [PATCHv2 net 2/4] be2net: Implement ndo_gso_check()
From: Sathya Perla @ 2014-11-14 9:43 UTC (permalink / raw)
To: Joe Stringer, netdev@vger.kernel.org
Cc: shahed.shaikh@qlogic.com, amirv@mellanox.com,
Dept-GELinuxNICDev@qlogic.com, therbert@google.com,
gerlitz.or@gmail.com, alexander.duyck@gmail.com,
linux-kernel@vger.kernel.org
In-Reply-To: <1415925495-59312-3-git-send-email-joestringer@nicira.com>
> -----Original Message-----
> From: Joe Stringer [mailto:joestringer@nicira.com]
>
> Use vxlan_gso_check() to advertise offload support for this NIC.
>
> Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Sathya Perla <sperla@emulex.com>
Thanks!
> ---
> v2: Refactor out vxlan helper.
> ---
> drivers/net/ethernet/emulex/benet/be_main.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/net/ethernet/emulex/benet/be_main.c
> b/drivers/net/ethernet/emulex/benet/be_main.c
> index 9a18e79..3e8475c 100644
> --- a/drivers/net/ethernet/emulex/benet/be_main.c
> +++ b/drivers/net/ethernet/emulex/benet/be_main.c
> @@ -4421,6 +4421,11 @@ static void be_del_vxlan_port(struct net_device
> *netdev, sa_family_t sa_family,
> "Disabled VxLAN offloads for UDP port %d\n",
> be16_to_cpu(port));
> }
> +
> +static bool be_gso_check(struct sk_buff *skb, struct net_device *dev)
> +{
> + return vxlan_gso_check(skb);
> +}
> #endif
>
> static const struct net_device_ops be_netdev_ops = {
> @@ -4450,6 +4455,7 @@ static const struct net_device_ops be_netdev_ops
> = {
> #ifdef CONFIG_BE2NET_VXLAN
> .ndo_add_vxlan_port = be_add_vxlan_port,
> .ndo_del_vxlan_port = be_del_vxlan_port,
> + .ndo_gso_check = be_gso_check,
> #endif
> };
>
> --
> 1.7.10.4
^ permalink raw reply
* Re: [PATCH Iproute2 next] ip link: Add ipvlan support to the iproute2/ip util
From: Michal Kubecek @ 2014-11-14 10:09 UTC (permalink / raw)
To: Mahesh Bandewar
Cc: netdev, Stephen Hemminger, Eric Dumazet, Maciej Zenczykowski,
Laurent Chavey, Tim Hockin, David Miller, Brandon Philips,
Pavel Emelianov
In-Reply-To: <1415948212-28927-1-git-send-email-maheshb@google.com>
On Thu, Nov 13, 2014 at 10:56:51PM -0800, Mahesh Bandewar wrote:
> Adding basic support to create virtual devices using 'ip'
> utility. Following is the syntax -
>
> ip link add link <master> <virtual> mode [ l2 | l3 ]
> e.g. ip link add link eth0 ipvl0 mode l3
I suppose "type ipvlan" is missing on both lines.
Michal Kubecek
^ permalink raw reply
* Re: [PATCH 1/3] arch: Introduce load_acquire() and store_release()
From: Will Deacon @ 2014-11-14 10:19 UTC (permalink / raw)
To: Alexander Duyck
Cc: linux-arch@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, mikey@neuling.org,
tony.luck@intel.com, mathieu.desnoyers@polymtl.ca,
donald.c.skidmore@intel.com, peterz@infradead.org,
benh@kernel.crashing.org, heiko.carstens@de.ibm.com,
oleg@redhat.com, davem@davemloft.net, michael@ellerman.id.au,
matthew.vick@intel.com, nic_swsd@realtek.com,
geert@linux-m68k.org, jeffrey.t.kirsher@intel.com
In-Reply-To: <20141113192723.12579.25343.stgit@ahduyck-server>
Hi Alex,
On Thu, Nov 13, 2014 at 07:27:23PM +0000, Alexander Duyck wrote:
> It is common for device drivers to make use of acquire/release semantics
> when dealing with descriptors stored in device memory. On reviewing the
> documentation and code for smp_load_acquire() and smp_store_release() as
> well as reviewing an IBM website that goes over the use of PowerPC barriers
> at http://www.ibm.com/developerworks/systems/articles/powerpc.html it
> occurred to me that the same code could likely be applied to device drivers.
>
> As a result this patch introduces load_acquire() and store_release(). The
> load_acquire() function can be used in the place of situations where a test
> for ownership must be followed by a memory barrier. The below example is
> from ixgbe:
>
> if (!rx_desc->wb.upper.status_error)
> break;
>
> /* This memory barrier is needed to keep us from reading
> * any other fields out of the rx_desc until we know the
> * descriptor has been written back
> */
> rmb();
>
> With load_acquire() this can be changed to:
>
> if (!load_acquire(&rx_desc->wb.upper.status_error))
> break;
I still don't think this is a good idea for the specific use-case you're
highlighting.
On ARM, an mb() can be *significantly* more expensive than an rmb() (since
we may have to drain store buffers on an outer L2 cache) and on arm64 it's
not at all clear that an LDAR is more efficient than an LDR; DMB LD
sequence. I can certainly imagine implementations where the latter would
be preferred.
So, whilst I'm perfectly fine to go along with mandatory acquire/release
macros (we should probably add a check to barf on __iomem pointers), I
don't agree with using them in preference to finer-grained read/write
barriers. Doing so will have a real impact on I/O performance.
Finally, do you know of any architectures where load_acquire/store_release
aren't implemented the same way as the smp_* variants on SMP kernels?
Will
^ permalink raw reply
* RE: [PATCH 1/3] arch: Introduce load_acquire() and store_release()
From: David Laight @ 2014-11-14 10:45 UTC (permalink / raw)
To: 'Alexander Duyck', linux-arch@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: mikey@neuling.org, tony.luck@intel.com,
mathieu.desnoyers@polymtl.ca, donald.c.skidmore@intel.com,
peterz@infradead.org, benh@kernel.crashing.org,
heiko.carstens@de.ibm.com, oleg@redhat.com, will.deacon@arm.com,
davem@davemloft.net, michael@ellerman.id.au,
matthew.vick@intel.com, nic_swsd@realtek.com,
geert@linux-m68k.org, jeffrey.t.kirsher@intel.com,
fweisbec@gmail.com, schwidefsky@de.ibm.com,
"linux@arm.linux.org.uk" <linux@
In-Reply-To: <20141113192723.12579.25343.stgit@ahduyck-server>
From: Alexander Duyck
> It is common for device drivers to make use of acquire/release semantics
> when dealing with descriptors stored in device memory. On reviewing the
> documentation and code for smp_load_acquire() and smp_store_release() as
> well as reviewing an IBM website that goes over the use of PowerPC barriers
> at http://www.ibm.com/developerworks/systems/articles/powerpc.html it
> occurred to me that the same code could likely be applied to device drivers.
>
> As a result this patch introduces load_acquire() and store_release(). The
> load_acquire() function can be used in the place of situations where a test
> for ownership must be followed by a memory barrier. The below example is
> from ixgbe:
>
> if (!rx_desc->wb.upper.status_error)
> break;
>
> /* This memory barrier is needed to keep us from reading
> * any other fields out of the rx_desc until we know the
> * descriptor has been written back
> */
> rmb();
>
> With load_acquire() this can be changed to:
>
> if (!load_acquire(&rx_desc->wb.upper.status_error))
> break;
If I'm quickly reading the 'new' code I need to look up yet another
function, with the 'old' code I can easily see the logic.
You've also added a memory barrier to the 'break' path - which isn't needed.
The driver might also have additional code that can be added before the barrier
so reducing the cost of the barrier.
The driver may also be able to perform multiple actions before a barrier is needed.
Hiding barriers isn't necessarily a good idea anyway.
If you are writing a driver you need to understand when and where they are needed.
Maybe you need a new (weaker) barrier to replace rmb() on some architectures.
...
David
^ permalink raw reply
* Re: Understanding what's going on when using a Huawei E173 USB 3G web-stick (UMTS/HSPA)
From: Sedat Dilek @ 2014-11-14 10:56 UTC (permalink / raw)
To: Dan Williams
Cc: Greg KH, David S. Miller,
netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-usb-u79uwXL29TY76Z2rM5mHXA, Aleksander Morgado
In-Reply-To: <CA+icZUW2iRkoAK10ayJ9PFuFGWBQzKdFe+8mfd=8oKkAp1txtg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Wed, Nov 12, 2014 at 2:21 PM, Sedat Dilek <sedat.dilek-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Tue, Nov 4, 2014 at 5:55 PM, Dan Williams <dcbw-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>> On Tue, 2014-11-04 at 16:11 +0100, Sedat Dilek wrote:
>>> Hi,
>>>
>>> I wanted to understand what is going on the kernel-side when
>>> connecting to the Internet via a Huawei E173 USB web-stick (3rd
>>> Generation: UMTS / HSPA).
>>>
>>> Especially the correlation between the diverse USB/NET kernel-drivers
>>> and how the networking is setup.
>>
>
> [ Sitting in front of a foreign Windows machine ]
>
> [ CC Aleksander ]
>
> Hi Dan,
>
> sorry for the late (and short) response.
>
> AFAICS you have given a "skeleton" for a "usb-wwan-networking"
> documentation :-).
>
> Personally, I would like to take into account some kernel-config
> options and some more things.
>
I started with documenting...
I have still some difficulties in understanding USB WWAN Networking.
So, this is what I revealed...
##### USB: HUAWEI E173 3G/UMTS/HSPA INTERNET STICK
### USB-NETWORKING AND WWAN SETUP
CONFIG_USB_USBNET=m <--- usb networking
CONFIG_USB_NET_CDCETHER=m <--- usb-wwan (net) configuration
CONFIG_USB_SERIAL_WWAN=m <--- usb-wwan (serial) configuration
CONFIG_USB_SERIAL_OPTION=m <--- usb-serial driver called "option"
### PPP OPTIONS
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=m
CONFIG_PPP_DEFLATE=m
CONFIG_PPP_FILTER=y
CONFIG_PPP_MULTILINK=y
CONFIG_PPP_ASYNC=m
Beyond the PPP options, I wanted to understand what
CONFIG_USB_NET_CDCETHER does and why I need it.
Can someone help?
Thanks.
- Sedat -
[1] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/net/usb/Kconfig#n189
P.S.: cdc_ether Kconfig option and checking my logs
>From [1]...
...
config USB_NET_CDCETHER
tristate "CDC Ethernet support (smart devices such as cable modems)"
depends on USB_USBNET
default y
help
This option supports devices conforming to the Communication Device
Class (CDC) Ethernet Control Model, a specification that's easy to
implement in device firmware. The CDC specifications are available
from <http://www.usb.org/>.
CDC Ethernet is an implementation option for DOCSIS cable modems
that support USB connectivity, used for non-Microsoft USB hosts.
The Linux-USB CDC Ethernet Gadget driver is an open implementation.
This driver should work with at least the following devices:
* Dell Wireless 5530 HSPA
* Ericsson PipeRider (all variants)
* Ericsson Mobile Broadband Module (all variants)
* Motorola (DM100 and SB4100)
* Broadcom Cable Modem (reference design)
* Toshiba (PCX1100U and F3507g/F3607gw)
* ...
This driver creates an interface named "ethX", where X depends on
what other networking devices you have in use. However, if the
IEEE 802 "local assignment" bit is set in the address, a "usbX"
name is used instead.
...
>From my logs...
$ dmesg | egrep -i 'option|wwan|ppp|3-1.2|huawei|gsm|modem'
[ 0.000000] please try 'cgroup_disable=memory' option if you don't
want memory cgroups
[ 0.549498] PPP generic driver version 2.4.2
[ 1.299059] usb 3-1.2: new high-speed USB device number 3 using ehci-pci
[ 1.394084] usb 3-1.2: New USB device found, idVendor=12d1, idProduct=1436
[ 1.394095] usb 3-1.2: New USB device strings: Mfr=4, Product=3,
SerialNumber=0
[ 1.394100] usb 3-1.2: Product: HUAWEI Mobile
[ 1.394103] usb 3-1.2: Manufacturer: HUAWEI Technology
[ 2.115424] usb-storage 3-1.2:1.0: USB Mass Storage device detected
[ 2.125026] usb-storage 3-1.2:1.1: USB Mass Storage device detected
[ 2.125607] usb-storage 3-1.2:1.2: USB Mass Storage device detected
[ 2.125888] usb-storage 3-1.2:1.3: USB Mass Storage device detected
[ 2.126187] usb-storage 3-1.2:1.4: USB Mass Storage device detected
[ 2.126461] usb-storage 3-1.2:1.5: USB Mass Storage device detected
[ 2.127098] scsi host11: usb-storage 3-1.2:1.5
[ 2.129370] usb-storage 3-1.2:1.6: USB Mass Storage device detected
[ 2.131685] scsi host12: usb-storage 3-1.2:1.6
[ 3.127317] scsi 11:0:0:0: CD-ROM HUAWEI Mass Storage
2.31 PQ: 0 ANSI: 2
[ 3.137589] scsi 12:0:0:0: Direct-Access HUAWEI SD Storage
2.31 PQ: 0 ANSI: 2
[ 13.500302] cdc_ether 3-1.2:1.1 wwan0: register 'cdc_ether' at
usb-0000:00:1a.0-1.2, Mobile Broadband Network Device,
02:50:f3:00:00:00
[ 14.160221] usbcore: registered new interface driver option
[ 14.160820] usbserial: USB Serial support registered for GSM modem (1-port)
[ 14.160940] option 3-1.2:1.0: GSM modem (1-port) converter detected
[ 14.163032] usb 3-1.2: GSM modem (1-port) converter now attached to ttyUSB0
[ 14.163305] option 3-1.2:1.3: GSM modem (1-port) converter detected
[ 14.163676] usb 3-1.2: GSM modem (1-port) converter now attached to ttyUSB1
[ 14.163742] option 3-1.2:1.4: GSM modem (1-port) converter detected
[ 14.165227] usb 3-1.2: GSM modem (1-port) converter now attached to ttyUSB2
[ 72.877065] PPP BSD Compression module registered
[ 72.881701] PPP Deflate Compression module registered
- EOT -
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox