Netdev List
 help / color / mirror / Atom feed
* Re: tg3 'No PHY devices' loading issue
From: Josh Boyer @ 2012-04-17 19:40 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: Matt Carlson, Michael Chan, netdev, linux-kernel
In-Reply-To: <20120417191042.GB3932@decadent.org.uk>

On Tue, Apr 17, 2012 at 08:10:42PM +0100, Ben Hutchings wrote:
> On Tue, Apr 17, 2012 at 11:50:07AM -0700, Matt Carlson wrote:
> > On Tue, Apr 17, 2012 at 10:18:57AM -0400, Josh Boyer wrote:
> > > Hi Matt and Michael,
> > > 
> > > I'm seeing an odd issue with the tg3 driver on one of my development
> > > machines.  I've tried kernels 3.2.10, 3.3.0, 3.3.1, 3.3.2 and 3.4-rc3
> > > and they all seem to exhibit this issue now.  When the machine boots
> > > and the tg3 driver is loaded, it fails to find a PHY and then reports
> > > 'Problem fetching invariants of chip'.  If I do a rmmod/modprobe of
> > > tg3 after login, the probe seems to work fine and ethernet works as
> > > expected.  You can see this in the dmesg below:
> [...]
> > The 57788 uses the broadcom phylib module.  For some reason, it isn't
> > available the first module load attempt.  A while ago, code was added to
> > phylib to request modules from userspace if the particular phy support
> > code wasn't already loaded.  It looks like this mechanism isn't working
> > too well the first time through.
> [...]
> 
> At a guess, tg3 is in the initramfs but broadcom isn't.

Good guess.  At least on one of the initramfs' I checked.  I'll verify
the others, but it seems likely they match.  As I just said in my other
reply, I would have expected tg3 to have a module dep on it to get it
included in the initramfs built, but it seems that isn't the case.

josh

^ permalink raw reply

* Re: tg3 'No PHY devices' loading issue
From: Josh Boyer @ 2012-04-17 19:39 UTC (permalink / raw)
  To: Matt Carlson; +Cc: Michael Chan, netdev, linux-kernel
In-Reply-To: <20120417185007.GA3742@mcarlson.broadcom.com>

On Tue, Apr 17, 2012 at 11:50:07AM -0700, Matt Carlson wrote:
> On Tue, Apr 17, 2012 at 10:18:57AM -0400, Josh Boyer wrote:
> > It has worked on some of the older kernels without the need for the
> > manual rmmod/modprobe step, so it seems to be somewhat timing related.
> > I'm not sure if there is a module load ordering issue, but that doesn't
> > seem to be the case.  I can't explain why a later modprobe would work
> > just fine though.
> > 
> > Do you have any thoughts on how to go about debugging/fixing this?  I'd
> > be happy to test and provide whatever information you need.
> 
> The 57788 uses the broadcom phylib module.  For some reason, it isn't
> available the first module load attempt.  A while ago, code was added to
> phylib to request modules from userspace if the particular phy support
> code wasn't already loaded.  It looks like this mechanism isn't working
> too well the first time through.
> 
> Can you reproduce the problem if you run 'rmmod broadcom' and then
> reload tg3?

I'll give it a shot, but that sounds like a quite plausible scenario to
me.  Oddly, even after the manual modprobe, the broadcom phy module
doesn't show an increased use count.  There's also no dependency on
'broadcom' listed for tg3 by modinfo.

josh

^ permalink raw reply

* [PATCH] net: qmi_wwan: support Sierra Wireless MC77xx devices in QMI mode
From: Bjørn Mork @ 2012-04-17 19:38 UTC (permalink / raw)
  To: netdev; +Cc: Bjørn Mork

The MC77xx devices can operate in two modes: "Direct IP" or "QMI",
switchable using a password protected AT command.  Both product ID
and USB interface configuration will change when switched.

The "sierra_net" driver supports the "Direct IP" mode.  This driver
supports the "QMI" mode.

There are also multiple possible USB interface configurations in each
mode, some providing more than one wwan interface.  Like many other
devices made for Windows, different interface types are identified
using a static interface number.  We define a Sierra specific
interface whitelist to support this.

Signed-off-by: Bjørn Mork <bjorn@mork.no>
---
 drivers/net/usb/qmi_wwan.c |   30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 0fcc075..8ffc733 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -486,6 +486,27 @@ static const struct driver_info	qmi_wwan_force_int4 = {
 	.data		= BIT(4), /* interface whitelist bitmap */
 };
 
+/* Sierra Wireless provide equally useless interface descriptors
+ * Devices in QMI mode can be switched between two different
+ * configurations:
+ *   a) USB interface #8 is QMI/wwan
+ *   b) USB interfaces #8, #19 and #20 are QMI/wwan
+ *
+ * Both configurations provide a number of other interfaces (serial++),
+ * some of which have the same endpoint configuration as we expect, so
+ * a whitelist or blacklist is necessary.
+ *
+ * FIXME: The below whitelist should include BIT(20).  It does not
+ * because I cannot get it to work...
+ */
+static const struct driver_info	qmi_wwan_sierra = {
+	.description	= "Sierra Wireless wwan/QMI device",
+	.flags		= FLAG_WWAN,
+	.bind		= qmi_wwan_bind_gobi,
+	.unbind		= qmi_wwan_unbind_shared,
+	.manage_power	= qmi_wwan_manage_power,
+	.data		= BIT(8) | BIT(19), /* interface whitelist bitmap */
+};
 
 #define HUAWEI_VENDOR_ID	0x12D1
 #define QMI_GOBI_DEVICE(vend, prod) \
@@ -566,6 +587,15 @@ static const struct usb_device_id products[] = {
 		.bInterfaceProtocol = 0xff,
 		.driver_info        = (unsigned long)&qmi_wwan_force_int4,
 	},
+	{	/* Sierra Wireless MC77xx in QMI mode */
+		.match_flags	    = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor           = 0x1199,
+		.idProduct          = 0x68a2,
+		.bInterfaceClass    = 0xff,
+		.bInterfaceSubClass = 0xff,
+		.bInterfaceProtocol = 0xff,
+		.driver_info        = (unsigned long)&qmi_wwan_sierra,
+	},
 	{QMI_GOBI_DEVICE(0x05c6, 0x9212)},	/* Acer Gobi Modem Device */
 	{QMI_GOBI_DEVICE(0x03f0, 0x1f1d)},	/* HP un2400 Gobi Modem Device */
 	{QMI_GOBI_DEVICE(0x03f0, 0x371d)},	/* HP un2430 Mobile Broadband Module */
-- 
1.7.9.5

^ permalink raw reply related

* Re: tg3 'No PHY devices' loading issue
From: Ben Hutchings @ 2012-04-17 19:10 UTC (permalink / raw)
  To: Matt Carlson; +Cc: Josh Boyer, Michael Chan, netdev, linux-kernel
In-Reply-To: <20120417185007.GA3742@mcarlson.broadcom.com>

On Tue, Apr 17, 2012 at 11:50:07AM -0700, Matt Carlson wrote:
> On Tue, Apr 17, 2012 at 10:18:57AM -0400, Josh Boyer wrote:
> > Hi Matt and Michael,
> > 
> > I'm seeing an odd issue with the tg3 driver on one of my development
> > machines.  I've tried kernels 3.2.10, 3.3.0, 3.3.1, 3.3.2 and 3.4-rc3
> > and they all seem to exhibit this issue now.  When the machine boots
> > and the tg3 driver is loaded, it fails to find a PHY and then reports
> > 'Problem fetching invariants of chip'.  If I do a rmmod/modprobe of
> > tg3 after login, the probe seems to work fine and ethernet works as
> > expected.  You can see this in the dmesg below:
[...]
> The 57788 uses the broadcom phylib module.  For some reason, it isn't
> available the first module load attempt.  A while ago, code was added to
> phylib to request modules from userspace if the particular phy support
> code wasn't already loaded.  It looks like this mechanism isn't working
> too well the first time through.
[...]

At a guess, tg3 is in the initramfs but broadcom isn't.

Ben.

-- 
Ben Hutchings
We get into the habit of living before acquiring the habit of thinking.
                                                              - Albert Camus

^ permalink raw reply

* Re: In tree firmware not kept up to date
From: Ben Hutchings @ 2012-04-17 19:03 UTC (permalink / raw)
  To: Ian Campbell
  Cc: netdev, linux-kernel, Ian Jackson, Michael Chan, David Miller
In-Reply-To: <1334675504.23948.99.camel@zakaz.uk.xensource.com>

On Tue, Apr 17, 2012 at 04:11:44PM +0100, Ian Campbell wrote:
> Hello,
> 
> What is the current policy wrt keeping the in-tree firmware updated
> alongside the drivers?

Muddled.

> I use CONFIG_FIRMWARE_IN_KERNEL but a couple of times[0] recently this
> has failed me because the in-tree bnx2 driver requires
> bnx2/bnx2-mips-09-6.2.1b.fw but the tree contains
> firmware/bnx2/bnx2-mips-09-6.2.1a.fw.ihex and not 6.2.1b.
> 
> Is it a bug that bnx2 was updated without adding the new firmware or is
> a bug that the obsolete firmware is still sitting in the tree?

It's a bug that any firmware is still in-tree, but
CONFIG_FIRMWARE_IN_KERNEL hasn't yet been made to work with a separate
linux-firmware directory.

Ben.

> Ian.
> 
> [0] It's possible the same thing and I've just tripped over it more than
> once now.

-- 
Ben Hutchings
We get into the habit of living before acquiring the habit of thinking.
                                                              - Albert Camus

^ permalink raw reply

* Re: [PATCH] net: filter: Just In Time compiler for sparc
From: Richard Mortimer @ 2012-04-17 18:59 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, sparclinux
In-Reply-To: <20120416.225823.1395194623649559124.davem@davemloft.net>

A few points below. Mainly a couple of comments that do not match the 
code itself. I didn't spot any errors in the assembler generation itself.

Richard

On 17/04/2012 03:58, David Miller wrote:
>
> Signed-off-by: David S. Miller<davem@davemloft.net>
> ---
>
> I think I'll commit this to net-next so it's easier to track
> any BPF changes we make there.
>
>   arch/sparc/Kconfig            |    1 +
>   arch/sparc/Makefile           |    1 +
>   arch/sparc/net/Makefile       |    4 +
>   arch/sparc/net/bpf_jit.h      |   52 +++
>   arch/sparc/net/bpf_jit_asm.S  |  199 +++++++++++
>   arch/sparc/net/bpf_jit_comp.c |  785 +++++++++++++++++++++++++++++++++++++++++
>   6 files changed, 1042 insertions(+)
>   create mode 100644 arch/sparc/net/Makefile
>   create mode 100644 arch/sparc/net/bpf_jit.h
>   create mode 100644 arch/sparc/net/bpf_jit_asm.S
>   create mode 100644 arch/sparc/net/bpf_jit_comp.c
>
> diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
> index db4e821..9f9afd9 100644
> --- a/arch/sparc/Kconfig
> +++ b/arch/sparc/Kconfig
> @@ -30,6 +30,7 @@ config SPARC
>   	select USE_GENERIC_SMP_HELPERS if SMP
>   	select GENERIC_PCI_IOMAP
>   	select HAVE_NMI_WATCHDOG if SPARC64
> +	select HAVE_BPF_JIT
>
>   config SPARC32
>   	def_bool !64BIT
> diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
> index eddcfb3..0e5de13 100644
> --- a/arch/sparc/Makefile
> +++ b/arch/sparc/Makefile
> @@ -66,6 +66,7 @@ head-y                 += arch/sparc/kernel/init_task.o
>
>   core-y                 += arch/sparc/kernel/
>   core-y                 += arch/sparc/mm/ arch/sparc/math-emu/
> +core-y                 += arch/sparc/net/
>
>   libs-y                 += arch/sparc/prom/
>   libs-y                 += arch/sparc/lib/
> diff --git a/arch/sparc/net/Makefile b/arch/sparc/net/Makefile
> new file mode 100644
> index 0000000..1306a58
> --- /dev/null
> +++ b/arch/sparc/net/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# Arch-specific network modules
> +#
> +obj-$(CONFIG_BPF_JIT) += bpf_jit_asm.o bpf_jit_comp.o
> diff --git a/arch/sparc/net/bpf_jit.h b/arch/sparc/net/bpf_jit.h
> new file mode 100644
> index 0000000..74f048b
> --- /dev/null
> +++ b/arch/sparc/net/bpf_jit.h
> @@ -0,0 +1,52 @@
> +#ifndef _BPF_JIT_H
> +#define _BPF_JIT_H
> +
> +/* Conventions:
> + *  %g1 : temporary
> + *  %g2 : Secondary temporary used by SKB data helper stubs.
> + *  %o0 : pointer to skb (first argument given to JIT function)
> + *  %o1 : BPF A accumulator
> + *  %o2 : BPF X accumulator
> + *  %o3 : Holds saved %o7 so we can call helper functions without needing
> + *        to allocate a register window.
> + *  %o4 : skb->data
> + *  %o5 : skb->len - skb->data_len

%o4 and %o5 are inconsistent with r_HEADLEN and r_SKBDATA below

%g3 defined as r_OFF below but not listed in conventions.

> + */
> +
> +#ifndef __ASSEMBLER__
> +#define G0		0x00
> +#define G1		0x01
> +#define G3		0x03
> +#define G6		0x06
> +#define O0		0x08
> +#define O1		0x09
> +#define O2		0x0a
> +#define O3		0x0b
> +#define O4		0x0c
> +#define O5		0x0d
> +#define SP		0x0e
> +#define O7		0x0f
> +#define FP		0x1e
> +
> +#define r_SKB		O0
> +#define r_A		O1
> +#define r_X		O2
> +#define r_saved_O7	O3
> +#define r_HEADLEN	O4
> +#define r_SKB_DATA	O5
> +#define r_TMP		G1
> +#define r_TMP2		G2
> +#define r_OFF		G3
> +#else
> +#define r_SKB		%o0
> +#define r_A		%o1
> +#define r_X		%o2
> +#define r_saved_O7	%o3
> +#define r_HEADLEN	%o4
> +#define r_SKB_DATA	%o5
> +#define r_TMP		%g1
> +#define r_TMP2		%g2
> +#define r_OFF		%g3
> +#endif
> +
> +#endif /* _BPF_JIT_H */
> diff --git a/arch/sparc/net/bpf_jit_asm.S b/arch/sparc/net/bpf_jit_asm.S
> new file mode 100644
> index 0000000..fdc6932
> --- /dev/null
> +++ b/arch/sparc/net/bpf_jit_asm.S
> @@ -0,0 +1,199 @@
> +#include<asm/ptrace.h>
> +
> +#include "bpf_jit.h"
> +
> +#ifdef CONFIG_SPARC64
> +#define SAVE_SZ		176
> +#define SCRATCH_OFF	STACK_BIAS + 128
> +#define BE_PTR(label)	be,pn %xcc, label
> +#else
> +#define SAVE_SZ		96
> +#define SCRATCH_OFF	72
> +#define BE_PTR(label)	be label
> +#endif
> +
> +#define SKF_MAX_NEG_OFF	(-0x200000) /* SKF_LL_OFF from filter.h */
> +
> +	.text
> +	.globl	bpf_jit_load_word
> +bpf_jit_load_word:
> +	cmp	r_OFF, 0
> +	bl	bpf_slow_path_word_neg
> +	 nop
> +	.globl	bpf_jit_load_word_positive_offset
> +bpf_jit_load_word_positive_offset:
> +	sub	r_HEADLEN, r_OFF, r_TMP
> +	cmp	r_TMP, 3
> +	ble	bpf_slow_path_word
> +	 add	r_SKB_DATA, r_OFF, r_TMP
> +	andcc	r_TMP, 3, %g0
> +	bne	load_word_unaligned
> +	 nop
> +	retl
> +	 ld	[r_SKB_DATA + r_OFF], r_A

This could be

ld [r_TMP], r_A

to be consistent with the use of r_TMP below.
But otherwise correct.

> +load_word_unaligned:
> +	ldub	[r_TMP + 0x0], r_OFF
> +	ldub	[r_TMP + 0x1], r_TMP2
> +	sll	r_OFF, 8, r_OFF
> +	or	r_OFF, r_TMP2, r_OFF
> +	ldub	[r_TMP + 0x2], r_TMP2
> +	sll	r_OFF, 8, r_OFF
> +	or	r_OFF, r_TMP2, r_OFF
> +	ldub	[r_TMP + 0x3], r_TMP2
> +	sll	r_OFF, 8, r_OFF
> +	retl
> +	 or	r_OFF, r_TMP2, r_A
> +
> +	.globl	bpf_jit_load_half
> +bpf_jit_load_half:
> +	cmp	r_OFF, 0
> +	bl	bpf_slow_path_half_neg
> +	 nop
> +	.globl	bpf_jit_load_half_positive_offset
> +bpf_jit_load_half_positive_offset:
> +	sub	r_HEADLEN, r_OFF, r_TMP
> +	cmp	r_TMP, 1
> +	ble	bpf_slow_path_half
> +	 add	r_SKB_DATA, r_OFF, r_TMP
> +	andcc	r_TMP, 1, %g0
> +	bne	load_half_unaligned
> +	 nop
> +	retl
> +	 lduh	[r_SKB_DATA + r_OFF], r_A

ditto for use of r_TMP

> +load_half_unaligned:
> +	ldub	[r_TMP + 0x0], r_OFF
> +	ldub	[r_TMP + 0x1], r_TMP2
> +	sll	r_OFF, 8, r_OFF
> +	retl
> +	 or	r_OFF, r_TMP2, r_A
> +
> +	.globl	bpf_jit_load_byte
> +bpf_jit_load_byte:
> +	cmp	r_OFF, 0
> +	bl	bpf_slow_path_byte_neg
> +	 nop
> +	.globl	bpf_jit_load_byte_positive_offset
> +bpf_jit_load_byte_positive_offset:
> +	cmp	r_OFF, r_HEADLEN
> +	bge	bpf_slow_path_byte
> +	 nop
> +	retl
> +	 ldub	[r_SKB_DATA + r_OFF], r_A
> +
> +	.globl	bpf_jit_load_byte_msh
> +bpf_jit_load_byte_msh:
> +	cmp	r_OFF, 0
> +	bl	bpf_slow_path_byte_msh_neg
> +	 nop
> +	.globl	bpf_jit_load_byte_msh_positive_offset
> +bpf_jit_load_byte_msh_positive_offset:
> +	cmp	r_OFF, r_HEADLEN
> +	bge	bpf_slow_path_byte_msh
> +	 nop
> +	ldub	[r_SKB_DATA + r_OFF], r_OFF
> +	and	r_OFF, 0xf, r_OFF
> +	retl
> +	 sll	r_OFF, 2, r_X
> +
> +#define bpf_slow_path_common(LEN)	\
> +	save	%sp, -SAVE_SZ, %sp;	\
> +	mov	%i0, %o0;		\
> +	mov	r_OFF, %o1;		\
> +	add	%fp, SCRATCH_OFF, %o2;	\
> +	call	skb_copy_bits;		\
> +	 mov	(LEN), %o3;		\
> +	cmp	%o0, 0;			\
> +	restore;
> +
> +bpf_slow_path_word:
> +	bpf_slow_path_common(4)
> +	bl	bpf_error
> +	 ld	[%sp + SCRATCH_OFF], r_A
> +	retl
> +	 nop
> +bpf_slow_path_half:
> +	bpf_slow_path_common(2)
> +	bl	bpf_error
> +	 lduh	[%sp + SCRATCH_OFF], r_A
> +	retl
> +	 nop
> +bpf_slow_path_byte:
> +	bpf_slow_path_common(1)
> +	bl	bpf_error
> +	 ldub	[%sp + SCRATCH_OFF], r_A
> +	retl
> +	 nop
> +bpf_slow_path_byte_msh:
> +	bpf_slow_path_common(1)
> +	bl	bpf_error
> +	 ldub	[%sp + SCRATCH_OFF], r_A
> +	and	r_OFF, 0xf, r_OFF
> +	retl
> +	 sll	r_OFF, 2, r_X
> +
> +#define bpf_negative_common(LEN)			\
> +	save	%sp, -SAVE_SZ, %sp;			\
> +	mov	%i0, %o0;				\
> +	mov	r_OFF, %o1;				\
> +	call	bpf_internal_load_pointer_neg_helper;	\
> +	 mov	(LEN), %o2;				\
> +	mov	%o0, r_TMP;				\
> +	cmp	%o0, 0;					\
> +	BE_PTR(bpf_error);				\
> +	 restore;
> +
> +bpf_slow_path_word_neg:
> +	sethi	%hi(SKF_MAX_NEG_OFF), r_TMP
> +	cmp	r_OFF, r_TMP
> +	bl	bpf_error
> +	 nop
> +	.globl	bpf_jit_load_word_negative_offset
> +bpf_jit_load_word_negative_offset:
> +	bpf_negative_common(4)
> +	andcc	r_TMP, 3, %g0
> +	bne	load_word_unaligned
> +	 nop
> +	retl
> +	 ld	[r_TMP], r_A
> +
> +bpf_slow_path_half_neg:
> +	sethi	%hi(SKF_MAX_NEG_OFF), r_TMP
> +	cmp	r_OFF, r_TMP
> +	bl	bpf_error
> +	 nop
> +	.globl	bpf_jit_load_half_negative_offset
> +bpf_jit_load_half_negative_offset:
> +	bpf_negative_common(2)
> +	andcc	r_TMP, 1, %g0
> +	bne	load_half_unaligned
> +	 nop
> +	retl
> +	 lduh	[r_TMP], r_A
> +
> +bpf_slow_path_byte_neg:
> +	sethi	%hi(SKF_MAX_NEG_OFF), r_TMP
> +	cmp	r_OFF, r_TMP
> +	bl	bpf_error
> +	 nop
> +	.globl	bpf_jit_load_byte_negative_offset
> +bpf_jit_load_byte_negative_offset:
> +	bpf_negative_common(1)
> +	retl
> +	 ldub	[r_TMP], r_A
> +
> +bpf_slow_path_byte_msh_neg:
> +	sethi	%hi(SKF_MAX_NEG_OFF), r_TMP
> +	cmp	r_OFF, r_TMP
> +	bl	bpf_error
> +	 nop
> +	.globl	bpf_jit_load_byte_msh_negative_offset
> +bpf_jit_load_byte_msh_negative_offset:
> +	bpf_negative_common(1)
> +	ldub	[r_TMP], r_OFF
> +	and	r_OFF, 0xf, r_OFF
> +	retl
> +	 sll	r_OFF, 2, r_X
> +
> +bpf_error:
> +	jmpl	r_saved_O7 + 8, %g0
> +	 clr	%o0
> diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c
> new file mode 100644
> index 0000000..86349ca
> --- /dev/null
> +++ b/arch/sparc/net/bpf_jit_comp.c
> @@ -0,0 +1,785 @@
> +#include<linux/moduleloader.h>
> +#include<linux/workqueue.h>
> +#include<linux/netdevice.h>
> +#include<linux/filter.h>
> +#include<linux/cache.h>
> +
> +#include<asm/cacheflush.h>
> +#include<asm/ptrace.h>
> +
> +#include "bpf_jit.h"
> +
> +int bpf_jit_enable __read_mostly;
> +
> +/* assembly code in arch/sparc/net/bpf_jit_asm.S */
> +extern u32 bpf_jit_load_word[];
> +extern u32 bpf_jit_load_half[];
> +extern u32 bpf_jit_load_byte[];
> +extern u32 bpf_jit_load_byte_msh[];
> +extern u32 bpf_jit_load_word_positive_offset[];
> +extern u32 bpf_jit_load_half_positive_offset[];
> +extern u32 bpf_jit_load_byte_positive_offset[];
> +extern u32 bpf_jit_load_byte_msh_positive_offset[];
> +extern u32 bpf_jit_load_word_negative_offset[];
> +extern u32 bpf_jit_load_half_negative_offset[];
> +extern u32 bpf_jit_load_byte_negative_offset[];
> +extern u32 bpf_jit_load_byte_msh_negative_offset[];
> +
> +static inline bool is_simm13(unsigned int value)
> +{
> +	return value + 0x1000<  0x2000;
> +}
> +
> +static void bpf_flush_icache(void *start_, void *end_)
> +{
> +#ifdef CONFIG_SPARC64
> +	/* Cheetah's I-cache is fully coherent.  */
> +	if (tlb_type == spitfire) {
> +		unsigned long start = (unsigned long) start_;
> +		unsigned long end = (unsigned long) end_;
> +
> +		start&= ~7UL;
> +		end = (end + 7UL)&  ~7UL;
> +		while (start<  end) {
> +			flushi(start);
> +			start += 32;
> +		}
> +	}
> +#endif
> +}
> +
> +#define SEEN_DATAREF 1 /* might call external helpers */
> +#define SEEN_XREG    2 /* ebx is used */
> +#define SEEN_MEM     4 /* use mem[] for temporary storage */
> +
> +#define S13(X)		((X)&  0x1fff)
> +#define IMMED		0x00002000
> +#define RD(X)		((X)<<  25)
> +#define RS1(X)		((X)<<  14)
> +#define RS2(X)		((X))
> +#define OP(X)		((X)<<  30)
> +#define OP2(X)		((X)<<  22)
> +#define OP3(X)		((X)<<  19)
> +#define COND(X)		((X)<<  25)
> +#define F1(X)		OP(X)
> +#define F2(X, Y)	(OP(X) | OP2(Y))
> +#define F3(X, Y)	(OP(X) | OP3(Y))
> +
> +#define CONDN		COND (0x0)
> +#define CONDE		COND (0x1)
> +#define CONDLE		COND (0x2)
> +#define CONDL		COND (0x3)
> +#define CONDLEU		COND (0x4)
> +#define CONDCS		COND (0x5)
> +#define CONDNEG		COND (0x6)
> +#define CONDVC		COND (0x7)
> +#define CONDA		COND (0x8)
> +#define CONDNE		COND (0x9)
> +#define CONDG		COND (0xa)
> +#define CONDGE		COND (0xb)
> +#define CONDGU		COND (0xc)
> +#define CONDCC		COND (0xd)
> +#define CONDPOS		COND (0xe)
> +#define CONDVS		COND (0xf)
> +
> +#define CONDGEU		CONDCC
> +#define CONDLU		CONDCS
> +
> +#define WDISP22(X)	(((X)>>  2)&  0x3fffff)
> +
> +#define BA		(F2(0, 2) | CONDA)
> +#define BGU		(F2(0, 2) | CONDGU)
> +#define BLEU		(F2(0, 2) | CONDLEU)
> +#define BGEU		(F2(0, 2) | CONDGEU)
> +#define BLU		(F2(0, 2) | CONDLU)
> +#define BE		(F2(0, 2) | CONDE)
> +#define BNE		(F2(0, 2) | CONDNE)
> +
> +#ifdef CONFIG_SPARC64
> +#define BNE_PTR		(F2(0, 1) | CONDNE | (2<<  20))
> +#else
> +#define BNE_PTR		BNE
> +#endif
> +
> +#define SETHI(K, REG)	\
> +	(F2(0, 0x4) | RD(REG) | (((K)>>  10)&  0x3fffff))
> +#define OR_LO(K, REG)	\
> +	(F3(2, 0x02) | IMMED | RS1(REG) | ((K)&  0x3ff) | RD(REG))
> +
> +#define ADD		F3(2, 0x00)
> +#define AND		F3(2, 0x01)
> +#define ANDCC		F3(2, 0x11)
> +#define OR		F3(2, 0x02)
> +#define SUB		F3(2, 0x04)
> +#define SUBCC		F3(2, 0x14)
> +#define MUL		F3(2, 0x0a)	/* umul */
> +#define DIV		F3(2, 0x0e)	/* udiv */
> +#define SLL		F3(2, 0x25)
> +#define SRL		F3(2, 0x26)
> +#define JMPL		F3(2, 0x38)
> +#define CALL		F1(1)
> +#define BR		F2(0, 0x01)
> +#define RD_Y		F3(2, 0x28)
> +#define WR_Y		F3(2, 0x30)
> +
> +#define LD32		F3(3, 0x00)
> +#define LD8		F3(3, 0x01)
> +#define LD16		F3(3, 0x02)
> +#define LD64		F3(3, 0x0b)
> +#define ST32		F3(3, 0x04)
> +
> +#ifdef CONFIG_SPARC64
> +#define LDPTR		LD64
> +#define BASE_STACKFRAME	176
> +#else
> +#define LDPTR		LD32
> +#define BASE_STACKFRAME	96
> +#endif
> +
> +#define LD32I		(LD32 | IMMED)
> +#define LD8I		(LD8 | IMMED)
> +#define LD16I		(LD16 | IMMED)
> +#define LD64I		(LD64 | IMMED)
> +#define LDPTRI		(LDPTR | IMMED)
> +#define ST32I		(ST32 | IMMED)
> +
> +#define emit_nop()		\
> +do {				\
> +	*prog++ = SETHI(0, G0);	\
> +} while (0)
> +
> +#define emit_neg()					\
> +do {	/* sub %g0, r_A, r_A */				\
> +	*prog++ = SUB | RS1(G0) | RS2(r_A) | RD(r_A);	\
> +} while (0)
> +
> +#define emit_reg_move(FROM, TO)				\
> +do {	/* or %g0, FROM, TO */				\
> +	*prog++ = OR | RS1(G0) | RS2(FROM) | RD(TO);	\
> +} while (0)
> +
> +#define emit_clear(REG)					\
> +do {	/* or %g0, %g0, REG */				\
> +	*prog++ = OR | RS1(G0) | RS2(G0) | RD(REG);	\
> +} while (0)
> +
> +#define emit_set_const(K, REG)					\
> +do {	/* sethi %hi(K), REG */					\
> +	*prog++ = SETHI(K, REG);				\
> +	/* or REG, %lo(K), REG */				\
> +	*prog++ = OR_LO(K, REG);				\
> +} while (0)
> +
> +	/* Emit
> +	 *
> +	 * 	OP	r_A, r_X, r_A
> +	 */
> +#define emit_alu_X(OPCODE)					\
> +do {								\
> +	seen |= SEEN_XREG;					\
> +	*prog++ = OPCODE | RS1(r_A) | RS2(r_X) | RD(r_A);	\
> +} while (0)
> +
> +	/* Emit either:
> +	 *
> +	 *	OP	r_A, K, r_A
> +	 *
> +	 * or
> +	 *
> +	 *	sethi	%hi(K), r_TMP
> +	 *	or	r_TMP, %lo(K), r_TMP
> +	 *	OP	r_A, r_TMP, r_A
> +	 *
> +	 * depending upon whether K fits in a signed 13-bit
> +	 * immediate instruction field.  Emit nothing if K
> +	 * is zero.
> +	 */
> +#define emit_alu_K(OPCODE, K)					\
> +do {			   					\
> +	if (K) {						\
> +		unsigned int _insn = OPCODE;			\
> +		_insn |= RS1(r_A) | RD(r_A);			\
> +		if (is_simm13(K)) {				\
> +			*prog++ = _insn | IMMED | S13(K);	\
> +		} else {					\
> +			emit_set_const(K, r_TMP);		\
> +			*prog++ = _insn | RS2(r_TMP);		\
> +		}		  	  			\
> +	}							\
> +} while (0)
> +
> +#define emit_loadimm(K, DEST)						\
> +do {									\
> +	if (is_simm13(K)) {						\
> +		/* or %g0, K, DEST */					\
> +		*prog++ = OR | IMMED | RS1(G0) | S13(K) | RD(DEST);	\
> +	} else {							\
> +		emit_set_const(K, DEST);				\
> +	}								\
> +} while (0)
> +
> +#define emit_loadptr(BASE, STRUCT, FIELD, DEST)				\
> +do {	unsigned int _off = offsetof(STRUCT, FIELD);			\
> +	BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(void *));	\
> +	*prog++ = LDPTRI | RS1(BASE) | S13(_off) | RD(DEST);		\
> +} while(0)
> +
> +#define emit_load32(BASE, STRUCT, FIELD, DEST)				\
> +do {	unsigned int _off = offsetof(STRUCT, FIELD);			\
> +	BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u32));	\
> +	*prog++ = LD32I | RS1(BASE) | S13(_off) | RD(DEST);		\
> +} while(0)
> +
> +#define emit_load16(BASE, STRUCT, FIELD, DEST)				\
> +do {	unsigned int _off = offsetof(STRUCT, FIELD);			\
> +	BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u16));	\
> +	*prog++ = LD16I | RS1(BASE) | S13(_off) | RD(DEST);		\
> +} while(0)
> +
> +#define __emit_load8(BASE, STRUCT, FIELD, DEST)				\
> +do {	unsigned int _off = offsetof(STRUCT, FIELD);			\
> +	*prog++ = LD8I | RS1(BASE) | S13(_off) | RD(DEST);		\
> +} while(0)
> +
> +#define emit_load8(BASE, STRUCT, FIELD, DEST)				\
> +do {	BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u8));	\
> +	__emit_load8(BASE, STRUCT, FIELD, DEST);			\
> +} while(0)
> +
> +#define emit_ldmem(OFF, DEST)					\
> +do {	*prog++ = LD32I | RS1(FP) | S13(-(OFF)) | RD(DEST);	\
> +} while(0)
> +
> +#define emit_stmem(OFF, SRC)					\
> +do {	*prog++ = LD32I | RS1(FP) | S13(-(OFF)) | RD(SRC);	\
> +} while(0)
> +
> +#define cpu_off		offsetof(struct thread_info, cpu)
> +
> +#ifdef CONFIG_SMP
> +#ifdef CONFIG_SPARC64
> +#define emit_load_cpu(REG)						\
> +	emit_load16(G6, struct thread_info, cpu, REG)
> +#else
> +#define emit_load_cpu(REG)						\
> +	emit_load32(G6, struct thread_info, cpu, REG)
> +#endif
> +#else
> +#define emit_load_cpu(REG)	emit_clear(REG)
> +#endif
> +
> +#define emit_skb_loadptr(FIELD, DEST) \
> +	emit_loadptr(r_SKB, struct sk_buff, FIELD, DEST)
> +#define emit_skb_load32(FIELD, DEST) \
> +	emit_load32(r_SKB, struct sk_buff, FIELD, DEST)
> +#define emit_skb_load16(FIELD, DEST) \
> +	emit_load16(r_SKB, struct sk_buff, FIELD, DEST)
> +#define __emit_skb_load8(FIELD, DEST) \
> +	__emit_load8(r_SKB, struct sk_buff, FIELD, DEST)
> +#define emit_skb_load8(FIELD, DEST) \
> +	emit_load8(r_SKB, struct sk_buff, FIELD, DEST)
> +
> +#define emit_jmpl(BASE, IMM_OFF, LREG) \
> +	*prog++ = (JMPL | IMMED | RS1(BASE) | S13(IMM_OFF) | RD(LREG))
> +
> +#define emit_call(FUNC)					\
> +do {	void *_here = image + addrs[i] - 8;		\
> +	unsigned int _off = (void *)(FUNC) - _here;	\
> +	*prog++ = CALL | (((_off)>>  2)&  0x3fffffff);	\
> +	emit_nop();					\
> +} while (0)
> +
> +#define emit_branch(BR_OPC, DEST)			\
> +do {	unsigned int _here = addrs[i] - 8;		\
> +	*prog++ = BR_OPC | WDISP22((DEST) - _here);	\
> +} while(0)
> +
> +#define emit_branch_off(BR_OPC, OFF)			\
> +do {	*prog++ = BR_OPC | WDISP22(OFF);		\
> +} while(0)
> +
> +#define emit_jump(DEST)		emit_branch(BA, DEST)
> +
> +#define emit_read_y(REG) 	*prog++ = RD_Y | RD(REG);
> +#define emit_write_y(REG) 	*prog++ = WR_Y | IMMED | RS1(REG) | S13(0);
> +
> +#define emit_cmp(R1, R2) \
> +	*prog++ = (SUBCC | RS1(R1) | RS2(R2) | RD(G0))
> +
> +#define emit_cmpi(R1, IMM) \
> +	*prog++ = (SUBCC | IMMED | RS1(R1) | S13(IMM) | RD(G0));
> +
> +#define emit_btst(R1, R2) \
> +	*prog++ = (ANDCC | RS1(R1) | RS2(R2) | RD(G0))
> +
> +#define emit_btsti(R1, IMM) \
> +	*prog++ = (ANDCC | IMMED | RS1(R1) | S13(IMM) | RD(G0));
> +
> +#define emit_sub(R1, R2, R3) \
> +	*prog++ = (SUB | RS1(R1) | RS2(R2) | RD(R3))
> +
> +#define emit_subi(R1, IMM, R3) \
> +	*prog++ = (SUB | IMMED | RS1(R1) | S13(IMM) | RD(R3))
> +
> +#define emit_add(R1, R2, R3) \
> +	*prog++ = (ADD | RS1(R1) | RS2(R2) | RD(R3))
> +
> +#define emit_addi(R1, IMM, R3) \
> +	*prog++ = (ADD | IMMED | RS1(R1) | S13(IMM) | RD(R3))
> +
> +#define emit_alloc_stack(SZ) \
> +	*prog++ = (SUB | IMMED | RS1(SP) | S13(SZ) | RD(SP))
> +
> +#define emit_release_stack(SZ) \
> +	*prog++ = (ADD | IMMED | RS1(SP) | S13(SZ) | RD(SP))
> +
> +void bpf_jit_compile(struct sk_filter *fp)
> +{
> +	unsigned int cleanup_addr, proglen, oldproglen = 0;
> +	u32 temp[8], *prog, *func, seen = 0, pass;
> +	const struct sock_filter *filter = fp->insns;
> +	int i, flen = fp->len, pc_ret0 = -1;
> +	unsigned int *addrs;
> +	void *image;
> +
> +	if (!bpf_jit_enable)
> +		return;
> +
> +	addrs = kmalloc(flen * sizeof(*addrs), GFP_KERNEL);
> +	if (addrs == NULL)
> +		return;
> +
> +	/* Before first pass, make a rough estimation of addrs[]
> +	 * each bpf instruction is translated to less than 64 bytes
> +	 */
> +	for (proglen = 0, i = 0; i<  flen; i++) {
> +		proglen += 64;
> +		addrs[i] = proglen;
> +	}
> +	cleanup_addr = proglen; /* epilogue address */
> +	image = NULL;
> +	for (pass = 0; pass<  10; pass++) {
> +		u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen;
> +
> +		/* no prologue/epilogue for trivial filters (RET something) */
> +		proglen = 0;
> +		prog = temp;
> +
> +		/* Prologue */
> +		if (seen_or_pass0) {
> +			if (seen_or_pass0&  SEEN_MEM) {
> +				unsigned int sz = BASE_STACKFRAME;
> +				sz += BPF_MEMWORDS * sizeof(u32);
> +				emit_alloc_stack(sz);
> +			}
> +
> +			/* Make sure we dont leek kernel memory. */
> +			if (seen_or_pass0&  SEEN_XREG)
> +				emit_clear(r_X);
> +
> +			/* If this filter needs to access skb data,
> +			 * load %o4 and %o4 with:

2nd %o4 should be %o5

> +			 *  %o4 = skb->len - skb->data_len
> +			 *  %o5 = skb->data
> +			 * And also back up %o7 into r_saved_O7 so we can
> +			 * invoke the stubs using 'call'.
> +			 */
> +			if (seen_or_pass0&  SEEN_DATAREF) {
> +				emit_load32(r_SKB, struct sk_buff, len, r_HEADLEN);
> +				emit_load32(r_SKB, struct sk_buff, data_len, r_TMP);
> +				emit_sub(r_HEADLEN, r_TMP, r_HEADLEN);
> +				emit_loadptr(r_SKB, struct sk_buff, data, r_SKB_DATA);
> +			}
> +		}
> +		emit_reg_move(O7, r_saved_O7);
> +
> +		switch (filter[0].code) {
> +		case BPF_S_RET_K:
> +		case BPF_S_LD_W_LEN:
> +		case BPF_S_ANC_PROTOCOL:
> +		case BPF_S_ANC_PKTTYPE:
> +		case BPF_S_ANC_IFINDEX:
> +		case BPF_S_ANC_MARK:
> +		case BPF_S_ANC_RXHASH:
> +		case BPF_S_ANC_CPU:
> +		case BPF_S_ANC_QUEUE:
> +		case BPF_S_LD_W_ABS:
> +		case BPF_S_LD_H_ABS:
> +		case BPF_S_LD_B_ABS:
> +			/* The first instruction sets the A register (or is
> +			 * a "RET 'constant'")
> +			 */
> +			break;
> +		default:
> +			/* Make sure we dont leak kernel information to the
> +			 * user.
> +			 */
> +			emit_clear(r_A); /* A = 0 */
> +		}
> +
> +		for (i = 0; i<  flen; i++) {
> +			unsigned int K = filter[i].k;
> +			unsigned int t_offset;
> +			unsigned int f_offset;
> +			u32 t_op, f_op;
> +			int ilen;
> +
> +			switch (filter[i].code) {
> +			case BPF_S_ALU_ADD_X:	/* A += X; */
> +				emit_alu_X(ADD);
> +				break;
> +			case BPF_S_ALU_ADD_K:	/* A += K; */
> +				emit_alu_K(ADD, K);
> +				break;
> +			case BPF_S_ALU_SUB_X:	/* A -= X; */
> +				emit_alu_X(SUB);
> +				break;
> +			case BPF_S_ALU_SUB_K:	/* A -= K */
> +				emit_alu_K(SUB, K);
> +				break;
> +			case BPF_S_ALU_AND_X:	/* A&= X */
> +				emit_alu_X(AND);
> +				break;
> +			case BPF_S_ALU_AND_K:	/* A&= K */
> +				emit_alu_K(AND, K);
> +				break;
> +			case BPF_S_ALU_OR_X:	/* A |= X */
> +				emit_alu_X(OR);
> +				break;
> +			case BPF_S_ALU_OR_K:	/* A |= K */
> +				emit_alu_K(OR, K);
> +				break;
> +			case BPF_S_ALU_LSH_X:	/* A<<= X */
> +				emit_alu_X(SLL);
> +				break;
> +			case BPF_S_ALU_LSH_K:	/* A<<= K */
> +				emit_alu_K(SLL, K);
> +				break;
> +			case BPF_S_ALU_RSH_X:	/* A>>= X */
> +				emit_alu_X(SRL);
> +				break;
> +			case BPF_S_ALU_RSH_K:	/* A>>= K */
> +				emit_alu_K(SRL, K);
> +				break;
> +			case BPF_S_ALU_MUL_X:	/* A *= X; */
> +				emit_alu_X(MUL);
> +				break;
> +			case BPF_S_ALU_MUL_K:	/* A *= K */
> +				emit_alu_K(MUL, K);
> +				break;
> +			case BPF_S_ALU_DIV_K:	/* A /= K */
> +				emit_alu_K(MUL, K);
> +				emit_read_y(r_A);
> +				break;
> +			case BPF_S_ALU_DIV_X:	/* A /= X; */
> +				emit_cmpi(r_X, 0);
> +				if (pc_ret0>  0) {
> +					t_offset = addrs[pc_ret0 - 1];
> +#ifdef CONFIG_SPARC32
> +					emit_branch(BE, t_offset + 20);
> +#else
> +					emit_branch(BE, t_offset + 8);
> +#endif
> +					emit_nop(); /* delay slot */
> +				} else {
> +					emit_branch_off(BNE, 16);
> +					emit_nop();
> +#ifdef CONFIG_SPARC32
> +					emit_jump(cleanup_addr + 20);
> +#else
> +					emit_jump(cleanup_addr + 8);
> +#endif
> +					emit_clear(r_A);
> +				}
> +				emit_write_y(G0);
> +#ifdef CONFIG_SPARC32
> +				emit_nop();
> +				emit_nop();
> +				emit_nop();
> +#endif
> +				emit_alu_X(DIV);
> +				break;
> +			case BPF_S_ALU_NEG:
> +				emit_neg();
> +				break;
> +			case BPF_S_RET_K:
> +				if (!K) {
> +					if (pc_ret0 == -1)
> +						pc_ret0 = i;
> +					emit_clear(r_A);
> +				} else {
> +					emit_loadimm(K, r_A);
> +				}
> +				/* Fallthrough */
> +			case BPF_S_RET_A:
> +				if (seen_or_pass0) {
> +					if (i != flen - 1) {
> +						emit_jump(cleanup_addr);
> +						emit_nop();
> +						break;
> +					}
> +					if (seen_or_pass0&  SEEN_MEM) {
> +						unsigned int sz = BASE_STACKFRAME;
> +						sz += BPF_MEMWORDS * sizeof(u32);
> +						emit_release_stack(sz);
> +					}
> +				}
> +				/* jmpl %r_saved_O7 + 8, %g0 */
> +				emit_jmpl(r_saved_O7, 8, G0);
> +				emit_reg_move(r_A, O0); /* delay slot */
> +				break;
> +			case BPF_S_MISC_TAX:
> +				seen |= SEEN_XREG;
> +				emit_reg_move(r_A, r_X);
> +				break;
> +			case BPF_S_MISC_TXA:
> +				seen |= SEEN_XREG;
> +				emit_reg_move(r_X, r_A);
> +				break;
> +			case BPF_S_ANC_CPU:
> +				emit_load_cpu(r_A);
> +				break;
> +			case BPF_S_ANC_PROTOCOL:
> +				emit_skb_load16(protocol, r_A);
> +				break;
> +#if 0
> +				/* GCC won't let us take the address of
> +				 * a bit field even though we very much
> +				 * know what we are doing here.
> +				 */
> +			case BPF_S_ANC_PKTTYPE:
> +				__emit_skb_load8(pkt_type, r_A);
> +				emit_alu_K(SRL, 5);
> +				break;
> +#endif
> +			case BPF_S_ANC_IFINDEX:
> +				emit_skb_loadptr(dev, r_A);
> +				emit_cmpi(r_A, 0);
> +				emit_branch(BNE_PTR, cleanup_addr + 4);
> +				emit_nop();
> +				emit_load32(r_A, struct net_device, ifindex, r_A);
> +				break;
> +			case BPF_S_ANC_MARK:
> +				emit_skb_load32(mark, r_A);
> +				break;
> +			case BPF_S_ANC_QUEUE:
> +				emit_skb_load16(queue_mapping, r_A);
> +				break;
> +			case BPF_S_ANC_HATYPE:
> +				emit_skb_loadptr(dev, r_A);
> +				emit_cmpi(r_A, 0);
> +				emit_branch(BNE_PTR, cleanup_addr + 4);
> +				emit_nop();
> +				emit_load16(r_A, struct net_device, type, r_A);
> +				break;
> +			case BPF_S_ANC_RXHASH:
> +				emit_skb_load32(rxhash, r_A);
> +				break;
> +
> +			case BPF_S_LD_IMM:
> +				emit_loadimm(K, r_A);
> +				break;
> +			case BPF_S_LDX_IMM:
> +				emit_loadimm(K, r_X);
> +				break;
> +			case BPF_S_LD_MEM:
> +				emit_ldmem(K * 4, r_A);
> +				break;
> +			case BPF_S_LDX_MEM:
> +				emit_ldmem(K * 4, r_X);
> +				break;
> +			case BPF_S_ST:
> +				emit_stmem(K * 4, r_A);
> +				break;
> +			case BPF_S_STX:
> +				emit_stmem(K * 4, r_X);
> +				break;
> +
> +#define CHOOSE_LOAD_FUNC(K, func) \
> +	((int)K<  0 ? ((int)K>= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset)
> +
> +			case BPF_S_LD_W_ABS:
> +				func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_word);
> +common_load:			seen |= SEEN_DATAREF;
> +				emit_loadimm(K, r_OFF);
> +				emit_call(func);
> +				break;
> +			case BPF_S_LD_H_ABS:
> +				func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_half);
> +				goto common_load;
> +			case BPF_S_LD_B_ABS:
> +				func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte);
> +				goto common_load;
> +			case BPF_S_LDX_B_MSH:
> +				func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte_msh);
> +				goto common_load;
> +			case BPF_S_LD_W_IND:
> +				func = bpf_jit_load_word;
> +common_load_ind:		seen |= SEEN_DATAREF | SEEN_XREG;
> +				if (K) {
> +					if (is_simm13(K)) {
> +						emit_addi(r_X, K, r_OFF);
> +					} else {
> +						emit_loadimm(K, r_TMP);
> +						emit_add(r_X, r_TMP, r_OFF);
> +					}
> +				} else {
> +					emit_reg_move(r_X, r_OFF);
> +				}
> +				emit_call(func);
> +				break;
> +			case BPF_S_LD_H_IND:
> +				func = bpf_jit_load_half;
> +				goto common_load_ind;
> +			case BPF_S_LD_B_IND:
> +				func = bpf_jit_load_byte;
> +				goto common_load_ind;
> +			case BPF_S_JMP_JA:
> +				emit_jump(addrs[i + K]);
> +				emit_nop();
> +				break;
> +
> +#define COND_SEL(CODE, TOP, FOP)	\
> +	case CODE:			\
> +		t_op = TOP;		\
> +		f_op = FOP;		\
> +		goto cond_branch
> +
> +			COND_SEL(BPF_S_JMP_JGT_K, BGU, BLEU);
> +			COND_SEL(BPF_S_JMP_JGE_K, BGEU, BLU);
> +			COND_SEL(BPF_S_JMP_JEQ_K, BE, BNE);
> +			COND_SEL(BPF_S_JMP_JSET_K, BNE, BE);
> +			COND_SEL(BPF_S_JMP_JGT_X, BGU, BLEU);
> +			COND_SEL(BPF_S_JMP_JGE_X, BGEU, BLU);
> +			COND_SEL(BPF_S_JMP_JEQ_X, BE, BNE);
> +			COND_SEL(BPF_S_JMP_JSET_X, BNE, BE);
> +
> +cond_branch:			f_offset = addrs[i + filter[i].jf];
> +				t_offset = addrs[i + filter[i].jt];
> +
> +				/* same targets, can avoid doing the test :) */
> +				if (filter[i].jt == filter[i].jf) {
> +					emit_jump(t_offset);
> +					emit_nop();
> +					break;
> +				}
> +
> +				switch (filter[i].code) {
> +				case BPF_S_JMP_JGT_X:
> +				case BPF_S_JMP_JGE_X:
> +				case BPF_S_JMP_JEQ_X:
> +					seen |= SEEN_XREG;
> +					emit_cmp(r_A, r_X);
> +					break;
> +				case BPF_S_JMP_JSET_X:
> +					seen |= SEEN_XREG;
> +					emit_btst(r_A, r_X);
> +					break;
> +				case BPF_S_JMP_JEQ_K:
> +				case BPF_S_JMP_JGT_K:
> +				case BPF_S_JMP_JGE_K:
> +					if (is_simm13(K)) {
> +						emit_cmpi(r_A, K);
> +					} else {
> +						emit_loadimm(K, r_TMP);
> +						emit_cmp(r_A, r_TMP);
> +					}
> +					break;
> +				case BPF_S_JMP_JSET_K:
> +					if (is_simm13(K)) {
> +						emit_btsti(r_A, K);
> +					} else {
> +						emit_loadimm(K, r_TMP);
> +						emit_btst(r_A, r_TMP);
> +					}
> +					break;
> +				}
> +				if (filter[i].jt != 0) {
> +					if (filter[i].jf)
> +						t_offset += 8;
> +					emit_branch(t_op, t_offset);
> +					emit_nop(); /* delay slot */
> +					if (filter[i].jf) {
> +						emit_jump(f_offset);
> +						emit_nop();
> +					}
> +					break;
> +				}
> +				emit_branch(f_op, f_offset);
> +				emit_nop(); /* delay slot */
> +				break;
> +
> +			default:
> +				/* hmm, too complex filter, give up with jit compiler */
> +				goto out;
> +			}
> +			ilen = (void *) prog - (void *) temp;
> +			if (image) {
> +				if (unlikely(proglen + ilen>  oldproglen)) {
> +					pr_err("bpb_jit_compile fatal error\n");
> +					kfree(addrs);
> +					module_free(NULL, image);
> +					return;
> +				}
> +				memcpy(image + proglen, temp, ilen);
> +			}
> +			proglen += ilen;
> +			addrs[i] = proglen;
> +			prog = temp;
> +		}
> +		/* last bpf instruction is always a RET :
> +		 * use it to give the cleanup instruction(s) addr
> +		 */
> +		cleanup_addr = proglen - 8; /* jmpl; mov r_A,%o0; */
> +		if (seen_or_pass0&  SEEN_MEM)
> +			cleanup_addr -= 4; /* add %sp, X, %sp; */
> +
> +		if (image) {
> +			if (proglen != oldproglen)
> +				pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n",
> +				       proglen, oldproglen);
> +			break;
> +		}
> +		if (proglen == oldproglen) {
> +			image = module_alloc(max_t(unsigned int,
> +						   proglen,
> +						   sizeof(struct work_struct)));
> +			if (!image)
> +				goto out;
> +		}
> +		oldproglen = proglen;
> +	}
> +
> +	if (bpf_jit_enable>  1)
> +		pr_err("flen=%d proglen=%u pass=%d image=%p\n",
> +		       flen, proglen, pass, image);
> +
> +	if (image) {
> +		if (bpf_jit_enable>  1)
> +			print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS,
> +				       16, 1, image, proglen, false);
> +		bpf_flush_icache(image, image + proglen);
> +		fp->bpf_func = (void *)image;
> +	}
> +out:
> +	kfree(addrs);
> +	return;
> +}
> +
> +static void jit_free_defer(struct work_struct *arg)
> +{
> +	module_free(NULL, arg);
> +}
> +
> +/* run from softirq, we must use a work_struct to call
> + * module_free() from process context
> + */
> +void bpf_jit_free(struct sk_filter *fp)
> +{
> +	if (fp->bpf_func != sk_run_filter) {
> +		struct work_struct *work = (struct work_struct *)fp->bpf_func;
> +
> +		INIT_WORK(work, jit_free_defer);
> +		schedule_work(work);
> +	}
> +}

^ permalink raw reply

* Re: tg3 'No PHY devices' loading issue
From: Matt Carlson @ 2012-04-17 18:50 UTC (permalink / raw)
  To: Josh Boyer; +Cc: Matt Carlson, Michael Chan, netdev, linux-kernel
In-Reply-To: <20120417141856.GA19507@zod.bos.redhat.com>

On Tue, Apr 17, 2012 at 10:18:57AM -0400, Josh Boyer wrote:
> Hi Matt and Michael,
> 
> I'm seeing an odd issue with the tg3 driver on one of my development
> machines.  I've tried kernels 3.2.10, 3.3.0, 3.3.1, 3.3.2 and 3.4-rc3
> and they all seem to exhibit this issue now.  When the machine boots
> and the tg3 driver is loaded, it fails to find a PHY and then reports
> 'Problem fetching invariants of chip'.  If I do a rmmod/modprobe of
> tg3 after login, the probe seems to work fine and ethernet works as
> expected.  You can see this in the dmesg below:
> 
> [jwboyer@vader ~]$ dmesg | grep tg3
> [    2.084969] tg3.c:v3.122 (December 7, 2011)
> [    2.093511] tg3 mdio bus: probed
> [    2.093513] tg3 0000:03:00.0: No PHY devices
> [    2.093531] tg3 0000:03:00.0: Problem fetching invariants of chip, aborting
> [   90.824697] tg3.c:v3.122 (December 7, 2011)
> [   90.857068] tg3 mdio bus: probed
> [   90.862540] tg3 0000:03:00.0: eth0: Tigon3 [partno(BCM57788) rev 57780001] (PCI Express) MAC address 18:03:73:e6:01:88
> [   90.862547] tg3 0000:03:00.0: eth0: attached PHY driver [Broadcom BCM57780] (mii_bus:phy_addr=300:01)
> [   90.862552] tg3 0000:03:00.0: eth0: RXcsums[1] LinkChgREG[0] MIirq[0] ASF[0] TSOcap[1]
> [   90.862557] tg3 0000:03:00.0: eth0: dma_rwctrl[76180000] dma_mask[64-bit]
> [   90.919961] tg3 0000:03:00.0: irq 47 for MSI/MSI-X
> [   91.863311] tg3 0000:03:00.0: p3p1: Link is down
> [   92.864348] tg3 0000:03:00.0: p3p1: Link is up at 100 Mbps, full duplex
> [   92.864352] tg3 0000:03:00.0: p3p1: Flow control is on for TX and on for RX
> 
> It has worked on some of the older kernels without the need for the
> manual rmmod/modprobe step, so it seems to be somewhat timing related.
> I'm not sure if there is a module load ordering issue, but that doesn't
> seem to be the case.  I can't explain why a later modprobe would work
> just fine though.
> 
> Do you have any thoughts on how to go about debugging/fixing this?  I'd
> be happy to test and provide whatever information you need.

The 57788 uses the broadcom phylib module.  For some reason, it isn't
available the first module load attempt.  A while ago, code was added to
phylib to request modules from userspace if the particular phy support
code wasn't already loaded.  It looks like this mechanism isn't working
too well the first time through.

Can you reproduce the problem if you run 'rmmod broadcom' and then
reload tg3?

^ permalink raw reply

* Re: 3.3.0, 3.4-rc1 reproducible tun Oops
From: Stanislav Kinsbursky @ 2012-04-17 18:49 UTC (permalink / raw)
  To: Simon Kirby; +Cc: Eric Dumazet, netdev@vger.kernel.org
In-Reply-To: <20120417183528.GA32726@hostway.ca>

17.04.2012 22:35, Simon Kirby написал:
> On Tue, Apr 17, 2012 at 04:18:53PM +0400, Stanislav Kinsbursky wrote:
>
>> 17.04.2012 06:08, Simon Kirby ??????????:
>>> On Thu, Apr 05, 2012 at 04:41:04AM +0200, Eric Dumazet wrote:
>>>
>>>> Hmm, is it happening if you remove the nvidia module ?
>>>>
>>>> If yes, please try to add slub_debug=FZPU
>>> Finally got annoyed enough at this to bisect it. It doesn't happen every
>>> time and I got a bit confused, but I finally tracked it down to:
>>>
>>> 1ab5ecb90cb6a3df1476e052f76a6e8f6511cb3d is the first bad commit
>>> commit 1ab5ecb90cb6a3df1476e052f76a6e8f6511cb3d
>>> Author: Stanislav Kinsbursky<skinsbursky@parallels.com>
>>> Date:   Mon Mar 12 02:59:41 2012 +0000
>>>
>>>      tun: don't hold network namespace by tun sockets
>>>
>>>      v3: added previously removed sock_put() to the tun_release() callback, because
>>>      sk_release_kernel() doesn't drop the socket reference.
>>>
>>>      v2: sk_release_kernel() used for socket release. Dummy tun_release() is
>>>      required for sk_release_kernel() --->   sock_release() --->   sock->ops->release()
>>>      call.
>>>
>>>      TUN was designed to destroy it's socket on network namesapce shutdown. But this
>>>      will never happen for persistent device, because it's socket holds network
>>>      namespace.
>>>      This patch removes of holding network namespace by TUN socket and replaces it
>>>      by creating socket in init_net and then changing it's net it to desired one. On
>>>      shutdown socket is moved back to init_net prior to final put.
>>>
>>>      Signed-off-by: Stanislav Kinsbursky<skinsbursky@parallels.com>
>>>      Signed-off-by: David S. Miller<davem@davemloft.net>
>>>
>>> ...With this reverted on top of 3.4-rc3, I no longer see crashes when I
>>> keep making and breaking the SSH tunnel while running "vmstat 1" in an
>>> SSH session over a socket that is running through that tunnel.
>>>
>>> Simon-
>> Hi, Simon.
>> Could you please try to apply the patch below on top of your the
>> tree (with 1ab5ecb90cb6a3df1476e052f76a6e8f6511cb3d applied) and
>> check does it fix the problem:
>>
>> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
>> index bb8c72c..1fc4622 100644
>> --- a/drivers/net/tun.c
>> +++ b/drivers/net/tun.c
>> @@ -1540,13 +1540,10 @@ static int tun_chr_close(struct inode
>> *inode, struct file *file)
>>   			if (dev->reg_state == NETREG_REGISTERED)
>>   				unregister_netdevice(dev);
>>   			rtnl_unlock();
>> -		}
>> +		} else
>> +			sock_put(tun->socket.sk);
>>   	}
>>
>> -	tun = tfile->tun;
>> -	if (tun)
>> -		sock_put(tun->socket.sk);
>> -
>>   	put_net(tfile->net);
>>   	kfree(tfile);
> (Whitespace-damaged patch, applied manually)
>
> Yes, I no longer see crashes with this applied. I haven't tried with
> kmemleak or similar, but it seems to work.

Sorry for whitespaces.
And thanks, Simon.

^ permalink raw reply

* Re: 3.3.0, 3.4-rc1 reproducible tun Oops
From: Simon Kirby @ 2012-04-17 18:35 UTC (permalink / raw)
  To: Stanislav Kinsbursky; +Cc: Eric Dumazet, netdev@vger.kernel.org
In-Reply-To: <4F8D5FAD.10304@parallels.com>

On Tue, Apr 17, 2012 at 04:18:53PM +0400, Stanislav Kinsbursky wrote:

> 17.04.2012 06:08, Simon Kirby ??????????:
> >On Thu, Apr 05, 2012 at 04:41:04AM +0200, Eric Dumazet wrote:
> >
> >>Hmm, is it happening if you remove the nvidia module ?
> >>
> >>If yes, please try to add slub_debug=FZPU
> >
> >Finally got annoyed enough at this to bisect it. It doesn't happen every
> >time and I got a bit confused, but I finally tracked it down to:
> >
> >1ab5ecb90cb6a3df1476e052f76a6e8f6511cb3d is the first bad commit
> >commit 1ab5ecb90cb6a3df1476e052f76a6e8f6511cb3d
> >Author: Stanislav Kinsbursky<skinsbursky@parallels.com>
> >Date:   Mon Mar 12 02:59:41 2012 +0000
> >
> >     tun: don't hold network namespace by tun sockets
> >
> >     v3: added previously removed sock_put() to the tun_release() callback, because
> >     sk_release_kernel() doesn't drop the socket reference.
> >
> >     v2: sk_release_kernel() used for socket release. Dummy tun_release() is
> >     required for sk_release_kernel() --->  sock_release() --->  sock->ops->release()
> >     call.
> >
> >     TUN was designed to destroy it's socket on network namesapce shutdown. But this
> >     will never happen for persistent device, because it's socket holds network
> >     namespace.
> >     This patch removes of holding network namespace by TUN socket and replaces it
> >     by creating socket in init_net and then changing it's net it to desired one. On
> >     shutdown socket is moved back to init_net prior to final put.
> >
> >     Signed-off-by: Stanislav Kinsbursky<skinsbursky@parallels.com>
> >     Signed-off-by: David S. Miller<davem@davemloft.net>
> >
> >...With this reverted on top of 3.4-rc3, I no longer see crashes when I
> >keep making and breaking the SSH tunnel while running "vmstat 1" in an
> >SSH session over a socket that is running through that tunnel.
> >
> >Simon-
> 
> Hi, Simon.
> Could you please try to apply the patch below on top of your the
> tree (with 1ab5ecb90cb6a3df1476e052f76a6e8f6511cb3d applied) and
> check does it fix the problem:
> 
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index bb8c72c..1fc4622 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -1540,13 +1540,10 @@ static int tun_chr_close(struct inode
> *inode, struct file *file)
>  			if (dev->reg_state == NETREG_REGISTERED)
>  				unregister_netdevice(dev);
>  			rtnl_unlock();
> -		}
> +		} else
> +			sock_put(tun->socket.sk);
>  	}
> 
> -	tun = tfile->tun;
> -	if (tun)
> -		sock_put(tun->socket.sk);
> -
>  	put_net(tfile->net);
>  	kfree(tfile);

(Whitespace-damaged patch, applied manually)

Yes, I no longer see crashes with this applied. I haven't tried with
kmemleak or similar, but it seems to work.

Thanks,

Simon-

^ permalink raw reply

* Route flush on linkdown: physical vs virtual/stacked interfaces
From: Christian Benvenuti (benve) @ 2012-04-17 17:33 UTC (permalink / raw)
  To: netdev

When we assign an IP address to an interface, the kernel
configures a number of routes:
- /32 for the configured IP address
- /32 for the broadcast/s (two of them)
- /netmask for the network
The /32 generated routes go to the LOCAL table, and
the /netmask route goes to the MAIN table.
OK.

When you ifconfig down an interface, the kernel removes
the /netmask route from the MAIN table but keeps the /32
routes in the LOCAL table.
The same happens when there is a link down event on the same
interface.
And this is fine.

However, software/stacked devices (like vlan and bridges) behave
differently with regards to linkdown events:

- let's assume we assigned an IP address to a vlan interface
  # vconfig add eth0 100
  # ifconfig eth0.100 10.0.1.1 netmask 255.255.255.0

- when you ifconfig down the (physical) interface sitting
  below the vlan interface (eth0 in the above example), the
  kernel removes all the routes previously generated by
  the above vlan interface IP address configuration (ie, both
  the /32 routes and the /netmask route).

- However, when there is a linkdown event for the lower
  interface (again eth0 in the above example), the kernel
  does not remove any of the routes associated with the vlan
  interface. 

The same happens for example with a bridge if I enslave only
one NIC and I ifconfig down the latter.

The result is that the /netmask route derived from the vlan
interface ip config remains in the routing table and becomes
a black hole.
Applications will (hopelessly) use it instead of, for
example, receiving a "network is unreachable" kind of error
message or using an alternate route.

Since latest net-next still behaves this way, is this behavior
wanted or is this a bug?

/Chris

^ permalink raw reply

* [PATCH v4 1/3] netdev/of/phy: New function: of_mdio_find_bus().
From: David Daney @ 2012-04-17 17:32 UTC (permalink / raw)
  To: Grant Likely, Rob Herring, devicetree-discuss, David S. Miller,
	netdev
  Cc: linux-kernel, linux-mips, afleming, galak, David Daney,
	Grant Likely, David S. Miller
In-Reply-To: <1334683966-12112-1-git-send-email-ddaney.cavm@gmail.com>

From: David Daney <david.daney@cavium.com>

Add of_mdio_find_bus() which allows an mii_bus to be located given its
associated the device tree node.

This is needed by the follow-on patch to add a driver for MDIO bus
multiplexers.

The of_mdiobus_register() function is modified so that the device tree
node is recorded in the mii_bus.  Then we can find it again by
iterating over all mdio_bus_class devices.

Because the OF device tree has now become an integral part of the
kernel, this can live in mdio_bus.c (which contains the needed
mdio_bus_class structure) instead of of_mdio.c.

Signed-off-by: David Daney <david.daney@cavium.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: "David S. Miller" <davem@davemloft.net>
---
 drivers/net/phy/mdio_bus.c |   32 ++++++++++++++++++++++++++++++++
 drivers/of/of_mdio.c       |    2 ++
 include/linux/of_mdio.h    |    2 ++
 3 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 8985cc6..83d5c9f 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -88,6 +88,38 @@ static struct class mdio_bus_class = {
 	.dev_release	= mdiobus_release,
 };
 
+#ifdef CONFIG_OF_MDIO
+/* Helper function for of_mdio_find_bus */
+static int of_mdio_bus_match(struct device *dev, void *mdio_bus_np)
+{
+	return dev->of_node == mdio_bus_np;
+}
+/**
+ * of_mdio_find_bus - Given an mii_bus node, find the mii_bus.
+ * @mdio_np: Pointer to the mii_bus.
+ *
+ * Returns a pointer to the mii_bus, or NULL if none found.
+ *
+ * Because the association of a device_node and mii_bus is made via
+ * of_mdiobus_register(), the mii_bus cannot be found before it is
+ * registered with of_mdiobus_register().
+ *
+ */
+struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np)
+{
+	struct device *d;
+
+	if (!mdio_bus_np)
+		return NULL;
+
+	d = class_find_device(&mdio_bus_class, NULL,  mdio_bus_np,
+			      of_mdio_bus_match);
+
+	return d ? to_mii_bus(d) : NULL;
+}
+EXPORT_SYMBOL(of_mdio_find_bus);
+#endif
+
 /**
  * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus
  * @bus: target mii_bus
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 483c0ad..2574abd 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -45,6 +45,8 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 		for (i=0; i<PHY_MAX_ADDR; i++)
 			mdio->irq[i] = PHY_POLL;
 
+	mdio->dev.of_node = np;
+
 	/* Register the MDIO bus */
 	rc = mdiobus_register(mdio);
 	if (rc)
diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h
index 53b94e0..912c27a 100644
--- a/include/linux/of_mdio.h
+++ b/include/linux/of_mdio.h
@@ -22,4 +22,6 @@ extern struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
 					 void (*hndlr)(struct net_device *),
 					 phy_interface_t iface);
 
+extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np);
+
 #endif /* __LINUX_OF_MDIO_H */
-- 
1.7.2.3

^ permalink raw reply related

* [PATCH v4 3/3] netdev/of/phy: Add MDIO bus multiplexer driven by GPIO lines.
From: David Daney @ 2012-04-17 17:32 UTC (permalink / raw)
  To: Grant Likely, Rob Herring, devicetree-discuss, David S. Miller,
	netdev
  Cc: linux-kernel, linux-mips, afleming, galak, David Daney
In-Reply-To: <1334683966-12112-1-git-send-email-ddaney.cavm@gmail.com>

From: David Daney <david.daney@cavium.com>

The GPIO pins select which sub bus is connected to the master.

Initially tested with an sn74cbtlv3253 switch device wired into the
MDIO bus.

Signed-off-by: David Daney <david.daney@cavium.com>
---
 .../devicetree/bindings/net/mdio-mux-gpio.txt      |  127 +++++++++++++++++
 drivers/net/phy/Kconfig                            |   10 ++
 drivers/net/phy/Makefile                           |    1 +
 drivers/net/phy/mdio-mux-gpio.c                    |  142 ++++++++++++++++++++
 4 files changed, 280 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/mdio-mux-gpio.txt
 create mode 100644 drivers/net/phy/mdio-mux-gpio.c

diff --git a/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt b/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt
new file mode 100644
index 0000000..7938411
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt
@@ -0,0 +1,127 @@
+Properties for an MDIO bus multiplexer/switch controlled by GPIO pins.
+
+This is a special case of a MDIO bus multiplexer.  One or more GPIO
+lines are used to control which child bus is connected.
+
+Required properties in addition to the generic multiplexer properties:
+
+- compatible : mdio-mux-gpio.
+- gpios : GPIO specifiers for each GPIO line.  One or more must be specified.
+
+
+Example :
+
+	/* The parent MDIO bus. */
+	smi1: mdio@1180000001900 {
+		compatible = "cavium,octeon-3860-mdio";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x11800 0x00001900 0x0 0x40>;
+	};
+
+	/*
+	   An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a
+	   pair of GPIO lines.  Child busses 2 and 3 populated with 4
+	   PHYs each.
+	 */
+	mdio-mux {
+		compatible = "mdio-mux-gpio";
+		gpios = <&gpio1 3 0>, <&gpio1 4 0>;
+		mdio-parent-bus = <&smi1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mdio@2 {
+			reg = <2>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			phy11: ethernet-phy@1 {
+				reg = <1>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+			phy12: ethernet-phy@2 {
+				reg = <2>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+			phy13: ethernet-phy@3 {
+				reg = <3>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+			phy14: ethernet-phy@4 {
+				reg = <4>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+		};
+
+		mdio@3 {
+			reg = <3>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			phy21: ethernet-phy@1 {
+				reg = <1>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+			phy22: ethernet-phy@2 {
+				reg = <2>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+			phy23: ethernet-phy@3 {
+				reg = <3>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+			phy24: ethernet-phy@4 {
+				reg = <4>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+		};
+	};
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 222b06b..39d8c66 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -143,6 +143,16 @@ config MDIO_BUS_MUX
 	  to a parent bus.  Switching between child busses is done by
 	  device specific drivers.
 
+config MDIO_BUS_MUX_GPIO
+	tristate "Support for GPIO controlled MDIO bus multiplexers"
+	depends on GENERIC_GPIO
+	select MDIO_BUS_MUX
+	help
+	  This module provides a driver for MDIO bus multiplexers that
+	  are controlled via GPIO lines.  The multiplexer connects one of
+	  several child MDIO busses to a parent bus.  Child bus
+	  selection is under the control of GPIO lines.
+
 endif # PHYLIB
 
 config MICREL_KS8995MA
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index a6b50e7..f51af68 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_MDIO_OCTEON)	+= mdio-octeon.o
 obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o
 obj-$(CONFIG_AMD_PHY)		+= amd.o
 obj-$(CONFIG_MDIO_BUS_MUX)	+= mdio-mux.o
+obj-$(CONFIG_MDIO_BUS_MUX_GPIO)	+= mdio-mux-gpio.o
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
new file mode 100644
index 0000000..e0cc4ef
--- /dev/null
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -0,0 +1,142 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2011, 2012 Cavium, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/of_mdio.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/phy.h>
+#include <linux/mdio-mux.h>
+#include <linux/of_gpio.h>
+
+#define DRV_VERSION "1.0"
+#define DRV_DESCRIPTION "GPIO controlled MDIO bus multiplexer driver"
+
+#define MDIO_MUX_GPIO_MAX_BITS 8
+
+struct mdio_mux_gpio_state {
+	int gpio[MDIO_MUX_GPIO_MAX_BITS];
+	unsigned int num_gpios;
+	void *mux_handle;
+};
+
+static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
+				   void *data)
+{
+	int change;
+	unsigned int n;
+	struct mdio_mux_gpio_state *s = data;
+
+	if (current_child == desired_child)
+		return 0;
+
+	change = current_child == -1 ? -1 : current_child ^ desired_child;
+
+	for (n = 0; n < s->num_gpios; n++) {
+		if (change & 1)
+			gpio_set_value_cansleep(s->gpio[n],
+						(desired_child & 1) != 0);
+		change >>= 1;
+		desired_child >>= 1;
+	}
+
+	return 0;
+}
+
+static int __devinit mdio_mux_gpio_probe(struct platform_device *pdev)
+{
+	enum of_gpio_flags f;
+	struct mdio_mux_gpio_state *s;
+	unsigned int num_gpios;
+	unsigned int n;
+	int r;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	num_gpios = of_gpio_count(pdev->dev.of_node);
+	if (num_gpios == 0 || num_gpios > MDIO_MUX_GPIO_MAX_BITS)
+		return -ENODEV;
+
+	s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
+	if (!s)
+		return -ENOMEM;
+
+	s->num_gpios = num_gpios;
+
+	for (n = 0; n < num_gpios; ) {
+		int gpio = of_get_gpio_flags(pdev->dev.of_node, n, &f);
+		if (gpio < 0) {
+			r = (gpio == -ENODEV) ? -EPROBE_DEFER : gpio;
+			goto err;
+		}
+		s->gpio[n] = gpio;
+
+		n++;
+
+		r = gpio_request(gpio, "mdio_mux_gpio");
+		if (r)
+			goto err;
+
+		r = gpio_direction_output(gpio, 0);
+		if (r)
+			goto err;
+	}
+
+	r = mdio_mux_init(&pdev->dev,
+			  mdio_mux_gpio_switch_fn, &s->mux_handle, s);
+
+	if (r == 0) {
+		pdev->dev.platform_data = s;
+		return 0;
+	}
+err:
+	while (n) {
+		n--;
+		gpio_free(s->gpio[n]);
+	}
+	devm_kfree(&pdev->dev, s);
+	return r;
+}
+
+static int __devexit mdio_mux_gpio_remove(struct platform_device *pdev)
+{
+	struct mdio_mux_gpio_state *s = pdev->dev.platform_data;
+	mdio_mux_uninit(s->mux_handle);
+	return 0;
+}
+
+static struct of_device_id mdio_mux_gpio_match[] = {
+	{
+		.compatible = "mdio-mux-gpio",
+	},
+	{
+		/* Legacy compatible property. */
+		.compatible = "cavium,mdio-mux-sn74cbtlv3253",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, mdio_mux_gpio_match);
+
+static struct platform_driver mdio_mux_gpio_driver = {
+	.driver = {
+		.name		= "mdio-mux-gpio",
+		.owner		= THIS_MODULE,
+		.of_match_table = mdio_mux_gpio_match,
+	},
+	.probe		= mdio_mux_gpio_probe,
+	.remove		= __devexit_p(mdio_mux_gpio_remove),
+};
+
+module_platform_driver(mdio_mux_gpio_driver);
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("David Daney");
+MODULE_LICENSE("GPL");
-- 
1.7.2.3

^ permalink raw reply related

* [PATCH v4 2/3] netdev/of/phy: Add MDIO bus multiplexer support.
From: David Daney @ 2012-04-17 17:32 UTC (permalink / raw)
  To: Grant Likely, Rob Herring, devicetree-discuss, David S. Miller,
	netdev
  Cc: linux-kernel, linux-mips, afleming, galak, David Daney
In-Reply-To: <1334683966-12112-1-git-send-email-ddaney.cavm@gmail.com>

From: David Daney <david.daney@cavium.com>

This patch adds a somewhat generic framework for MDIO bus
multiplexers.  It is modeled on the I2C multiplexer.

The multiplexer is needed if there are multiple PHYs with the same
address connected to the same MDIO bus adepter, or if there is
insufficient electrical drive capability for all the connected PHY
devices.

Conceptually it could look something like this:

                   ------------------
                   | Control Signal |
                   --------+---------
                           |
 ---------------   --------+------
 | MDIO MASTER |---| Multiplexer |
 ---------------   --+-------+----
                     |       |
                     C       C
                     h       h
                     i       i
                     l       l
                     d       d
                     |       |
     ---------       A       B   ---------
     |       |       |       |   |       |
     | PHY@1 +-------+       +---+ PHY@1 |
     |       |       |       |   |       |
     ---------       |       |   ---------
     ---------       |       |   ---------
     |       |       |       |   |       |
     | PHY@2 +-------+       +---+ PHY@2 |
     |       |                   |       |
     ---------                   ---------

This framework configures the bus topology from device tree data.  The
mechanics of switching the multiplexer is left to device specific
drivers.

The follow-on patch contains a multiplexer driven by GPIO lines.

Signed-off-by: David Daney <david.daney@cavium.com>
---
 Documentation/devicetree/bindings/net/mdio-mux.txt |  136 ++++++++++++++
 drivers/net/phy/Kconfig                            |    8 +
 drivers/net/phy/Makefile                           |    1 +
 drivers/net/phy/mdio-mux.c                         |  192 ++++++++++++++++++++
 include/linux/mdio-mux.h                           |   21 ++
 5 files changed, 358 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/mdio-mux.txt
 create mode 100644 drivers/net/phy/mdio-mux.c
 create mode 100644 include/linux/mdio-mux.h

diff --git a/Documentation/devicetree/bindings/net/mdio-mux.txt b/Documentation/devicetree/bindings/net/mdio-mux.txt
new file mode 100644
index 0000000..f65606f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mdio-mux.txt
@@ -0,0 +1,136 @@
+Common MDIO bus multiplexer/switch properties.
+
+An MDIO bus multiplexer/switch will have several child busses that are
+numbered uniquely in a device dependent manner.  The nodes for an MDIO
+bus multiplexer/switch will have one child node for each child bus.
+
+Required properties:
+- mdio-parent-bus : phandle to the parent MDIO bus.
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- Other properties specific to the multiplexer/switch hardware.
+
+Required properties for child nodes:
+- #address-cells = <1>;
+- #size-cells = <0>;
+- reg : The sub-bus number.
+
+
+Example :
+
+	/* The parent MDIO bus. */
+	smi1: mdio@1180000001900 {
+		compatible = "cavium,octeon-3860-mdio";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x11800 0x00001900 0x0 0x40>;
+	};
+
+	/*
+	   An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a
+	   pair of GPIO lines.  Child busses 2 and 3 populated with 4
+	   PHYs each.
+	 */
+	mdio-mux {
+		compatible = "mdio-mux-gpio";
+		gpios = <&gpio1 3 0>, <&gpio1 4 0>;
+		mdio-parent-bus = <&smi1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mdio@2 {
+			reg = <2>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			phy11: ethernet-phy@1 {
+				reg = <1>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+			phy12: ethernet-phy@2 {
+				reg = <2>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+			phy13: ethernet-phy@3 {
+				reg = <3>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+			phy14: ethernet-phy@4 {
+				reg = <4>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <10 8>; /* Pin 10, active low */
+			};
+		};
+
+		mdio@3 {
+			reg = <3>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			phy21: ethernet-phy@1 {
+				reg = <1>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+			phy22: ethernet-phy@2 {
+				reg = <2>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+			phy23: ethernet-phy@3 {
+				reg = <3>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+			phy24: ethernet-phy@4 {
+				reg = <4>;
+				compatible = "marvell,88e1149r";
+				marvell,reg-init = <3 0x10 0 0x5777>,
+					<3 0x11 0 0x00aa>,
+					<3 0x12 0 0x4105>,
+					<3 0x13 0 0x0a60>;
+				interrupt-parent = <&gpio>;
+				interrupts = <12 8>; /* Pin 12, active low */
+			};
+		};
+	};
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 0e01f4e..222b06b 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -135,6 +135,14 @@ config MDIO_OCTEON
 
 	  If in doubt, say Y.
 
+config MDIO_BUS_MUX
+	tristate
+	help
+	  This module provides a driver framework for MDIO bus
+	  multiplexers which connect one of several child MDIO busses
+	  to a parent bus.  Switching between child busses is done by
+	  device specific drivers.
+
 endif # PHYLIB
 
 config MICREL_KS8995MA
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index b7438b1..a6b50e7 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_MICREL_PHY)	+= micrel.o
 obj-$(CONFIG_MDIO_OCTEON)	+= mdio-octeon.o
 obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o
 obj-$(CONFIG_AMD_PHY)		+= amd.o
+obj-$(CONFIG_MDIO_BUS_MUX)	+= mdio-mux.o
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
new file mode 100644
index 0000000..39ea067
--- /dev/null
+++ b/drivers/net/phy/mdio-mux.c
@@ -0,0 +1,192 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2011, 2012 Cavium, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/mdio-mux.h>
+#include <linux/of_mdio.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define DRV_VERSION "1.0"
+#define DRV_DESCRIPTION "MDIO bus multiplexer driver"
+
+struct mdio_mux_child_bus;
+
+struct mdio_mux_parent_bus {
+	struct mii_bus *mii_bus;
+	int current_child;
+	int parent_id;
+	void *switch_data;
+	int (*switch_fn)(int current_child, int desired_child, void *data);
+
+	/* List of our children linked through their next fields. */
+	struct mdio_mux_child_bus *children;
+};
+
+struct mdio_mux_child_bus {
+	struct mii_bus *mii_bus;
+	struct mdio_mux_parent_bus *parent;
+	struct mdio_mux_child_bus *next;
+	int bus_number;
+	int phy_irq[PHY_MAX_ADDR];
+};
+
+/*
+ * The parent bus' lock is used to order access to the switch_fn.
+ */
+static int mdio_mux_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+	struct mdio_mux_child_bus *cb = bus->priv;
+	struct mdio_mux_parent_bus *pb = cb->parent;
+	int r;
+
+	mutex_lock(&pb->mii_bus->mdio_lock);
+	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
+	if (r)
+		goto out;
+
+	pb->current_child = cb->bus_number;
+
+	r = pb->mii_bus->read(pb->mii_bus, phy_id, regnum);
+out:
+	mutex_unlock(&pb->mii_bus->mdio_lock);
+
+	return r;
+}
+
+/*
+ * The parent bus' lock is used to order access to the switch_fn.
+ */
+static int mdio_mux_write(struct mii_bus *bus, int phy_id,
+			  int regnum, u16 val)
+{
+	struct mdio_mux_child_bus *cb = bus->priv;
+	struct mdio_mux_parent_bus *pb = cb->parent;
+
+	int r;
+
+	mutex_lock(&pb->mii_bus->mdio_lock);
+	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
+	if (r)
+		goto out;
+
+	pb->current_child = cb->bus_number;
+
+	r = pb->mii_bus->write(pb->mii_bus, phy_id, regnum, val);
+out:
+	mutex_unlock(&pb->mii_bus->mdio_lock);
+
+	return r;
+}
+
+static int parent_count;
+
+int mdio_mux_init(struct device *dev,
+		  int (*switch_fn)(int cur, int desired, void *data),
+		  void **mux_handle,
+		  void *data)
+{
+	struct device_node *parent_bus_node;
+	struct device_node *child_bus_node;
+	int r, ret_val;
+	struct mii_bus *parent_bus;
+	struct mdio_mux_parent_bus *pb;
+	struct mdio_mux_child_bus *cb;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	parent_bus_node = of_parse_phandle(dev->of_node, "mdio-parent-bus", 0);
+
+	if (!parent_bus_node)
+		return -ENODEV;
+
+	parent_bus = of_mdio_find_bus(parent_bus_node);
+	if (parent_bus == NULL) {
+		ret_val = -EPROBE_DEFER;
+		goto err_parent_bus;
+	}
+
+	pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
+	if (pb == NULL) {
+		ret_val = -ENOMEM;
+		goto err_parent_bus;
+	}
+
+	pb->switch_data = data;
+	pb->switch_fn = switch_fn;
+	pb->current_child = -1;
+	pb->parent_id = parent_count++;
+	pb->mii_bus = parent_bus;
+
+	ret_val = -ENODEV;
+	for_each_child_of_node(dev->of_node, child_bus_node) {
+		u32 v;
+
+		r = of_property_read_u32(child_bus_node, "reg", &v);
+		if (r)
+			continue;
+
+		cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);
+		if (cb == NULL) {
+			dev_err(dev,
+				"Error: Failed to allocate memory for child\n");
+			ret_val = -ENOMEM;
+			break;
+		}
+		cb->bus_number = v;
+		cb->parent = pb;
+		cb->mii_bus = mdiobus_alloc();
+		cb->mii_bus->priv = cb;
+
+		cb->mii_bus->irq = cb->phy_irq;
+		cb->mii_bus->name = "mdio_mux";
+		snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x",
+			 pb->parent_id, v);
+		cb->mii_bus->parent = dev;
+		cb->mii_bus->read = mdio_mux_read;
+		cb->mii_bus->write = mdio_mux_write;
+		r = of_mdiobus_register(cb->mii_bus, child_bus_node);
+		if (r) {
+			mdiobus_free(cb->mii_bus);
+			devm_kfree(dev, cb);
+		} else {
+			of_node_get(child_bus_node);
+			cb->next = pb->children;
+			pb->children = cb;
+		}
+	}
+	if (pb->children) {
+		*mux_handle = pb;
+		dev_info(dev, "Version " DRV_VERSION "\n");
+		return 0;
+	}
+err_parent_bus:
+	of_node_put(parent_bus_node);
+	return ret_val;
+}
+EXPORT_SYMBOL_GPL(mdio_mux_init);
+
+void mdio_mux_uninit(void *mux_handle)
+{
+	struct mdio_mux_parent_bus *pb = mux_handle;
+	struct mdio_mux_child_bus *cb = pb->children;
+
+	while (cb) {
+		mdiobus_unregister(cb->mii_bus);
+		mdiobus_free(cb->mii_bus);
+		cb = cb->next;
+	}
+}
+EXPORT_SYMBOL_GPL(mdio_mux_uninit);
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("David Daney");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mdio-mux.h b/include/linux/mdio-mux.h
new file mode 100644
index 0000000..a243dbb
--- /dev/null
+++ b/include/linux/mdio-mux.h
@@ -0,0 +1,21 @@
+/*
+ * MDIO bus multiplexer framwork.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2011, 2012 Cavium, Inc.
+ */
+#ifndef __LINUX_MDIO_MUX_H
+#define __LINUX_MDIO_MUX_H
+#include <linux/device.h>
+
+int mdio_mux_init(struct device *dev,
+		  int (*switch_fn) (int cur, int desired, void *data),
+		  void **mux_handle,
+		  void *data);
+
+void mdio_mux_uninit(void *mux_handle);
+
+#endif /* __LINUX_MDIO_MUX_H */
-- 
1.7.2.3

^ permalink raw reply related

* [PATCH v4 0/3] netdev/of/phy: MDIO bus multiplexer support.
From: David Daney @ 2012-04-17 17:32 UTC (permalink / raw)
  To: Grant Likely, Rob Herring,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, David S. Miller,
	netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	afleming-Re5JQEeQqe8AvxtiuMwx3w, David Daney,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

From: David Daney <david.daney-YGCgFSpz5w/QT0dZR+AlfA@public.gmane.org>

This code has been working well for about six months on a couple of
different configurations (boards), so I thought it would be a good
time to send it out again, and I hope get it on the path towards
merging.

v4: Correct some comment text and rename a couple of variables to
    better reflect their purpose.

v3: Update binding to use "mdio-mux-gpio" compatible property.
    Cleanups suggested by Grant Likely.  Now uses the driver probe
    deferral mechanism if GPIOs or parent bus not available.

v2: Update bindings to use "reg" and "mdio-parent-bus" instead of
    "cell-index" and "parent-bus"

v1:

We have several different boards with a multiplexer in the MDIO bus.
There is an MDIO bus controller connected to a switching device with
several child MDIO busses.

Everything is wired up using device tree bindings.

 1/3 - New of_mdio_find_bus() function used to help configuring the
       driver topology.

 2/3 - MDIO bus multiplexer framework.

 3/3 - A driver for a GPIO controlled multiplexer.

David Daney (3):
  netdev/of/phy: New function: of_mdio_find_bus().
  netdev/of/phy: Add MDIO bus multiplexer support.
  netdev/of/phy: Add MDIO bus multiplexer driven by GPIO lines.

 .../devicetree/bindings/net/mdio-mux-gpio.txt      |  127 +++++++++++++
 Documentation/devicetree/bindings/net/mdio-mux.txt |  136 ++++++++++++++
 drivers/net/phy/Kconfig                            |   18 ++
 drivers/net/phy/Makefile                           |    2 +
 drivers/net/phy/mdio-mux-gpio.c                    |  142 +++++++++++++++
 drivers/net/phy/mdio-mux.c                         |  192 ++++++++++++++++++++
 drivers/net/phy/mdio_bus.c                         |   32 ++++
 drivers/of/of_mdio.c                               |    2 +
 include/linux/mdio-mux.h                           |   21 ++
 include/linux/of_mdio.h                            |    2 +
 10 files changed, 674 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/mdio-mux-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/net/mdio-mux.txt
 create mode 100644 drivers/net/phy/mdio-mux-gpio.c
 create mode 100644 drivers/net/phy/mdio-mux.c
 create mode 100644 include/linux/mdio-mux.h

-- 
1.7.2.3

^ permalink raw reply

* Re: [PATCH v3 1/3] netdev/of/phy: New function: of_mdio_find_bus().
From: David Daney @ 2012-04-17 17:27 UTC (permalink / raw)
  To: Grant Likely, Rob Herring, devicetree-discuss, David S. Miller,
	netdev
  Cc: linux-kernel, linux-mips, afleming, galak, David Daney
In-Reply-To: <1334624608-26667-2-git-send-email-ddaney.cavm@gmail.com>

On 04/16/2012 06:03 PM, David Daney wrote:
> From: David Daney<david.daney@cavium.com>
>
> Add of_mdio_find_bus() which allows an mii_bus to be located given its
> associated the device tree node.
>
> This is needed by the follow-on patch to add a driver for MDIO bus
> multiplexers.
>
> The of_mdiobus_register() function is modified so that the device tree
> node is recorded in the mii_bus.  Then we can find it again by
> iterating over all mdio_bus_class devices.
>
> Because the OF device tree has now become an integral part of the
> kernel, this can live in mdio_bus.c (which contains the needed
> mdio_bus_class structure) instead of of_mdio.c.
>
> Signed-off-by: David Daney<david.daney@cavium.com>
> Cc: Grant Likely<grant.likely@secretlab.ca>
> Cc: "David S. Miller"<davem@davemloft.net>
> ---
>   drivers/net/phy/mdio_bus.c |   32 ++++++++++++++++++++++++++++++++
>   drivers/of/of_mdio.c       |    2 ++
>   include/linux/of_mdio.h    |    2 ++
>   3 files changed, 36 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> index 8985cc6..46e7dc5 100644
> --- a/drivers/net/phy/mdio_bus.c
> +++ b/drivers/net/phy/mdio_bus.c
> @@ -88,6 +88,38 @@ static struct class mdio_bus_class = {
>   	.dev_release	= mdiobus_release,
>   };
>
> +#ifdef CONFIG_OF_MDIO
> +/* Helper function for of_phy_find_device */

This comment is incorrect.  I will resend the set with this cleaned up.

David Daney

> +static int of_mii_bus_match(struct device *dev, void *mii_bus_np)
> +{
> +	return dev->of_node == mii_bus_np;
> +}
> +/**
> + * of_mdio_find_bus - Given an mii_bus node, find the mii_bus.
> + * @mdio_np: Pointer to the mii_bus.
> + *
> + * Returns a pointer to the mii_bus, or NULL if none found.
> + *
> + * Because the association of a device_node and mii_bus is made via
> + * of_mdiobus_register(), the mii_bus cannot be found before it is
> + * registered with of_mdiobus_register().
> + *
> + */
> +struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np)
> +{
[...]

^ permalink raw reply

* Re: [PATCH] net: filter: Just In Time compiler for sparc
From: Eric Dumazet @ 2012-04-17 17:23 UTC (permalink / raw)
  To: Jan Ceuleers; +Cc: David Miller, netdev, sparclinux
In-Reply-To: <4F8DA21E.9020809@computer.org>

On Tue, 2012-04-17 at 19:02 +0200, Jan Ceuleers wrote:
> David Miller wrote:
> >
> > Signed-off-by: David S. Miller<davem@davemloft.net>
> > ---
> >
> > I think I'll commit this to net-next so it's easier to track
> > any BPF changes we make there.
> 
> I hate to nitpick; see below.
> 
> > +	bl	bpf_slow_path_word_neg
> > +	 nop
> > +	.globl	bpf_jit_load_word_positive_offset
> > +bpf_jit_load_word_positive_offset:
> > +	sub	r_HEADLEN, r_OFF, r_TMP
> > +	cmp	r_TMP, 3
> > +	ble	bpf_slow_path_word
> > +	 add	r_SKB_DATA, r_OFF, r_TMP
> > +	andcc	r_TMP, 3, %g0
> > +	bne	load_word_unaligned
> > +	 nop
> > +	retl
> > +	 ld	[r_SKB_DATA + r_OFF], r_A
> 
> There is inconsistent spacing in multiple places; a few examples shown 
> above.

Thats ok, after a branch : Delay slot





^ permalink raw reply

* Re: [BUG] ixgbe: something wrong with queue selection ?
From: Ben Hutchings @ 2012-04-17 17:07 UTC (permalink / raw)
  To: John Fastabend
  Cc: Alexander Duyck, jeffrey.t.kirsher, Eric Dumazet,
	Skidmore, Donald C, Greg Rose, Jesse Brandeburg, netdev
In-Reply-To: <4F8D9C91.5010001@intel.com>

On Tue, 2012-04-17 at 09:38 -0700, John Fastabend wrote:
[...]
> We could consider using sk_tx_queue_get(sk) in select_queue or better
> yet use XPS and initialize it at sw init time. I think this would work
> nicely and finally remove the select_queue() logic I just haven't got
> to it yet. Hoping to get there the next few weeks.

Anything like <http://thread.gmane.org/gmane.linux.network/186700/>?

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* Re: [PATCH] net: filter: Just In Time compiler for sparc
From: Jan Ceuleers @ 2012-04-17 17:02 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, sparclinux
In-Reply-To: <20120416.225823.1395194623649559124.davem@davemloft.net>

David Miller wrote:
>
> Signed-off-by: David S. Miller<davem@davemloft.net>
> ---
>
> I think I'll commit this to net-next so it's easier to track
> any BPF changes we make there.

I hate to nitpick; see below.

> +	bl	bpf_slow_path_word_neg
> +	 nop
> +	.globl	bpf_jit_load_word_positive_offset
> +bpf_jit_load_word_positive_offset:
> +	sub	r_HEADLEN, r_OFF, r_TMP
> +	cmp	r_TMP, 3
> +	ble	bpf_slow_path_word
> +	 add	r_SKB_DATA, r_OFF, r_TMP
> +	andcc	r_TMP, 3, %g0
> +	bne	load_word_unaligned
> +	 nop
> +	retl
> +	 ld	[r_SKB_DATA + r_OFF], r_A

There is inconsistent spacing in multiple places; a few examples shown 
above.

KR, Jan

^ permalink raw reply

* Re: In tree firmware not kept up to date
From: Michael Chan @ 2012-04-17 17:01 UTC (permalink / raw)
  To: Ian Campbell
  Cc: netdev, linux-kernel, Ian Jackson, David Miller, Ben Hutchings
In-Reply-To: <1334675504.23948.99.camel@zakaz.uk.xensource.com>

On Tue, 2012-04-17 at 16:11 +0100, Ian Campbell wrote: 
> Hello,
> 
> What is the current policy wrt keeping the in-tree firmware updated
> alongside the drivers?
> 
> I use CONFIG_FIRMWARE_IN_KERNEL but a couple of times[0] recently this
> has failed me because the in-tree bnx2 driver requires
> bnx2/bnx2-mips-09-6.2.1b.fw but the tree contains
> firmware/bnx2/bnx2-mips-09-6.2.1a.fw.ihex and not 6.2.1b.
> 
> Is it a bug that bnx2 was updated without adding the new firmware or is
> a bug that the obsolete firmware is still sitting in the tree?

We probably should remove the obsolete firmware in the kernel tree.  All
the firmware files to support old and new kernels are here:

git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git

^ permalink raw reply

* Re: [PATCH] set fake_rtable's dst to NULL to avoid kernel Oops.
From: Eric Dumazet @ 2012-04-17 16:48 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Peter Huang (Peng), 'David S. Miller', netdev,
	linux-kernel, ctrix+debianbugs, peter.huangpeng, harry.majun
In-Reply-To: <20120417085248.3e642294@nehalam.linuxnetplumber.net>

On Tue, 2012-04-17 at 08:52 -0700, Stephen Hemminger wrote:

> This check seems like a disaster waiting to happen when the next
> change to DST table happens.

Please Peter Document this, adding a new DST_FAKE_RTABLE flag

#define DST_FAKE_RTABLE DST_NOPEER

or just use a bit, we have plenty of them available.

^ permalink raw reply

* Re: [BUG] ixgbe: something wrong with queue selection ?
From: Eric Dumazet @ 2012-04-17 16:46 UTC (permalink / raw)
  To: Alexander Duyck
  Cc: jeffrey.t.kirsher, Skidmore, Donald C, Greg Rose, John Fastabend,
	Jesse Brandeburg, netdev
In-Reply-To: <4F8D93E1.9090000@intel.com>

On Tue, 2012-04-17 at 09:01 -0700, Alexander Duyck wrote:
> On 04/17/2012 02:16 AM, Jeff Kirsher wrote:
> > On Tue, 2012-04-17 at 11:06 +0200, Eric Dumazet wrote:
> >> Hi guys
> >>
> >> I have bad feelings with ixgbe and its multiqueue selection.
> >>
> >> On a quad core machine (Q6600), I get lots of reorderings on a single
> >> TCP stream.
> >>
> >>
> >> Apparently packets happily spread on all available queues, instead of a
> >> single one.
> >>
> >> This defeats GRO at receiver and TCP performance is really bad.
> >>
> >> # ethtool -S eth5|egrep "x_queue_[0123]_packets" ; taskset 1 netperf -H
> >> 192.168.99.1 ; ethtool -S eth5|egrep "x_queue_[0123]_packets"
> >>      tx_queue_0_packets: 24
> >>      tx_queue_1_packets: 26
> >>      tx_queue_2_packets: 32
> >>      tx_queue_3_packets: 16
> >>      rx_queue_0_packets: 11
> >>      rx_queue_1_packets: 47
> >>      rx_queue_2_packets: 27
> >>      rx_queue_3_packets: 22
> >> MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to
> >> 192.168.99.1 (192.168.99.1) port 0 AF_INET
> >> Recv   Send    Send                          
> >> Socket Socket  Message  Elapsed              
> >> Size   Size    Size     Time     Throughput  
> >> bytes  bytes   bytes    secs.    10^6bits/sec  
> >>
> >>  87380  16384  16384    10.00    3866.43   
> >>      tx_queue_0_packets: 1653201
> >>      tx_queue_1_packets: 608000
> >>      tx_queue_2_packets: 541382
> >>      tx_queue_3_packets: 536543
> >>      rx_queue_0_packets: 434703
> >>      rx_queue_1_packets: 137444
> >>      rx_queue_2_packets: 131023
> >>      rx_queue_3_packets: 128407
> >>
> >> # ip ro get 192.168.99.1
> >> 192.168.99.1 dev eth5  src 192.168.99.2 
> >>     cache  ipid 0x438b rtt 4ms rttvar 4ms cwnd 57 reordering 127
> >>
> >> # lspci -v -s 02:00.0
> >> 02:00.0 Ethernet controller: Intel Corporation 82599EB 10-Gigabit
> >> SFI/SFP+ Network Connection (rev 01)
> >> 	Subsystem: Intel Corporation Ethernet Server Adapter X520-2
> >> 	Flags: bus master, fast devsel, latency 0, IRQ 16
> >> 	Memory at f1100000 (64-bit, prefetchable) [size=512K]
> >> 	I/O ports at b000 [size=32]
> >> 	Memory at f1200000 (64-bit, prefetchable) [size=16K]
> >> 	Capabilities: [40] Power Management version 3
> >> 	Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
> >> 	Capabilities: [70] MSI-X: Enable+ Count=64 Masked-
> >> 	Capabilities: [a0] Express Endpoint, MSI 00
> >> 	Capabilities: [100] Advanced Error Reporting
> >> 	Capabilities: [140] Device Serial Number 00-1b-21-ff-ff-4a-fe-54
> >> 	Capabilities: [150] Alternative Routing-ID Interpretation (ARI)
> >> 	Capabilities: [160] Single Root I/O Virtualization (SR-IOV)
> >> 	Kernel driver in use: ixgbe
> >> 	Kernel modules: ixgbe
> >>
> >>
> > Adding Don Skidmore and Alex Duyck...
> This is probably the result of ATR and the load balancer on the system. 
> What is likely happening is that the netperf process is getting moved
> from CPU to CPU, and this is causing the transmit queue to change.  Once
> this happens the ATR will cause the receive queue to change in order to
> follow the transmitting process.
> 
> One thing you might try is using the "-T" option in netperf to see if
> the behaviour occurs if the process is bound to a specific CPU.  Another
> thing you might try would be to disable ATR by enabling ntuple.  You
> should be able to do that with  "ethtool -K eth5 ntuple on".

I used taskset 1 netperf, to force netperf running on cpu0.

Problem is incoming ACKs seem to be spreaded, so TCP stack might run on
all cpus.

^ permalink raw reply

* Re: [BUG] ixgbe: something wrong with queue selection ?
From: John Fastabend @ 2012-04-17 16:38 UTC (permalink / raw)
  To: Alexander Duyck
  Cc: jeffrey.t.kirsher, Eric Dumazet, Skidmore, Donald C, Greg Rose,
	Jesse Brandeburg, netdev
In-Reply-To: <4F8D93E1.9090000@intel.com>

On 4/17/2012 9:01 AM, Alexander Duyck wrote:
> On 04/17/2012 02:16 AM, Jeff Kirsher wrote:
>> On Tue, 2012-04-17 at 11:06 +0200, Eric Dumazet wrote:
>>> Hi guys
>>>
>>> I have bad feelings with ixgbe and its multiqueue selection.
>>>
>>> On a quad core machine (Q6600), I get lots of reorderings on a single
>>> TCP stream.
>>>
>>>
>>> Apparently packets happily spread on all available queues, instead of a
>>> single one.
>>>
>>> This defeats GRO at receiver and TCP performance is really bad.
>>>
>>> # ethtool -S eth5|egrep "x_queue_[0123]_packets" ; taskset 1 netperf -H
>>> 192.168.99.1 ; ethtool -S eth5|egrep "x_queue_[0123]_packets"
>>>      tx_queue_0_packets: 24
>>>      tx_queue_1_packets: 26
>>>      tx_queue_2_packets: 32
>>>      tx_queue_3_packets: 16
>>>      rx_queue_0_packets: 11
>>>      rx_queue_1_packets: 47
>>>      rx_queue_2_packets: 27
>>>      rx_queue_3_packets: 22
>>> MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to
>>> 192.168.99.1 (192.168.99.1) port 0 AF_INET
>>> Recv   Send    Send                          
>>> Socket Socket  Message  Elapsed              
>>> Size   Size    Size     Time     Throughput  
>>> bytes  bytes   bytes    secs.    10^6bits/sec  
>>>
>>>  87380  16384  16384    10.00    3866.43   
>>>      tx_queue_0_packets: 1653201
>>>      tx_queue_1_packets: 608000
>>>      tx_queue_2_packets: 541382
>>>      tx_queue_3_packets: 536543
>>>      rx_queue_0_packets: 434703
>>>      rx_queue_1_packets: 137444
>>>      rx_queue_2_packets: 131023
>>>      rx_queue_3_packets: 128407
>>>
>>> # ip ro get 192.168.99.1
>>> 192.168.99.1 dev eth5  src 192.168.99.2 
>>>     cache  ipid 0x438b rtt 4ms rttvar 4ms cwnd 57 reordering 127
>>>
>>> # lspci -v -s 02:00.0
>>> 02:00.0 Ethernet controller: Intel Corporation 82599EB 10-Gigabit
>>> SFI/SFP+ Network Connection (rev 01)
>>> 	Subsystem: Intel Corporation Ethernet Server Adapter X520-2
>>> 	Flags: bus master, fast devsel, latency 0, IRQ 16
>>> 	Memory at f1100000 (64-bit, prefetchable) [size=512K]
>>> 	I/O ports at b000 [size=32]
>>> 	Memory at f1200000 (64-bit, prefetchable) [size=16K]
>>> 	Capabilities: [40] Power Management version 3
>>> 	Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
>>> 	Capabilities: [70] MSI-X: Enable+ Count=64 Masked-
>>> 	Capabilities: [a0] Express Endpoint, MSI 00
>>> 	Capabilities: [100] Advanced Error Reporting
>>> 	Capabilities: [140] Device Serial Number 00-1b-21-ff-ff-4a-fe-54
>>> 	Capabilities: [150] Alternative Routing-ID Interpretation (ARI)
>>> 	Capabilities: [160] Single Root I/O Virtualization (SR-IOV)
>>> 	Kernel driver in use: ixgbe
>>> 	Kernel modules: ixgbe
>>>
>>>
>> Adding Don Skidmore and Alex Duyck...
> This is probably the result of ATR and the load balancer on the system. 
> What is likely happening is that the netperf process is getting moved
> from CPU to CPU, and this is causing the transmit queue to change.  Once
> this happens the ATR will cause the receive queue to change in order to
> follow the transmitting process.
> 
> One thing you might try is using the "-T" option in netperf to see if
> the behaviour occurs if the process is bound to a specific CPU.  Another
> thing you might try would be to disable ATR by enabling ntuple.  You
> should be able to do that with  "ethtool -K eth5 ntuple on".
> 
> Thanks,
> 
> Alex
> --

We could consider using sk_tx_queue_get(sk) in select_queue or better
yet use XPS and initialize it at sw init time. I think this would work
nicely and finally remove the select_queue() logic I just haven't got
to it yet. Hoping to get there the next few weeks.

.John

^ permalink raw reply

* Re: [PATCH net-next 0/7] Pull request for 'davem-next.r8169' branch
From: David Miller @ 2012-04-17 16:04 UTC (permalink / raw)
  To: romieu; +Cc: netdev
In-Reply-To: <1334659751.git.romieu@fr.zoreil.com>

From: Francois Romieu <romieu@fr.zoreil.com>
Date: Tue, 17 Apr 2012 12:59:34 +0200

> Please pull from branch 'davem-next.r8169' in repository
> 
> git://violet.fr.zoreil.com/romieu/linux davem-next.r8169
> 
> to get the changes below.

Pulled, thanks Francois.

^ permalink raw reply

* Re: [BUG] ixgbe: something wrong with queue selection ?
From: Alexander Duyck @ 2012-04-17 16:01 UTC (permalink / raw)
  To: jeffrey.t.kirsher
  Cc: Eric Dumazet, Skidmore, Donald C, Greg Rose, John Fastabend,
	Jesse Brandeburg, netdev
In-Reply-To: <1334654187.2696.2.camel@jtkirshe-mobl>

On 04/17/2012 02:16 AM, Jeff Kirsher wrote:
> On Tue, 2012-04-17 at 11:06 +0200, Eric Dumazet wrote:
>> Hi guys
>>
>> I have bad feelings with ixgbe and its multiqueue selection.
>>
>> On a quad core machine (Q6600), I get lots of reorderings on a single
>> TCP stream.
>>
>>
>> Apparently packets happily spread on all available queues, instead of a
>> single one.
>>
>> This defeats GRO at receiver and TCP performance is really bad.
>>
>> # ethtool -S eth5|egrep "x_queue_[0123]_packets" ; taskset 1 netperf -H
>> 192.168.99.1 ; ethtool -S eth5|egrep "x_queue_[0123]_packets"
>>      tx_queue_0_packets: 24
>>      tx_queue_1_packets: 26
>>      tx_queue_2_packets: 32
>>      tx_queue_3_packets: 16
>>      rx_queue_0_packets: 11
>>      rx_queue_1_packets: 47
>>      rx_queue_2_packets: 27
>>      rx_queue_3_packets: 22
>> MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to
>> 192.168.99.1 (192.168.99.1) port 0 AF_INET
>> Recv   Send    Send                          
>> Socket Socket  Message  Elapsed              
>> Size   Size    Size     Time     Throughput  
>> bytes  bytes   bytes    secs.    10^6bits/sec  
>>
>>  87380  16384  16384    10.00    3866.43   
>>      tx_queue_0_packets: 1653201
>>      tx_queue_1_packets: 608000
>>      tx_queue_2_packets: 541382
>>      tx_queue_3_packets: 536543
>>      rx_queue_0_packets: 434703
>>      rx_queue_1_packets: 137444
>>      rx_queue_2_packets: 131023
>>      rx_queue_3_packets: 128407
>>
>> # ip ro get 192.168.99.1
>> 192.168.99.1 dev eth5  src 192.168.99.2 
>>     cache  ipid 0x438b rtt 4ms rttvar 4ms cwnd 57 reordering 127
>>
>> # lspci -v -s 02:00.0
>> 02:00.0 Ethernet controller: Intel Corporation 82599EB 10-Gigabit
>> SFI/SFP+ Network Connection (rev 01)
>> 	Subsystem: Intel Corporation Ethernet Server Adapter X520-2
>> 	Flags: bus master, fast devsel, latency 0, IRQ 16
>> 	Memory at f1100000 (64-bit, prefetchable) [size=512K]
>> 	I/O ports at b000 [size=32]
>> 	Memory at f1200000 (64-bit, prefetchable) [size=16K]
>> 	Capabilities: [40] Power Management version 3
>> 	Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
>> 	Capabilities: [70] MSI-X: Enable+ Count=64 Masked-
>> 	Capabilities: [a0] Express Endpoint, MSI 00
>> 	Capabilities: [100] Advanced Error Reporting
>> 	Capabilities: [140] Device Serial Number 00-1b-21-ff-ff-4a-fe-54
>> 	Capabilities: [150] Alternative Routing-ID Interpretation (ARI)
>> 	Capabilities: [160] Single Root I/O Virtualization (SR-IOV)
>> 	Kernel driver in use: ixgbe
>> 	Kernel modules: ixgbe
>>
>>
> Adding Don Skidmore and Alex Duyck...
This is probably the result of ATR and the load balancer on the system. 
What is likely happening is that the netperf process is getting moved
from CPU to CPU, and this is causing the transmit queue to change.  Once
this happens the ATR will cause the receive queue to change in order to
follow the transmitting process.

One thing you might try is using the "-T" option in netperf to see if
the behaviour occurs if the process is bound to a specific CPU.  Another
thing you might try would be to disable ATR by enabling ntuple.  You
should be able to do that with  "ethtool -K eth5 ntuple on".

Thanks,

Alex

^ permalink raw reply

* Re: [PATCH] set fake_rtable's dst to NULL to avoid kernel Oops.
From: Stephen Hemminger @ 2012-04-17 15:52 UTC (permalink / raw)
  To: Peter Huang (Peng)
  Cc: 'David S. Miller', netdev, eric.dumazet, linux-kernel,
	ctrix+debianbugs, peter.huangpeng, harry.majun
In-Reply-To: <000001cd1c62$75d10de0$617329a0$%huangpeng@huawei.com>

On Tue, 17 Apr 2012 14:22:26 +0800
"Peter Huang (Peng)" <peter.huangpeng@huawei.com> wrote:

> When bridge is deleted before tap/vif device's delete, kernel may encounter an oops because of NULL reference to fake_rtable's dst.
> Set fake_rtable's dst to NULL before sending packets out can solve this problem.
> 
> 
> Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
> Signed-off-by: Peter Huang <peter.huangpeng@huawei.com>
> ---
> include/linux/netfilter_bridge.h |    8 ++++++++
>  net/bridge/br_forward.c          |    1 +
>  net/bridge/br_netfilter.c        |    6 +-----
>  3 files changed, 10 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h
> index 0ddd161..70744fe 100644
> --- a/include/linux/netfilter_bridge.h
> +++ b/include/linux/netfilter_bridge.h
> @@ -104,9 +104,17 @@ struct bridge_skb_cb {
>  	} daddr;
>  };
>  
> +static inline void br_drop_fake_rtable(struct sk_buff *skb) {
> +	struct dst_entry *dst = skb_dst(skb);
> +	/* abuse fact that only fake_rtable has DST_NOPEER set */
> +	if (dst && (dst->flags & DST_NOPEER))
> +		skb_dst_drop(skb);
> +}

This check seems like a disaster waiting to happen when the next
change to DST table happens.

^ permalink raw reply


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