Netdev List
 help / color / mirror / Atom feed
* Re: TCP_MAXSEG vs TCP/generic segmentation offload (tso/gso)
From: Eric Dumazet @ 2010-11-25 15:18 UTC (permalink / raw)
  To: Niels Möller; +Cc: linux-kernel, netdev
In-Reply-To: <nnaakxqw1u.fsf@stalhein.lysator.liu.se>

Le jeudi 25 novembre 2010 à 16:09 +0100, Niels Möller a écrit :
> Eric Dumazet <eric.dumazet@gmail.com> writes:
> 
> > So... there is no 'bug', unless you trust too much tcpdump output.
> 
> I really expected tcpdump -e to display the actual values in the link
> layer header, including the correct frame size. It's more than a bit
> confusing if that is not the case...
> 
> In the future, I will try to remember to always run tcpdump on a network
> node which (i) is different from the sending one, and (ii) has GRO
> disabled (and hence will discard packets if it has trouble processing
> them all, rather than coalesce them).
> 

Just disable GSO and TSO on sending machine, then tcpdump will show you
individual frames.


> What about the TCP_MAXSEG socket option, should that work? From a quick
> look at driver source code, I could only see the handling of the
> per-interface MTU, no per-socket segment size.

TCP_MAXSEG is certainly not handled in driver layer, but TCP layer.

/* If user gave his TCP_MAXSEG, record it to clamp */
if (tp->rx_opt.user_mss)
	tp->rx_opt.mss_clamp = tp->rx_opt.user_mss;

I believe TCP_MAXSEG is working fine, but GRO/GSO dont care at all :
They coalesce frames whatever their size is.

^ permalink raw reply

* Re: TCP_MAXSEG vs TCP/generic segmentation offload (tso/gso)
From: Niels Möller @ 2010-11-25 15:09 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: linux-kernel, netdev
In-Reply-To: <1290695253.2858.336.camel@edumazet-laptop>

Eric Dumazet <eric.dumazet@gmail.com> writes:

> So... there is no 'bug', unless you trust too much tcpdump output.

I really expected tcpdump -e to display the actual values in the link
layer header, including the correct frame size. It's more than a bit
confusing if that is not the case...

In the future, I will try to remember to always run tcpdump on a network
node which (i) is different from the sending one, and (ii) has GRO
disabled (and hence will discard packets if it has trouble processing
them all, rather than coalesce them).

What about the TCP_MAXSEG socket option, should that work? From a quick
look at driver source code, I could only see the handling of the
per-interface MTU, no per-socket segment size.

Thanks for the quick reply,
/Niels

-- 
Niels Möller. PGP-encrypted email is preferred. Keyid C0B98E26.
Internet email is subject to wholesale government surveillance.

^ permalink raw reply

* Re: [PATCHv2] hso: fix disable_net
From: Johan Hovold @ 2010-11-25 15:03 UTC (permalink / raw)
  To: Filip Aben; +Cc: davem, linux-usb, netdev, pki
In-Reply-To: <1290692450.2112.21.camel@filip-linux>

On Thu, Nov 25, 2010 at 2:40 PM, Filip Aben <f.aben@option.com> wrote:
> The HSO driver incorrectly creates a serial device instead of a net
> device when disable_net is set. It shouldn't create anything for the
> network interface.
>
> Signed-off-by: Filip Aben <f.aben@option.com>

Please add the appropriate Reported-by-tags as well, e.g.

Reported-by: Piotr Isajew <pki@ex.com.pl>
Reported-by: Johan Hovold <jhovold@gmail.com>

As this patch is meant to fix a driver crash, perhaps it should be marked for
stable as well?

Thanks,
Johan

^ permalink raw reply

* Re: [PATCH 2/2] The new jhash implementation
From: Jozsef Kadlecsik @ 2010-11-25 14:41 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: linux-kernel, netdev, netfilter-devel, Linus Torvalds,
	Rusty Russell
In-Reply-To: <1290692943.2858.303.camel@edumazet-laptop>

On Thu, 25 Nov 2010, Eric Dumazet wrote:

> I agree jhash() should be not be inlined.
> 
> I am not sure for other variants.

Yeah, I have got the same feelings. I decided to un-inline all of them 
because that way the internal macros are not exposed at all.
 
> > +u32 jhash(const void *key, u32 length, u32 initval)
> > +{
> > +	u32 a, b, c;
> > +	const u8 *k = key;
> > +
> > +	/* Set up the internal state */
> > +	a = b = c = JHASH_INITVAL + length + initval;
> > +
> > +	/* All but the last block: affect some 32 bits of (a,b,c) */
> > +	while (length > 12) {
> > +		a += k[0] + ((u32)k[1]<<8) + ((u32)k[2]<<16) + ((u32)k[3]<<24);
> 
> disassembly code on x86_32 for the previous line :
> 
>   26:	66 90                	xchg   %ax,%ax
>   28:	0f b6 72 01          	movzbl 0x1(%edx),%esi
>   2c:	0f b6 4a 02          	movzbl 0x2(%edx),%ecx
>   30:	c1 e6 08             	shl    $0x8,%esi
>   33:	c1 e1 10             	shl    $0x10,%ecx
>   36:	8d 0c 0e             	lea    (%esi,%ecx,1),%ecx
>   39:	0f b6 32             	movzbl (%edx),%esi
>   3c:	8d 34 31             	lea    (%ecx,%esi,1),%esi
>   3f:	0f b6 4a 03          	movzbl 0x3(%edx),%ecx
>   43:	c1 e1 18             	shl    $0x18,%ecx
>   46:	8d 0c 0e             	lea    (%esi,%ecx,1),%ecx
> 
> or (CONFIG_CC_OPTIMIZE_FOR_SIZE=y) :
> 
>   1b:	0f b6 7b 01          	movzbl 0x1(%ebx),%edi
>   1f:	c1 e7 08             	shl    $0x8,%edi
>   22:	89 7d f0             	mov    %edi,-0x10(%ebp)
>   25:	0f b6 7b 02          	movzbl 0x2(%ebx),%edi
>   29:	c1 e7 10             	shl    $0x10,%edi
>   2c:	03 7d f0             	add    -0x10(%ebp),%edi
>   2f:	89 7d f0             	mov    %edi,-0x10(%ebp)
>   32:	0f b6 3b             	movzbl (%ebx),%edi
>   35:	03 7d f0             	add    -0x10(%ebp),%edi
>   38:	89 7d f0             	mov    %edi,-0x10(%ebp)
>   3b:	0f b6 7b 03          	movzbl 0x3(%ebx),%edi
>   3f:	c1 e7 18             	shl    $0x18,%edi
>   42:	03 7d f0             	add    -0x10(%ebp),%edi
> 
> 
> I suggest :
> 
> #include <linux/unaligned/packed_struct.h>
> ...
> 	a += __get_unaligned_cpu32(k);
> 	b += __get_unaligned_cpu32(k+4);
> 	c += __get_unaligned_cpu32(k+8);
> 
> Fits nicely in registers.

Good idea, thanks!

Here follows the updated second patch:

The current jhash.h implements the lookup2() hash function by Bob Jenkins.
However, lookup2() is outdated as Bob wrote a new hash function called
lookup3(). The new hash function

- mixes better than lookup2(): it passes the check that every input bit
  changes every output bit 50% of the time, while lookup2() failed it.
- performs better: compiled with -O2 on Core2 Duo, lookup3() 20-40% faster
  than lookup2() depending on the key length.

The patch replaces the lookup2() implementation of the 'jhash*'
functions with that of lookup3().

You can read a longer comparison of the two and other hash functions at
http://burtleburtle.net/bob/hash/doobs.html.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
---
 include/linux/jhash.h |  136 +++----------------------------------------
 lib/Makefile          |    2 +-
 lib/jhash.c           |  154 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 163 insertions(+), 129 deletions(-)
 create mode 100644 lib/jhash.c

diff --git a/include/linux/jhash.h b/include/linux/jhash.h
index ced1159..ca69ac3 100644
--- a/include/linux/jhash.h
+++ b/include/linux/jhash.h
@@ -1,134 +1,14 @@
 #ifndef _LINUX_JHASH_H
 #define _LINUX_JHASH_H
 
-/* jhash.h: Jenkins hash support.
- *
- * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
- *
- * http://burtleburtle.net/bob/hash/
- *
- * These are the credits from Bob's sources:
- *
- * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
- * hash(), hash2(), hash3, and mix() are externally useful functions.
- * Routines to test the hash are included if SELF_TEST is defined.
- * You can use this free for any purpose.  It has no warranty.
- *
- * Copyright (C) 2003 David S. Miller (davem@redhat.com)
- *
- * I've modified Bob's hash to be useful in the Linux kernel, and
- * any bugs present are surely my fault.  -DaveM
- */
-
-/* NOTE: Arguments are modified. */
-#define __jhash_mix(a, b, c) \
-{ \
-  a -= b; a -= c; a ^= (c>>13); \
-  b -= c; b -= a; b ^= (a<<8); \
-  c -= a; c -= b; c ^= (b>>13); \
-  a -= b; a -= c; a ^= (c>>12);  \
-  b -= c; b -= a; b ^= (a<<16); \
-  c -= a; c -= b; c ^= (b>>5); \
-  a -= b; a -= c; a ^= (c>>3);  \
-  b -= c; b -= a; b ^= (a<<10); \
-  c -= a; c -= b; c ^= (b>>15); \
-}
-
-/* The golden ration: an arbitrary value */
-#define JHASH_GOLDEN_RATIO	0x9e3779b9
-
-/* The most generic version, hashes an arbitrary sequence
- * of bytes.  No alignment or length assumptions are made about
- * the input key.
- */
-static inline u32 jhash(const void *key, u32 length, u32 initval)
-{
-	u32 a, b, c, len;
-	const u8 *k = key;
-
-	len = length;
-	a = b = JHASH_GOLDEN_RATIO;
-	c = initval;
-
-	while (len >= 12) {
-		a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24));
-		b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24));
-		c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24));
-
-		__jhash_mix(a,b,c);
-
-		k += 12;
-		len -= 12;
-	}
-
-	c += length;
-	switch (len) {
-	case 11: c += ((u32)k[10]<<24);
-	case 10: c += ((u32)k[9]<<16);
-	case 9 : c += ((u32)k[8]<<8);
-	case 8 : b += ((u32)k[7]<<24);
-	case 7 : b += ((u32)k[6]<<16);
-	case 6 : b += ((u32)k[5]<<8);
-	case 5 : b += k[4];
-	case 4 : a += ((u32)k[3]<<24);
-	case 3 : a += ((u32)k[2]<<16);
-	case 2 : a += ((u32)k[1]<<8);
-	case 1 : a += k[0];
-	};
-
-	__jhash_mix(a,b,c);
-
-	return c;
-}
-
-/* A special optimized version that handles 1 or more of u32s.
- * The length parameter here is the number of u32s in the key.
- */
-static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
-{
-	u32 a, b, c, len;
-
-	a = b = JHASH_GOLDEN_RATIO;
-	c = initval;
-	len = length;
-
-	while (len >= 3) {
-		a += k[0];
-		b += k[1];
-		c += k[2];
-		__jhash_mix(a, b, c);
-		k += 3; len -= 3;
-	}
-
-	c += length * 4;
-
-	switch (len) {
-	case 2 : b += k[1];
-	case 1 : a += k[0];
-	};
-
-	__jhash_mix(a,b,c);
-
-	return c;
-}
-
-
-/* A special ultra-optimized versions that knows they are hashing exactly
- * 3, 2 or 1 word(s).
- *
- * NOTE: In particular the "c += length; __jhash_mix(a,b,c);" normally
- *       done at the end is not done here.
- */
-static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
-{
-	a += JHASH_GOLDEN_RATIO;
-	b += JHASH_GOLDEN_RATIO;
-	c += initval;
-
-	__jhash_mix(a, b, c);
-
-	return c;
-}
+/* Best hash sizes are of power of two */
+#define jhash_size(n)   ((u32)1<<(n))
+/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */
+#define jhash_mask(n)   (jhash_size(n)-1)
+
+extern u32 jhash(const void *key, u32 length, u32 initval);
+extern u32 jhash2(const u32 *k, u32 length, u32 initval);
+extern u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval);
 
 static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
 {
diff --git a/lib/Makefile b/lib/Makefile
index e6a3763..a1a4932 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -10,7 +10,7 @@ endif
 lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 rbtree.o radix-tree.o dump_stack.o \
 	 idr.o int_sqrt.o extable.o prio_tree.o \
-	 sha1.o irq_regs.o reciprocal_div.o argv_split.o \
+	 jhash.o sha1.o irq_regs.o reciprocal_div.o argv_split.o \
 	 proportions.o prio_heap.o ratelimit.o show_mem.o \
 	 is_single_threaded.o plist.o decompress.o flex_array.o
 
diff --git a/lib/jhash.c b/lib/jhash.c
new file mode 100644
index 0000000..538277b
--- /dev/null
+++ b/lib/jhash.c
@@ -0,0 +1,154 @@
+/* jhash.c: Jenkins hash support.
+ *
+ * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ *
+ * These are functions for producing 32-bit hashes for hash table lookup.
+ * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+ * are externally useful functions.  Routines to test the hash are included
+ * if SELF_TEST is defined.  You can use this free for any purpose.  It's in
+ * the public domain.  It has no warranty.
+ *
+ * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are my fault.
+ * Jozsef
+ */
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/unaligned/packed_struct.h>
+#include <linux/jhash.h>
+
+/* __jhash_mix -- mix 3 32-bit values reversibly. */
+#define __jhash_mix(a, b, c)			\
+{						\
+	a -= c;  a ^= rol32(c, 4);  c += b;	\
+	b -= a;  b ^= rol32(a, 6);  a += c;	\
+	c -= b;  c ^= rol32(b, 8);  b += a;	\
+	a -= c;  a ^= rol32(c, 16); c += b;	\
+	b -= a;  b ^= rol32(a, 19); a += c;	\
+	c -= b;  c ^= rol32(b, 4);  b += a;	\
+}
+
+/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
+#define __jhash_final(a, b, c)			\
+{						\
+	c ^= b; c -= rol32(b, 14);		\
+	a ^= c; a -= rol32(c, 11);		\
+	b ^= a; b -= rol32(a, 25);		\
+	c ^= b; c -= rol32(b, 16);		\
+	a ^= c; a -= rol32(c, 4);		\
+	b ^= a; b -= rol32(a, 14);		\
+	c ^= b; c -= rol32(b, 24);		\
+}
+
+/* An arbitrary initial parameter */
+#define JHASH_INITVAL		0xdeadbeef
+
+/* jhash - hash an arbitrary key
+ * @k: sequence of bytes as key
+ * @length: the length of the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * The generic version, hashes an arbitrary sequence of bytes.
+ * No alignment or length assumptions are made about the input key.
+ *
+ * Returns the hash value of the key. The result depends on endianness.
+ */
+u32 jhash(const void *key, u32 length, u32 initval)
+{
+	u32 a, b, c;
+	const u8 *k = key;
+
+	/* Set up the internal state */
+	a = b = c = JHASH_INITVAL + length + initval;
+
+	/* All but the last block: affect some 32 bits of (a,b,c) */
+	while (length > 12) {
+		a += __get_unaligned_cpu32(k);
+		b += __get_unaligned_cpu32(k + 4);
+		c += __get_unaligned_cpu32(k + 8);
+		__jhash_mix(a, b, c);
+		length -= 12;
+		k += 12;
+	}
+	/* Last block: affect all 32 bits of (c) */
+	/* All the case statements fall through */
+	switch (length) {
+	case 12: c += (u32)k[11]<<24;
+	case 11: c += (u32)k[10]<<16;
+	case 10: c += (u32)k[9]<<8;
+	case 9:  c += k[8];
+	case 8:  b += (u32)k[7]<<24;
+	case 7:  b += (u32)k[6]<<16;
+	case 6:  b += (u32)k[5]<<8;
+	case 5:  b += k[4];
+	case 4:  a += (u32)k[3]<<24;
+	case 3:  a += (u32)k[2]<<16;
+	case 2:  a += (u32)k[1]<<8;
+	case 1:  a += k[0];
+		 __jhash_final(a, b, c);
+	case 0: /* Nothing left to add */
+		break;
+	}
+
+	return c;
+}
+EXPORT_SYMBOL(jhash);
+
+/* jhash2 - hash an array of u32's
+ * @k: the key which must be an array of u32's
+ * @length: the number of u32's in the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * Returns the hash value of the key.
+ */
+u32 jhash2(const u32 *k, u32 length, u32 initval)
+{
+	u32 a, b, c;
+
+	/* Set up the internal state */
+	a = b = c = JHASH_INITVAL + (length<<2) + initval;
+
+	/* Handle most of the key */
+	while (length > 3) {
+		a += k[0];
+		b += k[1];
+		c += k[2];
+		__jhash_mix(a, b, c);
+		length -= 3;
+		k += 3;
+	}
+
+	/* Handle the last 3 u32's: all the case statements fall through */
+	switch (length) {
+	case 3: c += k[2];
+	case 2: b += k[1];
+	case 1: a += k[0];
+		__jhash_final(a, b, c);
+	case 0:	/* Nothing left to add */
+		break;
+	}
+
+	return c;
+}
+EXPORT_SYMBOL(jhash2);
+
+/* jhash_3words - hash exactly 3, 2 or 1 word(s) */
+u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
+{
+	a += JHASH_INITVAL;
+	b += JHASH_INITVAL;
+	c += initval;
+
+	__jhash_mix(a, b, c);
+
+	return c;
+}
+EXPORT_SYMBOL(jhash_3words);
-- 
1.7.0.4

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlec@mail.kfki.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : KFKI Research Institute for Particle and Nuclear Physics
          H-1525 Budapest 114, POB. 49, Hungary

^ permalink raw reply related

* Re: TCP_MAXSEG vs TCP/generic segmentation offload (tso/gso)
From: Eric Dumazet @ 2010-11-25 14:27 UTC (permalink / raw)
  To: Niels Möller; +Cc: linux-kernel, netdev
In-Reply-To: <nny68hqzzn.fsf@stalhein.lysator.liu.se>

Le jeudi 25 novembre 2010 à 14:44 +0100, Niels Möller a écrit :
> [ This is a slightly updated repost of a an October 21 mail to the
>   linux-net list. Any hints or advice appreciated. /Niels ]
> 

CC netdev

> I have been observing large ethernet packets when generating TCP traffic
> over a local ethernet, up to a bit over 20000 bytes, even though the
> interface MTU is 1500 bytes.
> 
> Furthermore, I tried to use setsockopt with TCP_MAXSEG to limit the TCP
> segment size further, to 1000 bytes, and that didn't have any effect.
> 
> When bugreporting a related problem to the debian kernel maintainers, I
> was told that the behaviour may be linked to the use of TCP segmentation
> offload (see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=600286).
> 
> Disabling TSO and GSO using ethtool solves both problems: Generated
> packets now are limited in size by both the interface MTU and the
> segment size set with setsockopt. (Except the atl1c driver, where
> ethtool -K eth0 tso off only results in a "Cannot set device tcp
> segmentation offload settings: Operation not supported").
> 
> Before I try to write proper bug reports on specific network drivers (I
> have seen problems with several network drivers on different machines,
> unfortunately using different linux versions), I would like to know:
> 
> 1. Is TCP_MAXSEG supposed to work at all with network drivers that do
>    tcp segmentation offload?
> 
> 2. If it is supposed to work, can someone give a rough sketch on how the
>    per-socket segment size, set with setsockopt(... TCP_MAXSEG,...), is
>    passed down to the driver and to the network hardware? I suspect it
>    ought to be passed with each "pseudo-packet" to be transmitted.
> 
> I have spent some time searching the documentation and the net for
> answers, without result, hence I'm posting to this list. I'm not
> subscribed, so please cc any replies.
> 
> (Regarding packets larger than the interface MTU, that seems clearly
> buggy to me, and I think I already know enough to be able to file proper
> bug reports. And in the atl1c driver, it appears to have been fixed
> between 1.0.0.1-NAPI and 1.0.1.0-NAPI).

GSO is a software technique. Same for GRO.

Physical frames are indeed 1500 bytes (on regular ethernet links)

tcpdump gives you the high level view, before segmentation done in lower
levels (by NIC itself or in linux stack) in Transmit path.

We also have GRO in receive path, able to coalesce several 1500 bytes
frames into a single one (if same tcp flow), so that overhead in stacks
is lowered (netfilter, IP stack, tcp stack, bridge, routing ...)

So... there is no 'bug', unless you trust too much tcpdump output.

^ permalink raw reply

* [PATCHv2 2/2] USB CDC NCM host driver
From: Alexey Orishko @ 2010-11-25 14:14 UTC (permalink / raw)
  To: gregkh, linux-usb
  Cc: netdev, oliver, yauheni.kaliuta, balbi, sjur.brandeland,
	Alexey Orishko
In-Reply-To: <1290694472-2680-1-git-send-email-alexey.orishko@stericsson.com>

The patch provides USB CDC NCM host driver support in the Linux Kernel.

Changes:
drivers/net/usb/cdc_ncm.c:
- initial submission of the CDC NCM host driver;
- verified on Intel 32/64 bit, Intel Atom, ST-Ericsson U8500 (ARM)
- throughput measured over 100 Mbits duplex;
- driver supports 16-bit NTB format only, but it is more than enough for
  transfers up to 64K;
- driver can handle up to 32 datagrams in received NTB;
- timer is used to collect several packets in Tx direction

drivers/net/usb/Kconfig:
- a new entry to compile CDC NCM host driver
drivers/net/usb/Makefile:
- a new entry to compile CDC NCM host driver

Signed-off-by: Alexey Orishko <alexey.orishko@stericsson.com>
---
Patch v2:
based on suggestion moved Changes to the comment log

 drivers/net/usb/Kconfig   |   19 +
 drivers/net/usb/Makefile  |    1 +
 drivers/net/usb/cdc_ncm.c | 1224 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1244 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/usb/cdc_ncm.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 52ffabe..6f600cc 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -196,6 +196,25 @@ config USB_NET_CDC_EEM
 	  IEEE 802 "local assignment" bit is set in the address, a "usbX"
 	  name is used instead.
 
+config USB_NET_CDC_NCM
+	tristate "CDC NCM support"
+	depends on USB_USBNET
+	default y
+	help
+	  This driver provides support for CDC NCM (Network Control Model
+	  Device USB Class Specification). The CDC NCM specification is
+	  available from <http://www.usb.org/>.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module.
+
+	  This driver should work with at least the following devices:
+	    * ST-Ericsson M700 LTE FDD/TDD Mobile Broadband Modem (ref. design)
+	    * ST-Ericsson M5730 HSPA+ Mobile Broadband Modem (reference design)
+	    * ST-Ericsson M570 HSPA+ Mobile Broadband Modem (reference design)
+	    * ST-Ericsson M343 HSPA Mobile Broadband Modem (reference design)
+	    * Ericsson F5521gw Mobile Broadband Module
+
 config USB_NET_DM9601
 	tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index a19b025..cac1703 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -26,4 +26,5 @@ obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o
 obj-$(CONFIG_USB_IPHETH)	+= ipheth.o
 obj-$(CONFIG_USB_SIERRA_NET)	+= sierra_net.o
 obj-$(CONFIG_USB_NET_CX82310_ETH)	+= cx82310_eth.o
+obj-$(CONFIG_USB_NET_CDC_NCM)	+= cdc_ncm.o
 
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
new file mode 100644
index 0000000..80755cd
--- /dev/null
+++ b/drivers/net/usb/cdc_ncm.c
@@ -0,0 +1,1224 @@
+/*
+ * cdc_ncm.c
+ *
+ * Copyright (C) ST-Ericsson 2010
+ * Contact: Alexey Orishko <alexey.orishko@stericsson.com>
+ * Original author: Hans Petter Selasky <hans.petter.selasky@stericsson.com>
+ *
+ * USB Host Driver for Network Control Model (NCM)
+ * http://www.usb.org/developers/devclass_docs/NCM10.zip
+ *
+ * The NCM encoding, decoding and initialization logic
+ * derives from FreeBSD 8.x. if_cdce.c and if_cdcereg.h
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose this file to be licensed under the terms
+ * of the GNU General Public License (GPL) Version 2 or the 2-clause
+ * BSD license listed below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/ctype.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+#include <linux/usb/usbnet.h>
+#include <linux/usb/cdc.h>
+
+#define	DRIVER_VERSION				"23-Nov-2010"
+
+/* CDC NCM subclass 3.2.1 */
+#define USB_CDC_NCM_NDP16_LENGTH_MIN		0x10
+
+/* Maximum NTB length */
+#define	CDC_NCM_NTB_MAX_SIZE_TX			16384	/* bytes */
+#define	CDC_NCM_NTB_MAX_SIZE_RX			16384	/* bytes */
+
+/* Minimum value for MaxDatagramSize, ch. 6.2.9 */
+#define	CDC_NCM_MIN_DATAGRAM_SIZE		1514	/* bytes */
+
+#define	CDC_NCM_MIN_TX_PKT			512	/* bytes */
+
+/* Default value for MaxDatagramSize */
+#define	CDC_NCM_MAX_DATAGRAM_SIZE		2048	/* bytes */
+
+/*
+ * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting
+ * the last NULL entry. Any additional datagrams in NTB would be discarded.
+ */
+#define	CDC_NCM_DPT_DATAGRAMS_MAX		32
+
+/* Restart the timer, if amount of datagrams is less than given value */
+#define	CDC_NCM_RESTART_TIMER_DATAGRAM_CNT	3
+
+/*
+ * We need to align the beginning of the IP packet (not the Ethernet framing).
+ * The following macro aligns the given offset to the given remainder
+ * and modulus, which must be power of two.
+ */
+#define	CDC_NCM_ALIGN(rem, offset, mod)	\
+	((long)(((long)(rem)) - ((long)((-(long)(offset)) & (-(long)(mod))))))
+
+/* The following macro defines the minimum header space */
+#define	CDC_NCM_MIN_HDR_SIZE \
+	(sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \
+	(CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
+
+struct connection_speed_change {
+	__le32	USBitRate; /* holds 3GPP downlink value, bits per second */
+	__le32	DSBitRate; /* holds 3GPP uplink value, bits per second */
+} __attribute__ ((packed));
+
+struct cdc_ncm_data {
+	struct usb_cdc_ncm_nth16 nth16;
+	struct usb_cdc_ncm_ndp16 ndp16;
+	struct usb_cdc_ncm_dpe16 dpe16[CDC_NCM_DPT_DATAGRAMS_MAX + 1];
+};
+
+struct cdc_ncm_ctx {
+	struct cdc_ncm_data rx_ncm;
+	struct cdc_ncm_data tx_ncm;
+	struct usb_cdc_ncm_ntb_parameters ncm_parm;
+	struct timer_list tx_timer;
+
+	const struct usb_cdc_ncm_desc *func_desc;
+	const struct usb_cdc_header_desc *header_desc;
+	const struct usb_cdc_union_desc *union_desc;
+	const struct usb_cdc_ether_desc *ether_desc;
+
+	struct net_device *netdev;
+	struct usb_device *udev;
+	struct usb_host_endpoint *in_ep;
+	struct usb_host_endpoint *out_ep;
+	struct usb_host_endpoint *status_ep;
+	struct usb_interface *intf;
+	struct usb_interface *control;
+	struct usb_interface *data;
+
+	struct sk_buff *tx_curr_skb;
+	struct sk_buff *tx_rem_skb;
+
+	spinlock_t mtx;
+
+	u32 tx_timer_pending;
+	u32 tx_curr_offset;
+	u32 tx_curr_last_offset;
+	u32 tx_curr_frame_num;
+	u32 rx_speed;
+	u32 tx_speed;
+	u32 rx_max;
+	u32 tx_max;
+	u32 max_datagram_size;
+	u16 tx_max_datagrams;
+	u16 tx_remainder;
+	u16 tx_modulus;
+	u16 tx_ndp_modulus;
+	u16 tx_seq;
+	u16 connected;
+	u8 data_claimed;
+	u8 control_claimed;
+};
+
+static void cdc_ncm_tx_timeout(unsigned long arg);
+static const struct driver_info cdc_ncm_info;
+static struct usb_driver cdc_ncm_driver;
+static struct ethtool_ops cdc_ncm_ethtool_ops;
+
+static const struct usb_device_id cdc_devs[] = {
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM,
+		USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
+		.driver_info = (unsigned long)&cdc_ncm_info,
+	},
+	{
+	},
+};
+
+MODULE_DEVICE_TABLE(usb, cdc_devs);
+
+static void
+cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	strncpy(info->driver, dev->driver_name, sizeof(info->driver));
+	strncpy(info->version, DRIVER_VERSION, sizeof(info->version));
+	strncpy(info->fw_version, dev->driver_info->description,
+		sizeof(info->fw_version));
+	usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
+}
+
+static int
+cdc_ncm_do_request(struct cdc_ncm_ctx *ctx, struct usb_cdc_notification *req,
+		   void *data, u16 flags, u16 *actlen, u16 timeout)
+{
+	int err;
+
+	err = usb_control_msg(ctx->udev, (req->bmRequestType & USB_DIR_IN) ?
+				usb_rcvctrlpipe(ctx->udev, 0) :
+				usb_sndctrlpipe(ctx->udev, 0),
+				req->bNotificationType, req->bmRequestType,
+				req->wValue,
+				req->wIndex, data,
+				req->wLength, timeout);
+
+	if (err < 0) {
+		if (actlen)
+			*actlen = 0;
+		return err;
+	}
+
+	if (actlen)
+		*actlen = err;
+
+	return 0;
+}
+
+static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
+{
+	struct usb_cdc_notification req;
+	u32 val;
+	__le16 max_datagram_size;
+	u8 flags;
+	u8 iface_no;
+	int err;
+
+	iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
+
+	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE;
+	req.bNotificationType = USB_CDC_GET_NTB_PARAMETERS;
+	req.wValue = 0;
+	req.wIndex = cpu_to_le16(iface_no);
+	req.wLength = cpu_to_le16(sizeof(ctx->ncm_parm));
+
+	err = cdc_ncm_do_request(ctx, &req, &ctx->ncm_parm, 0, NULL, 1000);
+	if (err) {
+		pr_debug("failed GET_NTB_PARAMETERS\n");
+		return 1;
+	}
+
+	/* read correct set of parameters according to device mode */
+	ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);
+	ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize);
+	ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder);
+	ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor);
+	ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment);
+
+	if (ctx->func_desc != NULL)
+		flags = ctx->func_desc->bmNetworkCapabilities;
+	else
+		flags = 0;
+
+	pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u "
+		 "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u "
+		 "wNdpOutAlignment=%u flags=0x%x\n",
+		 ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus,
+		 ctx->tx_ndp_modulus, flags);
+
+	/* max count of tx datagrams without terminating NULL entry */
+	ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX;
+
+	/* verify maximum size of received NTB in bytes */
+	if ((ctx->rx_max <
+	    (CDC_NCM_MIN_HDR_SIZE + CDC_NCM_MIN_DATAGRAM_SIZE)) ||
+	    (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX)) {
+		pr_debug("Using default maximum receive length=%d\n",
+						CDC_NCM_NTB_MAX_SIZE_RX);
+		ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX;
+	}
+
+	/* verify maximum size of transmitted NTB in bytes */
+	if ((ctx->tx_max <
+	    (CDC_NCM_MIN_HDR_SIZE + CDC_NCM_MIN_DATAGRAM_SIZE)) ||
+	    (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX)) {
+		pr_debug("Using default maximum transmit length=%d\n",
+						CDC_NCM_NTB_MAX_SIZE_TX);
+		ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX;
+	}
+
+	/*
+	 * verify that the structure alignment is:
+	 * - power of two
+	 * - not greater than the maximum transmit length
+	 * - not less than four bytes
+	 */
+	val = ctx->tx_ndp_modulus;
+
+	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
+	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
+		pr_debug("Using default alignment: 4 bytes\n");
+		ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
+	}
+
+	/*
+	 * verify that the payload alignment is:
+	 * - power of two
+	 * - not greater than the maximum transmit length
+	 * - not less than four bytes
+	 */
+	val = ctx->tx_modulus;
+
+	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
+	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
+		pr_debug("Using default transmit modulus: 4 bytes\n");
+		ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
+	}
+
+	/* verify the payload remainder */
+	if (ctx->tx_remainder >= ctx->tx_modulus) {
+		pr_debug("Using default transmit remainder: 0 bytes\n");
+		ctx->tx_remainder = 0;
+	}
+
+	/* adjust TX-remainder according to NCM specification. */
+	ctx->tx_remainder = ((ctx->tx_remainder - ETH_HLEN) &
+						(ctx->tx_modulus - 1));
+
+	/* additional configuration */
+
+	/* set CRC Mode */
+	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE;
+	req.bNotificationType = USB_CDC_SET_CRC_MODE;
+	req.wValue = cpu_to_le16(USB_CDC_NCM_CRC_NOT_APPENDED);
+	req.wIndex = cpu_to_le16(iface_no);
+	req.wLength = 0;
+
+	err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
+	if (err)
+		pr_debug("Setting CRC mode off failed\n");
+
+	/* set NTB format */
+	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE;
+	req.bNotificationType = USB_CDC_SET_NTB_FORMAT;
+	req.wValue = cpu_to_le16(USB_CDC_NCM_NTB16_FORMAT);
+	req.wIndex = cpu_to_le16(iface_no);
+	req.wLength = 0;
+
+	err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
+	if (err)
+		pr_debug("Setting NTB format to 16-bit failed\n");
+
+	/* set Max Datagram Size (MTU) */
+	req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE;
+	req.bNotificationType = USB_CDC_GET_MAX_DATAGRAM_SIZE;
+	req.wValue = 0;
+	req.wIndex = cpu_to_le16(iface_no);
+	req.wLength = cpu_to_le16(2);
+
+	err = cdc_ncm_do_request(ctx, &req, &max_datagram_size, 0, NULL, 1000);
+	if (err) {
+		pr_debug(" GET_MAX_DATAGRAM_SIZE failed, using size=%u\n",
+			 CDC_NCM_MIN_DATAGRAM_SIZE);
+		/* use default */
+		ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
+	} else {
+		ctx->max_datagram_size = le16_to_cpu(max_datagram_size);
+
+		if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE)
+			ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
+		else if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
+			ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE;
+	}
+
+	if (ctx->netdev->mtu != (ctx->max_datagram_size - ETH_HLEN))
+		ctx->netdev->mtu = ctx->max_datagram_size - ETH_HLEN;
+
+	return 0;
+}
+
+static void
+cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)
+{
+	struct usb_host_endpoint *e;
+	u8 ep;
+
+	for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) {
+
+		e = intf->cur_altsetting->endpoint + ep;
+		switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_INT:
+			if (usb_endpoint_dir_in(&e->desc)) {
+				if (ctx->status_ep == NULL)
+					ctx->status_ep = e;
+			}
+			break;
+
+		case USB_ENDPOINT_XFER_BULK:
+			if (usb_endpoint_dir_in(&e->desc)) {
+				if (ctx->in_ep == NULL)
+					ctx->in_ep = e;
+			} else {
+				if (ctx->out_ep == NULL)
+					ctx->out_ep = e;
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
+{
+	if (ctx == NULL)
+		return;
+
+	del_timer_sync(&ctx->tx_timer);
+
+	if (ctx->data_claimed) {
+		usb_set_intfdata(ctx->data, NULL);
+		usb_driver_release_interface(driver_of(ctx->intf), ctx->data);
+	}
+
+	if (ctx->control_claimed) {
+		usb_set_intfdata(ctx->control, NULL);
+		usb_driver_release_interface(driver_of(ctx->intf),
+								ctx->control);
+	}
+
+	if (ctx->tx_rem_skb != NULL) {
+		dev_kfree_skb_any(ctx->tx_rem_skb);
+		ctx->tx_rem_skb = NULL;
+	}
+
+	if (ctx->tx_curr_skb != NULL) {
+		dev_kfree_skb_any(ctx->tx_curr_skb);
+		ctx->tx_curr_skb = NULL;
+	}
+
+	kfree(ctx);
+}
+
+static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct cdc_ncm_ctx *ctx;
+	struct usb_driver *driver;
+	u8 *buf;
+	int len;
+	int temp;
+	u8 iface_no;
+
+	ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+	if (ctx == NULL)
+		goto error;
+
+	memset(ctx, 0, sizeof(*ctx));
+
+	init_timer(&ctx->tx_timer);
+	spin_lock_init(&ctx->mtx);
+	ctx->netdev = dev->net;
+
+	/* store ctx pointer in device data field */
+	dev->data[0] = (unsigned long)ctx;
+
+	/* get some pointers */
+	driver = driver_of(intf);
+	buf = intf->cur_altsetting->extra;
+	len = intf->cur_altsetting->extralen;
+
+	ctx->udev = dev->udev;
+	ctx->intf = intf;
+
+	/* parse through descriptors associated with control interface */
+	while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) {
+
+		if (buf[1] != USB_DT_CS_INTERFACE)
+			goto advance;
+
+		switch (buf[2]) {
+		case USB_CDC_UNION_TYPE:
+			if (buf[0] < sizeof(*(ctx->union_desc)))
+				break;
+
+			ctx->union_desc =
+					(const struct usb_cdc_union_desc *)buf;
+
+			ctx->control = usb_ifnum_to_if(dev->udev,
+					ctx->union_desc->bMasterInterface0);
+			ctx->data = usb_ifnum_to_if(dev->udev,
+					ctx->union_desc->bSlaveInterface0);
+			break;
+
+		case USB_CDC_ETHERNET_TYPE:
+			if (buf[0] < sizeof(*(ctx->ether_desc)))
+				break;
+
+			ctx->ether_desc =
+					(const struct usb_cdc_ether_desc *)buf;
+
+			dev->hard_mtu =
+				le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
+
+			if (dev->hard_mtu <
+			    (CDC_NCM_MIN_DATAGRAM_SIZE - ETH_HLEN))
+				dev->hard_mtu =
+					CDC_NCM_MIN_DATAGRAM_SIZE - ETH_HLEN;
+
+			else if (dev->hard_mtu >
+				 (CDC_NCM_MAX_DATAGRAM_SIZE - ETH_HLEN))
+				dev->hard_mtu =
+					CDC_NCM_MAX_DATAGRAM_SIZE - ETH_HLEN;
+			break;
+
+		case USB_CDC_NCM_TYPE:
+			if (buf[0] < sizeof(*(ctx->func_desc)))
+				break;
+
+			ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf;
+			break;
+
+		default:
+			break;
+		}
+advance:
+		/* advance to next descriptor */
+		temp = buf[0];
+		buf += temp;
+		len -= temp;
+	}
+
+	/* check if we got everything */
+	if ((ctx->control == NULL) || (ctx->data == NULL) ||
+	    (ctx->ether_desc == NULL))
+		goto error;
+
+	/* claim interfaces, if any */
+	if (ctx->data != intf) {
+		temp = usb_driver_claim_interface(driver, ctx->data, dev);
+		if (temp)
+			goto error;
+		ctx->data_claimed = 1;
+	}
+
+	if (ctx->control != intf) {
+		temp = usb_driver_claim_interface(driver, ctx->control, dev);
+		if (temp)
+			goto error;
+		ctx->control_claimed = 1;
+	}
+
+	iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;
+
+	/* reset data interface */
+	temp = usb_set_interface(dev->udev, iface_no, 0);
+	if (temp)
+		goto error;
+
+	/* initialize data interface */
+	if (cdc_ncm_setup(ctx))
+		goto error;
+
+	/* configure data interface */
+	temp = usb_set_interface(dev->udev, iface_no, 1);
+	if (temp)
+		goto error;
+
+	cdc_ncm_find_endpoints(ctx, ctx->data);
+	cdc_ncm_find_endpoints(ctx, ctx->control);
+
+	if ((ctx->in_ep == NULL) || (ctx->out_ep == NULL) ||
+	    (ctx->status_ep == NULL))
+		goto error;
+
+	dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
+
+	usb_set_intfdata(ctx->data, dev);
+	usb_set_intfdata(ctx->control, dev);
+	usb_set_intfdata(ctx->intf, dev);
+
+	temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress);
+	if (temp)
+		goto error;
+
+	dev_info(&dev->udev->dev, "MAC-Address: "
+				"0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
+				dev->net->dev_addr[0], dev->net->dev_addr[1],
+				dev->net->dev_addr[2], dev->net->dev_addr[3],
+				dev->net->dev_addr[4], dev->net->dev_addr[5]);
+
+	dev->in = usb_rcvbulkpipe(dev->udev,
+		ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->out = usb_sndbulkpipe(dev->udev,
+		ctx->out_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->status = ctx->status_ep;
+	dev->rx_urb_size = ctx->rx_max;
+
+	/*
+	 * We should get an event when network connection is "connected" or
+	 * "disconnected". Set network connection in "disconnected" state
+	 * (carrier is OFF) during attach, so the IP network stack does not
+	 * start IPv6 negotiation and more.
+	 */
+	netif_carrier_off(dev->net);
+	ctx->tx_speed = ctx->rx_speed = 0;
+	return 0;
+
+error:
+	cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);
+	dev->data[0] = 0;
+	dev_info(&dev->udev->dev, "Descriptor failure\n");
+	return -ENODEV;
+}
+
+static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+	struct usb_driver *driver;
+
+	if (ctx == NULL)
+		return;		/* no setup */
+
+	driver = driver_of(intf);
+
+	usb_set_intfdata(ctx->data, NULL);
+	usb_set_intfdata(ctx->control, NULL);
+	usb_set_intfdata(ctx->intf, NULL);
+
+	/* release interfaces, if any */
+	if (ctx->data_claimed) {
+		usb_driver_release_interface(driver, ctx->data);
+		ctx->data_claimed = 0;
+	}
+
+	if (ctx->control_claimed) {
+		usb_driver_release_interface(driver, ctx->control);
+		ctx->control_claimed = 0;
+	}
+
+	cdc_ncm_free(ctx);
+}
+
+static void cdc_ncm_zero_fill(u8 *ptr, u32 first, u32 end, u32 max)
+{
+	if (first >= max)
+		return;
+	if (first >= end)
+		return;
+	if (end > max)
+		end = max;
+	memset(ptr + first, 0, end - first);
+}
+
+static struct sk_buff *
+cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
+{
+	struct sk_buff *skb_out;
+	u32 rem;
+	u32 offset;
+	u32 last_offset;
+	u16 n = 0;
+	u8 timeout = 0;
+
+	/* if there is a remaining skb, it gets priority */
+	if (skb != NULL)
+		swap(skb, ctx->tx_rem_skb);
+	else
+		timeout = 1;
+
+	/*
+	 * +----------------+
+	 * | skb_out        |
+	 * +----------------+
+	 *           ^ offset
+	 *        ^ last_offset
+	 */
+
+	/* check if we are resuming an OUT skb */
+	if (ctx->tx_curr_skb != NULL) {
+		/* pop variables */
+		skb_out = ctx->tx_curr_skb;
+		offset = ctx->tx_curr_offset;
+		last_offset = ctx->tx_curr_last_offset;
+		n = ctx->tx_curr_frame_num;
+
+	} else {
+		/* reset variables */
+		skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC);
+		if (skb_out == NULL) {
+			if (skb != NULL) {
+				dev_kfree_skb_any(skb);
+				ctx->netdev->stats.tx_dropped++;
+			}
+			goto exit_no_skb;
+		}
+
+		/* make room for NTH and NDP */
+		offset = CDC_NCM_ALIGN(0, sizeof(struct usb_cdc_ncm_nth16),
+							ctx->tx_ndp_modulus) +
+					sizeof(struct usb_cdc_ncm_ndp16) +
+					(ctx->tx_max_datagrams + 1) *
+					sizeof(struct usb_cdc_ncm_dpe16);
+
+		/* store last valid offset before alignment */
+		last_offset = offset;
+		/* align first Datagram offset correctly */
+		offset = CDC_NCM_ALIGN(ctx->tx_remainder, offset,
+							ctx->tx_modulus);
+		/* zero buffer till the first IP datagram */
+		cdc_ncm_zero_fill(skb_out->data, 0, offset, offset);
+		n = 0;
+		ctx->tx_curr_frame_num = 0;
+	}
+
+	for (; n < ctx->tx_max_datagrams; n++) {
+		/* check if end of transmit buffer is reached */
+		if (offset >= ctx->tx_max)
+			break;
+
+		/* compute maximum buffer size */
+		rem = ctx->tx_max - offset;
+
+		if (skb == NULL) {
+			skb = ctx->tx_rem_skb;
+			ctx->tx_rem_skb = NULL;
+
+			/* check for end of skb */
+			if (skb == NULL)
+				break;
+		}
+
+		if (skb->len > rem) {
+			if (n == 0) {
+				/* won't fit, MTU problem? */
+				dev_kfree_skb_any(skb);
+				skb = NULL;
+				ctx->netdev->stats.tx_dropped++;
+			} else {
+				/* no room for skb - store for later */
+				if (ctx->tx_rem_skb != NULL) {
+					dev_kfree_skb_any(ctx->tx_rem_skb);
+					ctx->netdev->stats.tx_dropped++;
+				}
+				ctx->tx_rem_skb = skb;
+				skb = NULL;
+
+				/* loop one more time */
+				timeout = 1;
+			}
+			break;
+		}
+
+		memcpy(((u8 *)skb_out->data) + offset, skb->data, skb->len);
+
+		ctx->tx_ncm.dpe16[n].wDatagramLength = cpu_to_le16(skb->len);
+		ctx->tx_ncm.dpe16[n].wDatagramIndex = cpu_to_le16(offset);
+
+		/* update offset */
+		offset += skb->len;
+
+		/* store last valid offset before alignment */
+		last_offset = offset;
+
+		/* align offset correctly */
+		offset = CDC_NCM_ALIGN(ctx->tx_remainder, offset,
+							ctx->tx_modulus);
+
+		/* zero padding */
+		cdc_ncm_zero_fill(skb_out->data, last_offset, offset,
+								ctx->tx_max);
+		dev_kfree_skb_any(skb);
+		skb = NULL;
+	}
+
+	/* free up any dangling skb */
+	if (skb != NULL) {
+		dev_kfree_skb_any(skb);
+		skb = NULL;
+		ctx->netdev->stats.tx_dropped++;
+	}
+
+	ctx->tx_curr_frame_num = n;
+
+	if (n == 0) {
+		/* wait for more frames */
+		/* push variables */
+		ctx->tx_curr_skb = skb_out;
+		ctx->tx_curr_offset = offset;
+		ctx->tx_curr_last_offset = last_offset;
+		goto exit_no_skb;
+
+	} else if ((n < ctx->tx_max_datagrams) && (timeout == 0)) {
+		/* wait for more frames */
+		/* push variables */
+		ctx->tx_curr_skb = skb_out;
+		ctx->tx_curr_offset = offset;
+		ctx->tx_curr_last_offset = last_offset;
+		/* set the pending count */
+		if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
+			ctx->tx_timer_pending = 2;
+		goto exit_no_skb;
+
+	} else {
+		/* frame goes out */
+		/* variables will be reset at next call */
+	}
+
+	/* check for overflow */
+	if (last_offset > ctx->tx_max)
+		last_offset = ctx->tx_max;
+
+	/* revert offset */
+	offset = last_offset;
+
+	/*
+	 * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes,
+	 * we send buffers as it is. If we get more data, it would be more
+	 * efficient for USB HS mobile device with DMA engine to receive a full
+	 * size NTB, than canceling DMA transfer and receiving a short packet.
+	 */
+	if (offset > CDC_NCM_MIN_TX_PKT)
+		offset = ctx->tx_max;
+
+	/* final zero padding */
+	cdc_ncm_zero_fill(skb_out->data, last_offset, offset, ctx->tx_max);
+
+	/* store last offset */
+	last_offset = offset;
+
+	if ((last_offset < ctx->tx_max) && ((last_offset %
+			le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) {
+		/* force short packet */
+		*(((u8 *)skb_out->data) + last_offset) = 0;
+		last_offset++;
+	}
+
+	/* zero the rest of the DPEs plus the last NULL entry */
+	for (; n <= CDC_NCM_DPT_DATAGRAMS_MAX; n++) {
+		ctx->tx_ncm.dpe16[n].wDatagramLength = 0;
+		ctx->tx_ncm.dpe16[n].wDatagramIndex = 0;
+	}
+
+	/* fill out 16-bit NTB header */
+	ctx->tx_ncm.nth16.dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
+	ctx->tx_ncm.nth16.wHeaderLength =
+					cpu_to_le16(sizeof(ctx->tx_ncm.nth16));
+	ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq);
+	ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset);
+	ctx->tx_ncm.nth16.wFpIndex = CDC_NCM_ALIGN(0,
+					sizeof(struct usb_cdc_ncm_nth16),
+					ctx->tx_ndp_modulus);
+
+	memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16));
+	ctx->tx_seq++;
+
+	/* fill out 16-bit NDP table */
+	ctx->tx_ncm.ndp16.dwSignature =
+				cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN);
+	rem = sizeof(ctx->tx_ncm.ndp16) + ((ctx->tx_curr_frame_num + 1) *
+					sizeof(struct usb_cdc_ncm_dpe16));
+	ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem);
+	ctx->tx_ncm.ndp16.wNextFpIndex = 0; /* reserved */
+
+	memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wFpIndex,
+						&(ctx->tx_ncm.ndp16),
+						sizeof(ctx->tx_ncm.ndp16));
+
+	memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wFpIndex +
+					sizeof(ctx->tx_ncm.ndp16),
+					&(ctx->tx_ncm.dpe16),
+					(ctx->tx_curr_frame_num + 1) *
+					sizeof(struct usb_cdc_ncm_dpe16));
+
+	/* set frame length */
+	skb_put(skb_out, last_offset);
+
+	/* return skb */
+	ctx->tx_curr_skb = NULL;
+	return skb_out;
+
+exit_no_skb:
+	return NULL;
+}
+
+static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
+{
+	/* start timer, if not already started */
+	if (timer_pending(&ctx->tx_timer) == 0) {
+		ctx->tx_timer.function = &cdc_ncm_tx_timeout;
+		ctx->tx_timer.data = (unsigned long)ctx;
+		ctx->tx_timer.expires = jiffies + ((HZ + 999) / 1000);
+		add_timer(&ctx->tx_timer);
+	}
+}
+
+static void cdc_ncm_tx_timeout(unsigned long arg)
+{
+	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)arg;
+	u8 restart;
+
+	spin_lock(&ctx->mtx);
+	if (ctx->tx_timer_pending != 0) {
+		ctx->tx_timer_pending--;
+		restart = 1;
+	} else
+		restart = 0;
+
+	spin_unlock(&ctx->mtx);
+
+	if (restart)
+		cdc_ncm_tx_timeout_start(ctx);
+	else if (ctx->netdev != NULL)
+		usbnet_start_xmit(NULL, ctx->netdev);
+}
+
+static struct sk_buff *
+cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	struct sk_buff *skb_out;
+	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+	u8 need_timer = 0;
+
+	/*
+	 * The Ethernet API we are using does not support transmitting
+	 * multiple Ethernet frames in a single call. This driver will
+	 * accumulate multiple Ethernet frames and send out a larger
+	 * USB frame when the USB buffer is full or when a single jiffies
+	 * timeout happens.
+	 */
+	if (ctx == NULL)
+		goto error;
+
+	spin_lock(&ctx->mtx);
+	skb_out = cdc_ncm_fill_tx_frame(ctx, skb);
+	if (ctx->tx_curr_skb != NULL)
+		need_timer = 1;
+	spin_unlock(&ctx->mtx);
+
+	/* Start timer, if there is a remaining skb */
+	if (need_timer)
+		cdc_ncm_tx_timeout_start(ctx);
+
+	if (skb_out)
+		dev->net->stats.tx_packets += ctx->tx_curr_frame_num;
+	return skb_out;
+
+error:
+	if (skb != NULL)
+		dev_kfree_skb_any(skb);
+
+	return NULL;
+}
+
+static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
+{
+	struct sk_buff *skb;
+	struct cdc_ncm_ctx *ctx;
+	int sumlen;
+	int actlen;
+	int temp;
+	int nframes;
+	int x;
+	int offset;
+
+	ctx = (struct cdc_ncm_ctx *)dev->data[0];
+	if (ctx == NULL)
+		goto error;
+
+	actlen = skb_in->len;
+	sumlen = CDC_NCM_NTB_MAX_SIZE_RX;
+
+	if (actlen < (sizeof(ctx->rx_ncm.nth16) + sizeof(ctx->rx_ncm.ndp16))) {
+		pr_debug("frame too short\n");
+		goto error;
+	}
+
+	memcpy(&(ctx->rx_ncm.nth16), ((u8 *)skb_in->data),
+						sizeof(ctx->rx_ncm.nth16));
+
+	if (le32_to_cpu(ctx->rx_ncm.nth16.dwSignature) !=
+	    USB_CDC_NCM_NTH16_SIGN) {
+		pr_debug("invalid NTH16 signature <%u>\n",
+			 le32_to_cpu(ctx->rx_ncm.nth16.dwSignature));
+		goto error;
+	}
+
+	temp = le16_to_cpu(ctx->rx_ncm.nth16.wBlockLength);
+	if (temp > sumlen) {
+		pr_debug("unsupported NTB block length %u/%u\n", temp, sumlen);
+		goto error;
+	}
+
+	temp = le16_to_cpu(ctx->rx_ncm.nth16.wFpIndex);
+	if ((temp + sizeof(ctx->rx_ncm.ndp16)) > actlen) {
+		pr_debug("invalid DPT16 index\n");
+		goto error;
+	}
+
+	memcpy(&(ctx->rx_ncm.ndp16), ((u8 *)skb_in->data) + temp,
+						sizeof(ctx->rx_ncm.ndp16));
+
+	if (le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature) !=
+	    USB_CDC_NCM_NDP16_NOCRC_SIGN) {
+		pr_debug("invalid DPT16 signature <%u>\n",
+			 le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature));
+		goto error;
+	}
+
+	if (le16_to_cpu(ctx->rx_ncm.ndp16.wLength) <
+	    USB_CDC_NCM_NDP16_LENGTH_MIN) {
+		pr_debug("invalid DPT16 length <%u>\n",
+			 le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature));
+		goto error;
+	}
+
+	nframes = ((le16_to_cpu(ctx->rx_ncm.ndp16.wLength) -
+					sizeof(struct usb_cdc_ncm_ndp16)) /
+					sizeof(struct usb_cdc_ncm_dpe16));
+	nframes--; /* we process NDP entries except for the last one */
+
+	pr_debug("nframes = %u\n", nframes);
+
+	temp += sizeof(ctx->rx_ncm.ndp16);
+
+	if ((temp + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > actlen) {
+		pr_debug("Invalid nframes = %d\n", nframes);
+		goto error;
+	}
+
+	if (nframes > CDC_NCM_DPT_DATAGRAMS_MAX) {
+		pr_debug("Truncating number of frames from %u to %u\n",
+					nframes, CDC_NCM_DPT_DATAGRAMS_MAX);
+		nframes = CDC_NCM_DPT_DATAGRAMS_MAX;
+	}
+
+	memcpy(&(ctx->rx_ncm.dpe16), ((u8 *)skb_in->data) + temp,
+				nframes * (sizeof(struct usb_cdc_ncm_dpe16)));
+
+	for (x = 0; x < nframes; x++) {
+		offset = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramIndex);
+		temp = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramLength);
+
+		/*
+		 * CDC NCM ch. 3.7
+		 * All entries after first NULL entry are to be ignored
+		 */
+		if ((offset == 0) || (temp == 0)) {
+			if (!x)
+				goto error; /* empty NTB */
+			break;
+		}
+
+		/* sanity checking */
+		if (((offset + temp) > actlen) ||
+		    (temp > CDC_NCM_MAX_DATAGRAM_SIZE) || (temp < ETH_HLEN)) {
+			pr_debug("invalid frame detected (ignored)"
+				"offset[%u]=%u, length=%u, skb=%p\n",
+							x, offset, temp, skb);
+			if (!x)
+				goto error;
+			break;
+
+		} else {
+			skb = skb_clone(skb_in, GFP_ATOMIC);
+			skb->len = temp;
+			skb->data = ((u8 *)skb_in->data) + offset;
+			skb_set_tail_pointer(skb, temp);
+			usbnet_skb_return(dev, skb);
+		}
+	}
+	return 1;
+error:
+	return 0;
+}
+
+static void
+cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx,
+		     struct connection_speed_change *data)
+{
+	uint32_t rx_speed = le32_to_cpu(data->USBitRate);
+	uint32_t tx_speed = le32_to_cpu(data->DSBitRate);
+
+	/*
+	 * Currently the USB-NET API does not support reporting the actual
+	 * device speed. Do print it instead.
+	 */
+	if ((tx_speed != ctx->tx_speed) || (rx_speed != ctx->rx_speed)) {
+		ctx->tx_speed = tx_speed;
+		ctx->rx_speed = rx_speed;
+
+		if ((tx_speed > 1000000) && (rx_speed > 1000000)) {
+			printk(KERN_INFO KBUILD_MODNAME
+				": %s: %u mbit/s downlink "
+				"%u mbit/s uplink\n",
+				ctx->netdev->name,
+				(unsigned int)(rx_speed / 1000000U),
+				(unsigned int)(tx_speed / 1000000U));
+		} else {
+			printk(KERN_INFO KBUILD_MODNAME
+				": %s: %u kbit/s downlink "
+				"%u kbit/s uplink\n",
+				ctx->netdev->name,
+				(unsigned int)(rx_speed / 1000U),
+				(unsigned int)(tx_speed / 1000U));
+		}
+	}
+}
+
+static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
+{
+	struct cdc_ncm_ctx *ctx;
+	struct usb_cdc_notification *event;
+
+	ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+	if (urb->actual_length < sizeof(*event))
+		return;
+
+	/* test for split data in 8-byte chunks */
+	if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
+		cdc_ncm_speed_change(ctx,
+		      (struct connection_speed_change *)urb->transfer_buffer);
+		return;
+	}
+
+	event = urb->transfer_buffer;
+
+	switch (event->bNotificationType) {
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		/*
+		 * According to the CDC NCM specification ch.7.1
+		 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be
+		 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
+		 */
+		ctx->connected = event->wValue;
+
+		printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:"
+			" %sconnected\n",
+			ctx->netdev->name, ctx->connected ? "" : "dis");
+
+		if (ctx->connected)
+			netif_carrier_on(dev->net);
+		else {
+			netif_carrier_off(dev->net);
+			ctx->tx_speed = ctx->rx_speed = 0;
+		}
+		break;
+
+	case USB_CDC_NOTIFY_SPEED_CHANGE:
+		if (urb->actual_length <
+		    (sizeof(*event) + sizeof(struct connection_speed_change)))
+			set_bit(EVENT_STS_SPLIT, &dev->flags);
+		else
+			cdc_ncm_speed_change(ctx,
+				(struct connection_speed_change *) &event[1]);
+		break;
+
+	default:
+		dev_err(&dev->udev->dev, "NCM: unexpected "
+			"notification 0x%02x!\n", event->bNotificationType);
+		break;
+	}
+}
+
+static int cdc_ncm_check_connect(struct usbnet *dev)
+{
+	struct cdc_ncm_ctx *ctx;
+
+	ctx = (struct cdc_ncm_ctx *)dev->data[0];
+	if (ctx == NULL)
+		return 1;	/* disconnected */
+
+	return !ctx->connected;
+}
+
+static int
+cdc_ncm_probe(struct usb_interface *udev, const struct usb_device_id *prod)
+{
+	return usbnet_probe(udev, prod);
+}
+
+static void cdc_ncm_disconnect(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+
+	if (dev == NULL)
+		return;		/* already disconnected */
+
+	usbnet_disconnect(intf);
+}
+
+static int cdc_ncm_manage_power(struct usbnet *dev, int status)
+{
+	dev->intf->needs_remote_wakeup = status;
+	return 0;
+}
+
+static const struct driver_info cdc_ncm_info = {
+	.description = "CDC NCM",
+	.flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET,
+	.bind = cdc_ncm_bind,
+	.unbind = cdc_ncm_unbind,
+	.check_connect = cdc_ncm_check_connect,
+	.manage_power = cdc_ncm_manage_power,
+	.status = cdc_ncm_status,
+	.rx_fixup = cdc_ncm_rx_fixup,
+	.tx_fixup = cdc_ncm_tx_fixup,
+};
+
+static struct usb_driver cdc_ncm_driver = {
+	.name = "cdc_ncm",
+	.id_table = cdc_devs,
+	.probe = cdc_ncm_probe,
+	.disconnect = cdc_ncm_disconnect,
+	.suspend = usbnet_suspend,
+	.resume = usbnet_resume,
+	.supports_autosuspend = 1,
+};
+
+static struct ethtool_ops cdc_ncm_ethtool_ops = {
+	.get_drvinfo = cdc_ncm_get_drvinfo,
+	.get_link = usbnet_get_link,
+	.get_msglevel = usbnet_get_msglevel,
+	.set_msglevel = usbnet_set_msglevel,
+	.get_settings = usbnet_get_settings,
+	.set_settings = usbnet_set_settings,
+	.nway_reset = usbnet_nway_reset,
+};
+
+static int __init cdc_ncm_init(void)
+{
+	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION "\n");
+	return usb_register(&cdc_ncm_driver);
+}
+
+module_init(cdc_ncm_init);
+
+static void __exit cdc_ncm_exit(void)
+{
+	usb_deregister(&cdc_ncm_driver);
+}
+
+module_exit(cdc_ncm_exit);
+
+MODULE_AUTHOR("Hans Petter Selasky");
+MODULE_DESCRIPTION("USB CDC NCM host driver");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.7.0.4


^ permalink raw reply related

* [PATCHv2 1/2] usbnet: changes for upcoming cdc_ncm driver
From: Alexey Orishko @ 2010-11-25 14:14 UTC (permalink / raw)
  To: gregkh, linux-usb
  Cc: netdev, oliver, yauheni.kaliuta, balbi, sjur.brandeland,
	Alexey Orishko

Changes:
include/linux/usb/usbnet.h:
- a new flag to indicate driver's capability to accumulate IP packets in Tx
 direction and extract several packets from single skb in Rx direction.

drivers/net/usb/usbnet.c:
- the procedure of counting packets in usbnet was updated due to the
 accumulating of IP packets in the driver;
- no short packets are sent if indicated by the flag in the driver_info
 structure.

Signed-off-by: Alexey Orishko <alexey.orishko@stericsson.com>
---
Patch v2:
based on suggestion moved Changes to the comment log
updated comments in the code

 drivers/net/usb/usbnet.c   |   45 ++++++++++++++++++++++++++++++-------------
 include/linux/usb/usbnet.h |    6 +++++
 2 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index c04d49e..cff74b8 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -391,14 +391,19 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
 		goto error;
 	// else network stack removes extra byte if we forced a short packet
 
-	if (skb->len)
-		usbnet_skb_return (dev, skb);
-	else {
-		netif_dbg(dev, rx_err, dev->net, "drop\n");
-error:
-		dev->net->stats.rx_errors++;
-		skb_queue_tail (&dev->done, skb);
+	if (skb->len) {
+		/* all data was already cloned from skb inside the driver */
+		if (dev->driver_info->flags & FLAG_MULTI_PACKET)
+			dev_kfree_skb_any(skb);
+		else
+			usbnet_skb_return(dev, skb);
+		return;
 	}
+
+	netif_dbg(dev, rx_err, dev->net, "drop\n");
+error:
+	dev->net->stats.rx_errors++;
+	skb_queue_tail(&dev->done, skb);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -971,7 +976,8 @@ static void tx_complete (struct urb *urb)
 	struct usbnet		*dev = entry->dev;
 
 	if (urb->status == 0) {
-		dev->net->stats.tx_packets++;
+		if (!(dev->driver_info->flags & FLAG_MULTI_PACKET))
+			dev->net->stats.tx_packets++;
 		dev->net->stats.tx_bytes += entry->length;
 	} else {
 		dev->net->stats.tx_errors++;
@@ -1044,8 +1050,13 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
 	if (info->tx_fixup) {
 		skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
 		if (!skb) {
-			netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n");
-			goto drop;
+			if (netif_msg_tx_err(dev)) {
+				netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n");
+				goto drop;
+			} else {
+				/* cdc_ncm collected packet; waits for more */
+				goto not_drop;
+			}
 		}
 	}
 	length = skb->len;
@@ -1067,13 +1078,18 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
 	/* don't assume the hardware handles USB_ZERO_PACKET
 	 * NOTE:  strictly conforming cdc-ether devices should expect
 	 * the ZLP here, but ignore the one-byte packet.
+	 * NOTE2: CDC NCM specification is different from CDC ECM when
+	 * handling ZLP/short packets, so cdc_ncm driver will make short
+	 * packet itself if needed.
 	 */
 	if (length % dev->maxpacket == 0) {
 		if (!(info->flags & FLAG_SEND_ZLP)) {
-			urb->transfer_buffer_length++;
-			if (skb_tailroom(skb)) {
-				skb->data[skb->len] = 0;
-				__skb_put(skb, 1);
+			if (!(info->flags & FLAG_MULTI_PACKET)) {
+				urb->transfer_buffer_length++;
+				if (skb_tailroom(skb)) {
+					skb->data[skb->len] = 0;
+					__skb_put(skb, 1);
+				}
 			}
 		} else
 			urb->transfer_flags |= URB_ZERO_PACKET;
@@ -1122,6 +1138,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
 		netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", retval);
 drop:
 		dev->net->stats.tx_dropped++;
+not_drop:
 		if (skb)
 			dev_kfree_skb_any (skb);
 		usb_free_urb (urb);
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 7ae27a4..44842c8 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -97,6 +97,12 @@ struct driver_info {
 
 #define FLAG_LINK_INTR	0x0800		/* updates link (carrier) status */
 
+/*
+ * Indicates to usbnet, that USB driver accumulates multiple IP packets.
+ * Affects statistic (counters) and short packet handling.
+ */
+#define FLAG_MULTI_PACKET	0x1000
+
 	/* init device ... can sleep, or cause probe() failure */
 	int	(*bind)(struct usbnet *, struct usb_interface *);
 
-- 
1.7.0.4


^ permalink raw reply related

* Re: Fwd: Simple kernel attack using socketpair. easy, 100% reproductiblle, works under guest. no way to protect :(
From: Eric Dumazet @ 2010-11-25 14:11 UTC (permalink / raw)
  To: Марк Коренберг,
	David Miller
  Cc: netdev
In-Reply-To: <AANLkTi=NTFXXY42nh+j3xM4n-NTPaz=fWKmAY=MpsHze@mail.gmail.com>

Le jeudi 25 novembre 2010 à 13:35 +0500, Марк Коренберг a écrit :
> quick and dirty fix will be not to allow to pass unix socket inside
> unix socket. I think it would not break much applications.

Really, if it was not needed, net/unix/garbage.c would not exist at
all...

It is needed by some apps.


[PATCH] af_unix: limit recursion level

Its easy to eat all kernel memory and trigger NMI watchdog, using an
exploit program that queues unix sockets on top of others.

lkml ref : http://lkml.org/lkml/2010/11/25/8

This mechanism is used in applications, one choice we have is to have a
recursion limit.

Other limits might be needed as well (if we queue other types of files),
since the passfd mechanism is currently limited by socket receive queue
sizes only.

Add a recursion_level to unix socket, allowing up to 4 levels.

Each time we send an unix socket through sendfd mechanism, we copy its
recursion level (plus one) to receiver. This recursion level is cleared
when socket receive queue is emptied.

Reported-by: Марк Коренберг <socketpair@gmail.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 include/net/af_unix.h |    2 ++
 net/unix/af_unix.c    |   37 ++++++++++++++++++++++++++++++++-----
 net/unix/garbage.c    |    2 +-
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index 90c9e28..18e5c3f 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -10,6 +10,7 @@ extern void unix_inflight(struct file *fp);
 extern void unix_notinflight(struct file *fp);
 extern void unix_gc(void);
 extern void wait_for_unix_gc(void);
+extern struct sock *unix_get_socket(struct file *filp);
 
 #define UNIX_HASH_SIZE	256
 
@@ -56,6 +57,7 @@ struct unix_sock {
 	spinlock_t		lock;
 	unsigned int		gc_candidate : 1;
 	unsigned int		gc_maybe_cycle : 1;
+	unsigned char		recursion_level;
 	struct socket_wq	peer_wq;
 };
 #define unix_sk(__sk) ((struct unix_sock *)__sk)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 3c95304..2268e67 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1343,9 +1343,25 @@ static void unix_destruct_scm(struct sk_buff *skb)
 	sock_wfree(skb);
 }
 
+#define MAX_RECURSION_LEVEL 4
+
 static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
 {
 	int i;
+	unsigned char max_level = 0;
+	int unix_sock_count = 0;
+
+	for (i = scm->fp->count - 1; i >= 0; i--) {
+		struct sock *sk = unix_get_socket(scm->fp->fp[i]);
+
+		if (sk) {
+			unix_sock_count++;
+			max_level = max(max_level,
+					unix_sk(sk)->recursion_level);
+		}
+	}
+	if (unlikely(max_level > MAX_RECURSION_LEVEL))
+		return -ETOOMANYREFS;
 
 	/*
 	 * Need to duplicate file references for the sake of garbage
@@ -1356,9 +1372,11 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
 	if (!UNIXCB(skb).fp)
 		return -ENOMEM;
 
-	for (i = scm->fp->count-1; i >= 0; i--)
-		unix_inflight(scm->fp->fp[i]);
-	return 0;
+	if (unix_sock_count) {
+		for (i = scm->fp->count - 1; i >= 0; i--)
+			unix_inflight(scm->fp->fp[i]);
+	}
+	return max_level;
 }
 
 static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds)
@@ -1393,6 +1411,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
 	struct sk_buff *skb;
 	long timeo;
 	struct scm_cookie tmp_scm;
+	int max_level;
 
 	if (NULL == siocb->scm)
 		siocb->scm = &tmp_scm;
@@ -1431,8 +1450,9 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		goto out;
 
 	err = unix_scm_to_skb(siocb->scm, skb, true);
-	if (err)
+	if (err < 0)
 		goto out_free;
+	max_level = err + 1;
 	unix_get_secdata(siocb->scm, skb);
 
 	skb_reset_transport_header(skb);
@@ -1514,6 +1534,8 @@ restart:
 	if (sock_flag(other, SOCK_RCVTSTAMP))
 		__net_timestamp(skb);
 	skb_queue_tail(&other->sk_receive_queue, skb);
+	if (max_level > unix_sk(other)->recursion_level)
+		unix_sk(other)->recursion_level = max_level;
 	unix_state_unlock(other);
 	other->sk_data_ready(other, len);
 	sock_put(other);
@@ -1544,6 +1566,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 	int sent = 0;
 	struct scm_cookie tmp_scm;
 	bool fds_sent = false;
+	int max_level;
 
 	if (NULL == siocb->scm)
 		siocb->scm = &tmp_scm;
@@ -1607,10 +1630,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
 		/* Only send the fds in the first buffer */
 		err = unix_scm_to_skb(siocb->scm, skb, !fds_sent);
-		if (err) {
+		if (err < 0) {
 			kfree_skb(skb);
 			goto out_err;
 		}
+		max_level = err + 1;
 		fds_sent = true;
 
 		err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
@@ -1626,6 +1650,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 			goto pipe_err_free;
 
 		skb_queue_tail(&other->sk_receive_queue, skb);
+		if (max_level > unix_sk(other)->recursion_level)
+			unix_sk(other)->recursion_level = max_level;
 		unix_state_unlock(other);
 		other->sk_data_ready(other, size);
 		sent += size;
@@ -1845,6 +1871,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 		unix_state_lock(sk);
 		skb = skb_dequeue(&sk->sk_receive_queue);
 		if (skb == NULL) {
+			unix_sk(sk)->recursion_level = 0;
 			if (copied >= target)
 				goto unlock;
 
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index c8df6fd..a2d99a5 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -96,7 +96,7 @@ static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait);
 unsigned int unix_tot_inflight;
 
 
-static struct sock *unix_get_socket(struct file *filp)
+struct sock *unix_get_socket(struct file *filp)
 {
 	struct sock *u_sock = NULL;
 	struct inode *inode = filp->f_path.dentry->d_inode;



^ permalink raw reply related

* Re: [PATCH 2/2] The new jhash implementation
From: Eric Dumazet @ 2010-11-25 14:05 UTC (permalink / raw)
  To: Changli Gao
  Cc: Jozsef Kadlecsik, linux-kernel, netdev, netfilter-devel,
	Linus Torvalds, Rusty Russell
In-Reply-To: <AANLkTi=jqRY18Omv2Y2a1XjT-SH8W--wJ7A4y4PV1oT1@mail.gmail.com>

Le jeudi 25 novembre 2010 à 21:55 +0800, Changli Gao a écrit :

> > I suggest :
> >
> > #include <linux/unaligned/packed_struct.h>
> > ...
> >        a += __get_unaligned_cpu32(k);
> >        b += __get_unaligned_cpu32(k+4);
> >        c += __get_unaligned_cpu32(k+8);
> >
> > Fits nicely in registers.
> >
> 
> I think you mean get_unaligned_le32().
> 

No, I meant __get_unaligned_cpu32()

We do same thing in jhash2() :

	a += k[0]; 
	b += k[1];
	c += k[2];

We dont care of bit order of the 32bit quantity we are adding to a,b or
c , as long its consistent for the current machine ;)

get_unaligned_le32() would be slow on big endian arches.



--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/2] Prepare the tree for un-inlined jhash.
From: Jozsef Kadlecsik @ 2010-11-25 13:57 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: linux-kernel, netdev, netfilter-devel, Linus Torvalds,
	Rusty Russell
In-Reply-To: <alpine.LNX.2.01.1011251439160.21752@obet.zrqbmnf.qr>

On Thu, 25 Nov 2010, Jan Engelhardt wrote:

> On Thursday 2010-11-25 14:15, Jozsef Kadlecsik wrote:
> 
> >jhash is widely used in the kernel and because the functions
> >are inlined, the cost in size is significant. Also, the new jhash
> >functions are slightly larger than the previous ones so better un-inline.
> >As a preparation step, the calls to the internal macros are replaced
> >with the plain jhash function calls.
> 
> Do you have a non-normative allyesconfig/allmodconfig build whose 
> size(1) you can run on, to show approximately just how much it differs?

In the cover mail I referred the link to the message from Ilpo Jarvinen: 
"I once looked into inlining cost and jhash functions were among the most 
wasteful (kernel-wide). Multiple jhash bodies were 100+ bytes, and the 
overall cost was 10k+."

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlec@mail.kfki.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : KFKI Research Institute for Particle and Nuclear Physics
          H-1525 Budapest 114, POB. 49, Hungary

^ permalink raw reply

* Re: [PATCH 2/2] The new jhash implementation
From: Changli Gao @ 2010-11-25 13:55 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Jozsef Kadlecsik, linux-kernel, netdev, netfilter-devel,
	Linus Torvalds, Rusty Russell
In-Reply-To: <1290692943.2858.303.camel@edumazet-laptop>

On Thu, Nov 25, 2010 at 9:49 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> Le jeudi 25 novembre 2010 à 14:15 +0100, Jozsef Kadlecsik a écrit :
>> The current jhash.h implements the lookup2() hash function by Bob Jenkins.
>> However, lookup2() is outdated as Bob wrote a new hash function called
>> lookup3(). The new hash function
>>
>> - mixes better than lookup2(): it passes the check that every input bit
>>   changes every output bit 50% of the time, while lookup2() failed it.
>> - performs better: compiled with -O2 on Core2 Duo, lookup3() 20-40% faster
>>   than lookup2() depending on the key length.
>>
>> The patch replaces the lookup2() implementation of the 'jhash*'
>> functions with that of lookup3().
>>
>> You can read a longer comparison of the two and other hash functions at
>> http://burtleburtle.net/bob/hash/doobs.html.
>>
>> Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
>> ---
>>  include/linux/jhash.h |  136 +++-----------------------------------------
>>  lib/Makefile          |    2 +-
>>  lib/jhash.c           |  153 +++++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 162 insertions(+), 129 deletions(-)
>>  create mode 100644 lib/jhash.c
>>
> ...
>
> I agree jhash() should be not be inlined.
>
> I am not sure for other variants.
>
>> +u32 jhash(const void *key, u32 length, u32 initval)
>> +{
>> +     u32 a, b, c;
>> +     const u8 *k = key;
>> +
>> +     /* Set up the internal state */
>> +     a = b = c = JHASH_INITVAL + length + initval;
>> +
>> +     /* All but the last block: affect some 32 bits of (a,b,c) */
>> +     while (length > 12) {
>> +             a += k[0] + ((u32)k[1]<<8) + ((u32)k[2]<<16) + ((u32)k[3]<<24);
>
> disassembly code on x86_32 for the previous line :
>
>  26:   66 90                   xchg   %ax,%ax
>  28:   0f b6 72 01             movzbl 0x1(%edx),%esi
>  2c:   0f b6 4a 02             movzbl 0x2(%edx),%ecx
>  30:   c1 e6 08                shl    $0x8,%esi
>  33:   c1 e1 10                shl    $0x10,%ecx
>  36:   8d 0c 0e                lea    (%esi,%ecx,1),%ecx
>  39:   0f b6 32                movzbl (%edx),%esi
>  3c:   8d 34 31                lea    (%ecx,%esi,1),%esi
>  3f:   0f b6 4a 03             movzbl 0x3(%edx),%ecx
>  43:   c1 e1 18                shl    $0x18,%ecx
>  46:   8d 0c 0e                lea    (%esi,%ecx,1),%ecx
>
> or (CONFIG_CC_OPTIMIZE_FOR_SIZE=y) :
>
>  1b:   0f b6 7b 01             movzbl 0x1(%ebx),%edi
>  1f:   c1 e7 08                shl    $0x8,%edi
>  22:   89 7d f0                mov    %edi,-0x10(%ebp)
>  25:   0f b6 7b 02             movzbl 0x2(%ebx),%edi
>  29:   c1 e7 10                shl    $0x10,%edi
>  2c:   03 7d f0                add    -0x10(%ebp),%edi
>  2f:   89 7d f0                mov    %edi,-0x10(%ebp)
>  32:   0f b6 3b                movzbl (%ebx),%edi
>  35:   03 7d f0                add    -0x10(%ebp),%edi
>  38:   89 7d f0                mov    %edi,-0x10(%ebp)
>  3b:   0f b6 7b 03             movzbl 0x3(%ebx),%edi
>  3f:   c1 e7 18                shl    $0x18,%edi
>  42:   03 7d f0                add    -0x10(%ebp),%edi
>
>
> I suggest :
>
> #include <linux/unaligned/packed_struct.h>
> ...
>        a += __get_unaligned_cpu32(k);
>        b += __get_unaligned_cpu32(k+4);
>        c += __get_unaligned_cpu32(k+8);
>
> Fits nicely in registers.
>

I think you mean get_unaligned_le32().

-- 
Regards,
Changli Gao(xiaosuo@gmail.com)

^ permalink raw reply

* Re: [PATCH 2/2] The new jhash implementation
From: Eric Dumazet @ 2010-11-25 13:49 UTC (permalink / raw)
  To: Jozsef Kadlecsik
  Cc: linux-kernel, netdev, netfilter-devel, Linus Torvalds,
	Rusty Russell
In-Reply-To: <1290690908-794-3-git-send-email-kadlec@blackhole.kfki.hu>

Le jeudi 25 novembre 2010 à 14:15 +0100, Jozsef Kadlecsik a écrit :
> The current jhash.h implements the lookup2() hash function by Bob Jenkins.
> However, lookup2() is outdated as Bob wrote a new hash function called
> lookup3(). The new hash function
> 
> - mixes better than lookup2(): it passes the check that every input bit
>   changes every output bit 50% of the time, while lookup2() failed it.
> - performs better: compiled with -O2 on Core2 Duo, lookup3() 20-40% faster
>   than lookup2() depending on the key length.
> 
> The patch replaces the lookup2() implementation of the 'jhash*'
> functions with that of lookup3().
> 
> You can read a longer comparison of the two and other hash functions at
> http://burtleburtle.net/bob/hash/doobs.html.
> 
> Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
> ---
>  include/linux/jhash.h |  136 +++-----------------------------------------
>  lib/Makefile          |    2 +-
>  lib/jhash.c           |  153 +++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 162 insertions(+), 129 deletions(-)
>  create mode 100644 lib/jhash.c
> 
...

I agree jhash() should be not be inlined.

I am not sure for other variants.

> +u32 jhash(const void *key, u32 length, u32 initval)
> +{
> +	u32 a, b, c;
> +	const u8 *k = key;
> +
> +	/* Set up the internal state */
> +	a = b = c = JHASH_INITVAL + length + initval;
> +
> +	/* All but the last block: affect some 32 bits of (a,b,c) */
> +	while (length > 12) {
> +		a += k[0] + ((u32)k[1]<<8) + ((u32)k[2]<<16) + ((u32)k[3]<<24);

disassembly code on x86_32 for the previous line :

  26:	66 90                	xchg   %ax,%ax
  28:	0f b6 72 01          	movzbl 0x1(%edx),%esi
  2c:	0f b6 4a 02          	movzbl 0x2(%edx),%ecx
  30:	c1 e6 08             	shl    $0x8,%esi
  33:	c1 e1 10             	shl    $0x10,%ecx
  36:	8d 0c 0e             	lea    (%esi,%ecx,1),%ecx
  39:	0f b6 32             	movzbl (%edx),%esi
  3c:	8d 34 31             	lea    (%ecx,%esi,1),%esi
  3f:	0f b6 4a 03          	movzbl 0x3(%edx),%ecx
  43:	c1 e1 18             	shl    $0x18,%ecx
  46:	8d 0c 0e             	lea    (%esi,%ecx,1),%ecx

or (CONFIG_CC_OPTIMIZE_FOR_SIZE=y) :

  1b:	0f b6 7b 01          	movzbl 0x1(%ebx),%edi
  1f:	c1 e7 08             	shl    $0x8,%edi
  22:	89 7d f0             	mov    %edi,-0x10(%ebp)
  25:	0f b6 7b 02          	movzbl 0x2(%ebx),%edi
  29:	c1 e7 10             	shl    $0x10,%edi
  2c:	03 7d f0             	add    -0x10(%ebp),%edi
  2f:	89 7d f0             	mov    %edi,-0x10(%ebp)
  32:	0f b6 3b             	movzbl (%ebx),%edi
  35:	03 7d f0             	add    -0x10(%ebp),%edi
  38:	89 7d f0             	mov    %edi,-0x10(%ebp)
  3b:	0f b6 7b 03          	movzbl 0x3(%ebx),%edi
  3f:	c1 e7 18             	shl    $0x18,%edi
  42:	03 7d f0             	add    -0x10(%ebp),%edi


I suggest :

#include <linux/unaligned/packed_struct.h>
...
	a += __get_unaligned_cpu32(k);
	b += __get_unaligned_cpu32(k+4);
	c += __get_unaligned_cpu32(k+8);

Fits nicely in registers.
 



> +		b += k[4] + ((u32)k[5]<<8) + ((u32)k[6]<<16) + ((u32)k[7]<<24);
> +		c += k[8] + ((u32)k[9]<<8) + ((u32)k[10]<<16) + ((u32)k[11]<<24);
> +		__jhash_mix(a, b, c);
> +		length -= 12;
> +		k += 12;
> +	}
> +	/* Last block: affect all 32 bits of (c) */
> +	/* All the case statements fall through */
> +	switch (length) {
> +	case 12: c += (u32)k[11]<<24;
> +	case 11: c += (u32)k[10]<<16;
> +	case 10: c += (u32)k[9]<<8;
> +	case 9:  c += k[8];
> +	case 8:  b += (u32)k[7]<<24;
> +	case 7:  b += (u32)k[6]<<16;
> +	case 6:  b += (u32)k[5]<<8;
> +	case 5:  b += k[4];
> +	case 4:  a += (u32)k[3]<<24;
> +	case 3:  a += (u32)k[2]<<16;
> +	case 2:  a += (u32)k[1]<<8;
> +	case 1:  a += k[0];
> +		 __jhash_final(a, b, c);
> +	case 0: /* Nothing left to add */
> +		break;
> +	}
> +
> +	return c;
> +}
> +EXPORT_SYMBOL(jhash);

^ permalink raw reply

* [PATCHv2] hso: fix disable_net
From: Filip Aben @ 2010-11-25 13:40 UTC (permalink / raw)
  To: davem-fT/PcQaiUtIeIZ0/mPfg9Q
  Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
	jhovold-Re5JQEeQqe8AvxtiuMwx3w, pki-/L4m51SJ8HhmR6Xm/wNWPw

The HSO driver incorrectly creates a serial device instead of a net
device when disable_net is set. It shouldn't create anything for the
network interface.

Signed-off-by: Filip Aben <f.aben-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
---
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index b154a94..1b755ae 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -2994,12 +2994,14 @@ static int hso_probe(struct usb_interface *interface,
 
 	case HSO_INTF_BULK:
 		/* It's a regular bulk interface */
-		if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) &&
-		    !disable_net)
-			hso_dev = hso_create_net_device(interface, port_spec);
-		else
+		if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { 
+			if(!disable_net)
+				hso_dev =
+				    hso_create_net_device(interface, port_spec);
+		} else {
 			hso_dev =
 			    hso_create_bulk_serial_device(interface, port_spec);
+		}
 		if (!hso_dev)
 			goto exit;
 		break;

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH 1/2] Prepare the tree for un-inlined jhash.
From: Jan Engelhardt @ 2010-11-25 13:39 UTC (permalink / raw)
  To: Jozsef Kadlecsik
  Cc: linux-kernel, netdev, netfilter-devel, Linus Torvalds,
	Rusty Russell
In-Reply-To: <1290690908-794-2-git-send-email-kadlec@blackhole.kfki.hu>

On Thursday 2010-11-25 14:15, Jozsef Kadlecsik wrote:

>jhash is widely used in the kernel and because the functions
>are inlined, the cost in size is significant. Also, the new jhash
>functions are slightly larger than the previous ones so better un-inline.
>As a preparation step, the calls to the internal macros are replaced
>with the plain jhash function calls.

Do you have a non-normative allyesconfig/allmodconfig build whose 
size(1) you can run on, to show approximately just how much it differs?


thanks,
Jan

^ permalink raw reply

* [PATCH 2/2] The new jhash implementation
From: Jozsef Kadlecsik @ 2010-11-25 13:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: netdev, netfilter-devel, Linus Torvalds, Rusty Russell,
	Jozsef Kadlecsik
In-Reply-To: <1290690908-794-2-git-send-email-kadlec@blackhole.kfki.hu>

The current jhash.h implements the lookup2() hash function by Bob Jenkins.
However, lookup2() is outdated as Bob wrote a new hash function called
lookup3(). The new hash function

- mixes better than lookup2(): it passes the check that every input bit
  changes every output bit 50% of the time, while lookup2() failed it.
- performs better: compiled with -O2 on Core2 Duo, lookup3() 20-40% faster
  than lookup2() depending on the key length.

The patch replaces the lookup2() implementation of the 'jhash*'
functions with that of lookup3().

You can read a longer comparison of the two and other hash functions at
http://burtleburtle.net/bob/hash/doobs.html.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
---
 include/linux/jhash.h |  136 +++-----------------------------------------
 lib/Makefile          |    2 +-
 lib/jhash.c           |  153 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 162 insertions(+), 129 deletions(-)
 create mode 100644 lib/jhash.c

diff --git a/include/linux/jhash.h b/include/linux/jhash.h
index ced1159..ca69ac3 100644
--- a/include/linux/jhash.h
+++ b/include/linux/jhash.h
@@ -1,134 +1,14 @@
 #ifndef _LINUX_JHASH_H
 #define _LINUX_JHASH_H
 
-/* jhash.h: Jenkins hash support.
- *
- * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
- *
- * http://burtleburtle.net/bob/hash/
- *
- * These are the credits from Bob's sources:
- *
- * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
- * hash(), hash2(), hash3, and mix() are externally useful functions.
- * Routines to test the hash are included if SELF_TEST is defined.
- * You can use this free for any purpose.  It has no warranty.
- *
- * Copyright (C) 2003 David S. Miller (davem@redhat.com)
- *
- * I've modified Bob's hash to be useful in the Linux kernel, and
- * any bugs present are surely my fault.  -DaveM
- */
-
-/* NOTE: Arguments are modified. */
-#define __jhash_mix(a, b, c) \
-{ \
-  a -= b; a -= c; a ^= (c>>13); \
-  b -= c; b -= a; b ^= (a<<8); \
-  c -= a; c -= b; c ^= (b>>13); \
-  a -= b; a -= c; a ^= (c>>12);  \
-  b -= c; b -= a; b ^= (a<<16); \
-  c -= a; c -= b; c ^= (b>>5); \
-  a -= b; a -= c; a ^= (c>>3);  \
-  b -= c; b -= a; b ^= (a<<10); \
-  c -= a; c -= b; c ^= (b>>15); \
-}
-
-/* The golden ration: an arbitrary value */
-#define JHASH_GOLDEN_RATIO	0x9e3779b9
-
-/* The most generic version, hashes an arbitrary sequence
- * of bytes.  No alignment or length assumptions are made about
- * the input key.
- */
-static inline u32 jhash(const void *key, u32 length, u32 initval)
-{
-	u32 a, b, c, len;
-	const u8 *k = key;
-
-	len = length;
-	a = b = JHASH_GOLDEN_RATIO;
-	c = initval;
-
-	while (len >= 12) {
-		a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24));
-		b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24));
-		c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24));
-
-		__jhash_mix(a,b,c);
-
-		k += 12;
-		len -= 12;
-	}
-
-	c += length;
-	switch (len) {
-	case 11: c += ((u32)k[10]<<24);
-	case 10: c += ((u32)k[9]<<16);
-	case 9 : c += ((u32)k[8]<<8);
-	case 8 : b += ((u32)k[7]<<24);
-	case 7 : b += ((u32)k[6]<<16);
-	case 6 : b += ((u32)k[5]<<8);
-	case 5 : b += k[4];
-	case 4 : a += ((u32)k[3]<<24);
-	case 3 : a += ((u32)k[2]<<16);
-	case 2 : a += ((u32)k[1]<<8);
-	case 1 : a += k[0];
-	};
-
-	__jhash_mix(a,b,c);
-
-	return c;
-}
-
-/* A special optimized version that handles 1 or more of u32s.
- * The length parameter here is the number of u32s in the key.
- */
-static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
-{
-	u32 a, b, c, len;
-
-	a = b = JHASH_GOLDEN_RATIO;
-	c = initval;
-	len = length;
-
-	while (len >= 3) {
-		a += k[0];
-		b += k[1];
-		c += k[2];
-		__jhash_mix(a, b, c);
-		k += 3; len -= 3;
-	}
-
-	c += length * 4;
-
-	switch (len) {
-	case 2 : b += k[1];
-	case 1 : a += k[0];
-	};
-
-	__jhash_mix(a,b,c);
-
-	return c;
-}
-
-
-/* A special ultra-optimized versions that knows they are hashing exactly
- * 3, 2 or 1 word(s).
- *
- * NOTE: In particular the "c += length; __jhash_mix(a,b,c);" normally
- *       done at the end is not done here.
- */
-static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
-{
-	a += JHASH_GOLDEN_RATIO;
-	b += JHASH_GOLDEN_RATIO;
-	c += initval;
-
-	__jhash_mix(a, b, c);
-
-	return c;
-}
+/* Best hash sizes are of power of two */
+#define jhash_size(n)   ((u32)1<<(n))
+/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */
+#define jhash_mask(n)   (jhash_size(n)-1)
+
+extern u32 jhash(const void *key, u32 length, u32 initval);
+extern u32 jhash2(const u32 *k, u32 length, u32 initval);
+extern u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval);
 
 static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
 {
diff --git a/lib/Makefile b/lib/Makefile
index e6a3763..a1a4932 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -10,7 +10,7 @@ endif
 lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 rbtree.o radix-tree.o dump_stack.o \
 	 idr.o int_sqrt.o extable.o prio_tree.o \
-	 sha1.o irq_regs.o reciprocal_div.o argv_split.o \
+	 jhash.o sha1.o irq_regs.o reciprocal_div.o argv_split.o \
 	 proportions.o prio_heap.o ratelimit.o show_mem.o \
 	 is_single_threaded.o plist.o decompress.o flex_array.o
 
diff --git a/lib/jhash.c b/lib/jhash.c
new file mode 100644
index 0000000..593cc77
--- /dev/null
+++ b/lib/jhash.c
@@ -0,0 +1,153 @@
+/* jhash.c: Jenkins hash support.
+ *
+ * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ *
+ * These are functions for producing 32-bit hashes for hash table lookup.
+ * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+ * are externally useful functions.  Routines to test the hash are included
+ * if SELF_TEST is defined.  You can use this free for any purpose.  It's in
+ * the public domain.  It has no warranty.
+ *
+ * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are my fault.
+ * Jozsef
+ */
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/jhash.h>
+
+/* __jhash_mix -- mix 3 32-bit values reversibly. */
+#define __jhash_mix(a, b, c)			\
+{						\
+	a -= c;  a ^= rol32(c, 4);  c += b;	\
+	b -= a;  b ^= rol32(a, 6);  a += c;	\
+	c -= b;  c ^= rol32(b, 8);  b += a;	\
+	a -= c;  a ^= rol32(c, 16); c += b;	\
+	b -= a;  b ^= rol32(a, 19); a += c;	\
+	c -= b;  c ^= rol32(b, 4);  b += a;	\
+}
+
+/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
+#define __jhash_final(a, b, c)			\
+{						\
+	c ^= b; c -= rol32(b, 14);		\
+	a ^= c; a -= rol32(c, 11);		\
+	b ^= a; b -= rol32(a, 25);		\
+	c ^= b; c -= rol32(b, 16);		\
+	a ^= c; a -= rol32(c, 4);		\
+	b ^= a; b -= rol32(a, 14);		\
+	c ^= b; c -= rol32(b, 24);		\
+}
+
+/* An arbitrary initial parameter */
+#define JHASH_INITVAL		0xdeadbeef
+
+/* jhash - hash an arbitrary key
+ * @k: sequence of bytes as key
+ * @length: the length of the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * The generic version, hashes an arbitrary sequence of bytes.
+ * No alignment or length assumptions are made about the input key.
+ *
+ * Returns the hash value of the key. The result depends on endianness.
+ */
+u32 jhash(const void *key, u32 length, u32 initval)
+{
+	u32 a, b, c;
+	const u8 *k = key;
+
+	/* Set up the internal state */
+	a = b = c = JHASH_INITVAL + length + initval;
+
+	/* All but the last block: affect some 32 bits of (a,b,c) */
+	while (length > 12) {
+		a += k[0] + ((u32)k[1]<<8) + ((u32)k[2]<<16) + ((u32)k[3]<<24);
+		b += k[4] + ((u32)k[5]<<8) + ((u32)k[6]<<16) + ((u32)k[7]<<24);
+		c += k[8] + ((u32)k[9]<<8) + ((u32)k[10]<<16) + ((u32)k[11]<<24);
+		__jhash_mix(a, b, c);
+		length -= 12;
+		k += 12;
+	}
+	/* Last block: affect all 32 bits of (c) */
+	/* All the case statements fall through */
+	switch (length) {
+	case 12: c += (u32)k[11]<<24;
+	case 11: c += (u32)k[10]<<16;
+	case 10: c += (u32)k[9]<<8;
+	case 9:  c += k[8];
+	case 8:  b += (u32)k[7]<<24;
+	case 7:  b += (u32)k[6]<<16;
+	case 6:  b += (u32)k[5]<<8;
+	case 5:  b += k[4];
+	case 4:  a += (u32)k[3]<<24;
+	case 3:  a += (u32)k[2]<<16;
+	case 2:  a += (u32)k[1]<<8;
+	case 1:  a += k[0];
+		 __jhash_final(a, b, c);
+	case 0: /* Nothing left to add */
+		break;
+	}
+
+	return c;
+}
+EXPORT_SYMBOL(jhash);
+
+/* jhash2 - hash an array of u32's
+ * @k: the key which must be an array of u32's
+ * @length: the number of u32's in the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * Returns the hash value of the key.
+ */
+u32 jhash2(const u32 *k, u32 length, u32 initval)
+{
+	u32 a, b, c;
+
+	/* Set up the internal state */
+	a = b = c = JHASH_INITVAL + (length<<2) + initval;
+
+	/* Handle most of the key */
+	while (length > 3) {
+		a += k[0];
+		b += k[1];
+		c += k[2];
+		__jhash_mix(a, b, c);
+		length -= 3;
+		k += 3;
+	}
+
+	/* Handle the last 3 u32's: all the case statements fall through */
+	switch (length) {
+	case 3: c += k[2];
+	case 2: b += k[1];
+	case 1: a += k[0];
+		__jhash_final(a, b, c);
+	case 0:	/* Nothing left to add */
+		break;
+	}
+
+	return c;
+}
+EXPORT_SYMBOL(jhash2);
+
+/* jhash_3words - hash exactly 3, 2 or 1 word(s) */
+u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
+{
+	a += JHASH_INITVAL;
+	b += JHASH_INITVAL;
+	c += initval;
+
+	__jhash_mix(a, b, c);
+
+	return c;
+}
+EXPORT_SYMBOL(jhash_3words);
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 1/2] Prepare the tree for un-inlined jhash.
From: Jozsef Kadlecsik @ 2010-11-25 13:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: netdev, netfilter-devel, Linus Torvalds, Rusty Russell,
	Jozsef Kadlecsik
In-Reply-To: <1290690908-794-1-git-send-email-kadlec@blackhole.kfki.hu>

jhash is widely used in the kernel and because the functions
are inlined, the cost in size is significant. Also, the new jhash
functions are slightly larger than the previous ones so better un-inline.
As a preparation step, the calls to the internal macros are replaced
with the plain jhash function calls.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
---
 net/ipv6/inet6_connection_sock.c |   18 ++++++++----------
 net/ipv6/reassembly.c            |   36 ++++++++++++++++--------------------
 2 files changed, 24 insertions(+), 30 deletions(-)

diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 8a16280..861d252 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -60,18 +60,16 @@ EXPORT_SYMBOL_GPL(inet6_csk_bind_conflict);
 static u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport,
 			   const u32 rnd, const u16 synq_hsize)
 {
-	u32 a = (__force u32)raddr->s6_addr32[0];
-	u32 b = (__force u32)raddr->s6_addr32[1];
-	u32 c = (__force u32)raddr->s6_addr32[2];
-
-	a += JHASH_GOLDEN_RATIO;
-	b += JHASH_GOLDEN_RATIO;
-	c += rnd;
-	__jhash_mix(a, b, c);
-
-	a += (__force u32)raddr->s6_addr32[3];
-	b += (__force u32)rport;
-	__jhash_mix(a, b, c);
+	u32 c;
+
+	c = jhash_3words((__force u32)raddr->s6_addr32[0],
+			 (__force u32)raddr->s6_addr32[1],
+			 (__force u32)raddr->s6_addr32[2],
+			 rnd);
+
+	c = jhash_2words((__force u32)raddr->s6_addr32[3],
+			 (__force u32)rport,
+			 c);
 
 	return c & (synq_hsize - 1);
 }
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index c7ba314..5e57490 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -104,26 +104,22 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
 unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr,
 			     const struct in6_addr *daddr, u32 rnd)
 {
-	u32 a, b, c;
-
-	a = (__force u32)saddr->s6_addr32[0];
-	b = (__force u32)saddr->s6_addr32[1];
-	c = (__force u32)saddr->s6_addr32[2];
-
-	a += JHASH_GOLDEN_RATIO;
-	b += JHASH_GOLDEN_RATIO;
-	c += rnd;
-	__jhash_mix(a, b, c);
-
-	a += (__force u32)saddr->s6_addr32[3];
-	b += (__force u32)daddr->s6_addr32[0];
-	c += (__force u32)daddr->s6_addr32[1];
-	__jhash_mix(a, b, c);
-
-	a += (__force u32)daddr->s6_addr32[2];
-	b += (__force u32)daddr->s6_addr32[3];
-	c += (__force u32)id;
-	__jhash_mix(a, b, c);
+	u32 c;
+
+	c = jhash_3words((__force u32)saddr->s6_addr32[0],
+			 (__force u32)saddr->s6_addr32[1],
+			 (__force u32)saddr->s6_addr32[2],
+			 rnd);
+
+	c = jhash_3words((__force u32)saddr->s6_addr32[3],
+			 (__force u32)daddr->s6_addr32[0],
+			 (__force u32)daddr->s6_addr32[1],
+			 c);
+
+	c =  jhash_3words((__force u32)daddr->s6_addr32[2],
+			  (__force u32)daddr->s6_addr32[3],
+			  (__force u32)id,
+			  c);
 
 	return c & (INETFRAGS_HASHSZ - 1);
 }
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 0/2] New jhash function
From: Jozsef Kadlecsik @ 2010-11-25 13:15 UTC (permalink / raw)
  To: linux-kernel
  Cc: netdev, netfilter-devel, Linus Torvalds, Rusty Russell,
	Jozsef Kadlecsik

Hi,

Please consider applying the following patch:

The current jhash.h implements the lookup2() hash function by Bob Jenkins. 
However, lookup2() is outdated as Bob wrote a new hash function called 
lookup3(). The new hash function

- mixes better than lookup2(): it passes the check that every input bit 
  changes every output bit 50% of the time, while lookup2() failed it.
- performs better: compiled with -O2 on Core2 Duo, lookup3() 20-40% faster
  than lookup2() depending on the key length.

You can read a longer comparison of the two and other hash functions at 
http://burtleburtle.net/bob/hash/doobs.html.

jhash is widely used in the kernel and because the functions
are inlined, the cost in size is significant. The new functions
are slightly larger than the previous ones so the new implementation
uses non-inlined fucntions. (See
http://lkml.indiana.edu/hypermail//linux/kernel/0902.2/01149.html).
Therefore the first patch replaces the calls to the internal macros
of jhash with the jhash function calls and the second patch contains
the implementation of the new jhash functions.

Jozsef Kadlecsik (2):
  Prepare the tree for un-inlined jhash.
  The new jhash implementation

 include/linux/jhash.h            |  136 ++-------------------------------
 lib/Makefile                     |    2 +-
 lib/jhash.c                      |  153 ++++++++++++++++++++++++++++++++++++++
 net/ipv6/inet6_connection_sock.c |   18 ++---
 net/ipv6/reassembly.c            |   36 ++++-----
 5 files changed, 186 insertions(+), 159 deletions(-)
 create mode 100644 lib/jhash.c


^ permalink raw reply

* Re: [PATCH 0/8] ipvs: ipvs update for nf-next-2.6
From: Patrick McHardy @ 2010-11-25 13:03 UTC (permalink / raw)
  To: Simon Horman
  Cc: lvs-devel, netdev, netfilter, netfilter-devel, Hans Schillstrom,
	Julian Anastasov
In-Reply-To: <1290649618-18955-1-git-send-email-horms@verge.net.au>

Am 25.11.2010 02:46, schrieb Simon Horman:
> git://git.kernel.org/pub/scm/linux/kernel/git/horms/lvs-test-2.6.git master

Pulled, thanks Simon.

^ permalink raw reply

* Re: [PATCH] hso: fix disable_net
From: Johan Hovold @ 2010-11-25 12:52 UTC (permalink / raw)
  To: Filip Aben; +Cc: davem, linux-usb, netdev, pki, j.dumon
In-Reply-To: <1290627352.1739.19.camel@filip-linux>

On Wed, Nov 24, 2010 at 8:35 PM, Filip Aben <f.aben@option.com> wrote:
> The HSO driver incorrectly creates a serial device instead of a net
> device when disable_net is set. It shouldn't create anything for the
> network interface.

Looks good apart from the indentation issue pointed out by Sergei (you
should also
indent the hso_create_net_device line).

As I mentioned in another thread, this was actually suggested about a year ago
but was never accepted due to patch corruption (the other two patches submitted
as part of that series also seem to have been plain wrong, though):

	http://kerneltrap.org/mailarchive/linux-netdev/2009/11/19/6261572

Thanks,
Johan

^ permalink raw reply

* Re: [PATCH net-next-2.6 v3] can: Topcliff: PCH_CAN driver: Add Flow control,
From: Marc Kleine-Budde @ 2010-11-25 12:40 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w, Samuel Ortiz,
	margie.foster-ral2JQCrhuEAvxtiuMwx3w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	yong.y.wang-ral2JQCrhuEAvxtiuMwx3w,
	socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	kok.howg.ewe-ral2JQCrhuEAvxtiuMwx3w,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w, qi.wang-ral2JQCrhuEAvxtiuMwx3w,
	David S. Miller, Christian Pellegrin, Wolfgang Grandegger
In-Reply-To: <002f01cb8c9d$103f7760$66f8800a-a06+6cuVnkTSQfdrb5gaxUEOCMrvLtNR@public.gmane.org>


[-- Attachment #1.1: Type: text/plain, Size: 734 bytes --]

On 11/25/2010 01:34 PM, Tomoya MORINAGA wrote:
> On Thursday, November 25, 2010 9:08 PM, Marc Kleine-Budde wrote : 
>> Why do you Read-Modify-Write for TX? Naively speaking you just need to
>> push your Data into a Mail/IF/Whatever and push the send button.
> 
> I see.
> I will implement this.

With "naively speaking", I mean I haven't looked at the Datasheet, maybe
in the real world with real hardware it's needed to do a RMW.

regard, Marc

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


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

[-- Attachment #2: Type: text/plain, Size: 188 bytes --]

_______________________________________________
Socketcan-core mailing list
Socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
https://lists.berlios.de/mailman/listinfo/socketcan-core

^ permalink raw reply

* Re: [PATCH net-next-2.6 v3] can: Topcliff: PCH_CAN driver: Add Flow control,
From: Tomoya MORINAGA @ 2010-11-25 12:34 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: andrew.chih.howe.khor-ral2JQCrhuEAvxtiuMwx3w, Samuel Ortiz,
	margie.foster-ral2JQCrhuEAvxtiuMwx3w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	yong.y.wang-ral2JQCrhuEAvxtiuMwx3w,
	socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	kok.howg.ewe-ral2JQCrhuEAvxtiuMwx3w,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w, qi.wang-ral2JQCrhuEAvxtiuMwx3w,
	David S. Miller, Christian Pellegrin, Wolfgang Grandegger
In-Reply-To: <4CEE51CA.8010402@pengutronix.de>

On Thursday, November 25, 2010 9:08 PM, Marc Kleine-Budde wrote : 
> Why do you Read-Modify-Write for TX? Naively speaking you just need to
> push your Data into a Mail/IF/Whatever and push the send button.

I see.
I will implement this.

--
Thanks,

Tomoya MORINAGA
OKI SEMICONDUCTOR CO., LTD.

^ permalink raw reply

* [PATCH 6/8] ethoc: rework mdio read/write
From: Jonas Bonn @ 2010-11-25 12:30 UTC (permalink / raw)
  To: netdev; +Cc: Jonas Bonn
In-Reply-To: <1290688232-25142-1-git-send-email-jonas@southpole.se>

MDIO read and write were checking whether a timeout had expired to determine
whether to recheck the result of the MDIO operation.  Under heavy CPU usage,
however, it was possible for the timeout to expire before the routine got
around to be able to check a second time even, thus erroneousy returning an
-EBUSY.

This patch changes the the MDIO IO routines to try up to five times to complete
the operation before giving up, thus lessening the dependency on CPU load.

This resolves a problem whereby a ping flood would keep the CPU so busy that
the above problem would manifest itself; the MDIO command to check link status
would fail and the interface would erroneously be shut down.

Signed-off-by: Jonas Bonn <jonas@southpole.se>
---
 drivers/net/ethoc.c |   14 ++++++--------
 1 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index 43431ff..f3048fa 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -611,13 +611,13 @@ static int ethoc_poll(struct napi_struct *napi, int budget)
 
 static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg)
 {
-	unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT;
 	struct ethoc *priv = bus->priv;
+	int i;
 
 	ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg));
 	ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
 
-	while (time_before(jiffies, timeout)) {
+	for (i=0; i < 5; i++) {
 		u32 status = ethoc_read(priv, MIISTATUS);
 		if (!(status & MIISTATUS_BUSY)) {
 			u32 data = ethoc_read(priv, MIIRX_DATA);
@@ -625,8 +625,7 @@ static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg)
 			ethoc_write(priv, MIICOMMAND, 0);
 			return data;
 		}
-
-		schedule();
+		usleep_range(100,200);
 	}
 
 	return -EBUSY;
@@ -634,22 +633,21 @@ static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg)
 
 static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
 {
-	unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT;
 	struct ethoc *priv = bus->priv;
+	int i;
 
 	ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg));
 	ethoc_write(priv, MIITX_DATA, val);
 	ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
 
-	while (time_before(jiffies, timeout)) {
+	for (i=0; i < 5; i++) {
 		u32 stat = ethoc_read(priv, MIISTATUS);
 		if (!(stat & MIISTATUS_BUSY)) {
 			/* reset MII command register */
 			ethoc_write(priv, MIICOMMAND, 0);
 			return 0;
 		}
-
-		schedule();
+		usleep_range(100,200);
 	}
 
 	return -EBUSY;
-- 
1.7.1


^ permalink raw reply related

* [PATCH 8/8] ethoc: remove division from loops
From: Jonas Bonn @ 2010-11-25 12:30 UTC (permalink / raw)
  To: netdev; +Cc: Jonas Bonn
In-Reply-To: <1290688232-25142-1-git-send-email-jonas@southpole.se>

Calculating the BD entry using a modulus operation isn't optimal, especially
inside the loop.  This patch removes the modulus operations in favour of:

i)  simply checking for wrapping in the case of cur_rx
ii) forcing num_tx to be a power of two and using it to mask out the
    entry from cur_tx

The also prevents possible issues related overflow of the cur_rx and cur_tx
counters.

Signed-off-by: Jonas Bonn <jonas@southpole.se>
---
 drivers/net/ethoc.c |   17 +++++++++++++----
 1 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index 93b50d6..b79d7e1 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -412,7 +412,7 @@ static int ethoc_rx(struct net_device *dev, int limit)
 		unsigned int entry;
 		struct ethoc_bd bd;
 
-		entry = priv->num_tx + (priv->cur_rx % priv->num_rx);
+		entry = priv->num_tx + priv->cur_rx;
 		ethoc_read_bd(priv, entry, &bd);
 		if (bd.stat & RX_BD_EMPTY) {
 			ethoc_ack_irq(priv, INT_MASK_RX);
@@ -456,7 +456,8 @@ static int ethoc_rx(struct net_device *dev, int limit)
 		bd.stat &= ~RX_BD_STATS;
 		bd.stat |=  RX_BD_EMPTY;
 		ethoc_write_bd(priv, entry, &bd);
-		priv->cur_rx++;
+		if (++priv->cur_rx == priv->num_rx)
+			priv->cur_rx = 0;
 	}
 
 	return count;
@@ -503,7 +504,7 @@ static int ethoc_tx(struct net_device *dev, int limit)
 	for (count = 0; count < limit; ++count) {
 		unsigned int entry;
 
-		entry = priv->dty_tx % priv->num_tx;
+		entry = priv->dty_tx & (priv->num_tx-1);
 
 		ethoc_read_bd(priv, entry, &bd);
 
@@ -1000,9 +1001,17 @@ static int __devinit ethoc_probe(struct platform_device *pdev)
 	/* calculate the number of TX/RX buffers, maximum 128 supported */
 	num_bd = min_t(unsigned int,
 		128, (netdev->mem_end - netdev->mem_start + 1) / ETHOC_BUFSIZ);
-	priv->num_tx = max(2, num_bd / 4);
+	if (num_bd < 4) {
+		ret = -ENODEV;
+		goto error;
+	}
+	/* num_tx must be a power of two */
+	priv->num_tx = rounddown_pow_of_two(num_bd >> 1);
 	priv->num_rx = num_bd - priv->num_tx;
 
+	dev_dbg(&pdev->dev, "ethoc: num_tx: %d num_rx: %d\n",
+		priv->num_tx, priv->num_rx);
+
 	priv->vma = devm_kzalloc(&pdev->dev, num_bd*sizeof(void*), GFP_KERNEL);
 	if (!priv->vma) {
 		ret = -ENOMEM;
-- 
1.7.1


^ permalink raw reply related

* [PATCH 5/8] ethoc: rework interrupt handling
From: Jonas Bonn @ 2010-11-25 12:30 UTC (permalink / raw)
  To: netdev; +Cc: Jonas Bonn
In-Reply-To: <1290688232-25142-1-git-send-email-jonas@southpole.se>

The old interrupt handling was incorrect in that it did not account for the
fact that the interrupt source bits get set irregardless of whether or not
their corresponding mask is set.  This patch fixes that by masking off the
source bits for masked interrupts.

Furthermore, the handling of transmission events is moved to the NAPI polling
handler alongside the reception handler, thus preventing a whole bunch of
interrupts during heavy traffic.

Signed-off-by: Jonas Bonn <jonas@southpole.se>
---
 drivers/net/ethoc.c |   76 +++++++++++++++++++++++++++++++++------------------
 1 files changed, 49 insertions(+), 27 deletions(-)

diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index a12a07e..43431ff 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -495,29 +495,42 @@ static int ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd)
 	return 0;
 }
 
-static void ethoc_tx(struct net_device *dev)
+static int ethoc_tx(struct net_device *dev, int limit)
 {
 	struct ethoc *priv = netdev_priv(dev);
+	int count;
+	struct ethoc_bd bd;
 
-	spin_lock(&priv->lock);
+	for (count = 0; count < limit; ++count) {
+		unsigned int entry;
 
-	while (priv->dty_tx != priv->cur_tx) {
-		unsigned int entry = priv->dty_tx % priv->num_tx;
-		struct ethoc_bd bd;
+		entry = priv->dty_tx % priv->num_tx;
 
 		ethoc_read_bd(priv, entry, &bd);
-		if (bd.stat & TX_BD_READY)
-			break;
 
-		entry = (++priv->dty_tx) % priv->num_tx;
+		if (bd.stat & TX_BD_READY || (priv->dty_tx == priv->cur_tx)) {
+			ethoc_ack_irq(priv, INT_MASK_TX);
+			/* If interrupt came in between reading in the BD
+			 * and clearing the interrupt source, then we risk
+			 * missing the event as the TX interrupt won't trigger
+			 * right away when we reenable it; hence, check
+			 * BD_EMPTY here again to make sure there isn't such an
+			 * event pending...
+			 */
+			ethoc_read_bd(priv, entry, &bd);
+			if (bd.stat & TX_BD_READY ||
+			    (priv->dty_tx == priv->cur_tx))
+				break;
+		}
+
 		(void)ethoc_update_tx_stats(priv, &bd);
+		priv->dty_tx++;
 	}
 
 	if ((priv->cur_tx - priv->dty_tx) <= (priv->num_tx / 2))
 		netif_wake_queue(dev);
 
-	ethoc_ack_irq(priv, INT_MASK_TX);
-	spin_unlock(&priv->lock);
+	return count;
 }
 
 static irqreturn_t ethoc_interrupt(int irq, void *dev_id)
@@ -525,32 +538,38 @@ static irqreturn_t ethoc_interrupt(int irq, void *dev_id)
 	struct net_device *dev = dev_id;
 	struct ethoc *priv = netdev_priv(dev);
 	u32 pending;
-
-	ethoc_disable_irq(priv, INT_MASK_ALL);
+	u32 mask;
+
+	/* Figure out what triggered the interrupt...
+	 * The tricky bit here is that the interrupt source bits get
+	 * set in INT_SOURCE for an event irregardless of whether that
+	 * event is masked or not.  Thus, in order to figure out what
+	 * triggered the interrupt, we need to remove the sources
+	 * for all events that are currently masked.  This behaviour
+	 * is not particularly well documented but reasonable...
+	 */
+	mask = ethoc_read(priv, INT_MASK);
 	pending = ethoc_read(priv, INT_SOURCE);
+	pending &= mask;
+
 	if (unlikely(pending == 0)) {
-		ethoc_enable_irq(priv, INT_MASK_ALL);
 		return IRQ_NONE;
 	}
 
 	ethoc_ack_irq(priv, pending);
 
+	/* We always handle the dropped packet interrupt */
 	if (pending & INT_MASK_BUSY) {
 		dev_err(&dev->dev, "packet dropped\n");
 		dev->stats.rx_dropped++;
 	}
 
-	if (pending & INT_MASK_RX) {
-		if (napi_schedule_prep(&priv->napi))
-			__napi_schedule(&priv->napi);
-	} else {
-		ethoc_enable_irq(priv, INT_MASK_RX);
+	/* Handle receive/transmit event by switching to polling */
+	if (pending & (INT_MASK_TX | INT_MASK_RX)) {
+		ethoc_disable_irq(priv, INT_MASK_TX | INT_MASK_RX);
+		napi_schedule(&priv->napi);
 	}
 
-	if (pending & INT_MASK_TX)
-		ethoc_tx(dev);
-
-	ethoc_enable_irq(priv, INT_MASK_ALL & ~INT_MASK_RX);
 	return IRQ_HANDLED;
 }
 
@@ -576,15 +595,18 @@ static int ethoc_get_mac_address(struct net_device *dev, void *addr)
 static int ethoc_poll(struct napi_struct *napi, int budget)
 {
 	struct ethoc *priv = container_of(napi, struct ethoc, napi);
-	int work_done = 0;
+	int rx_work_done = 0;
+	int tx_work_done = 0;
+
+	rx_work_done = ethoc_rx(priv->netdev, budget);
+	tx_work_done = ethoc_tx(priv->netdev, budget);
 
-	work_done = ethoc_rx(priv->netdev, budget);
-	if (work_done < budget) {
+	if (rx_work_done < budget && tx_work_done < budget) {
 		napi_complete(napi);
-		ethoc_enable_irq(priv, INT_MASK_RX);
+		ethoc_enable_irq(priv, INT_MASK_TX | INT_MASK_RX);
 	}
 
-	return work_done;
+	return rx_work_done;
 }
 
 static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg)
-- 
1.7.1


^ permalink raw reply related

* [PATCH 7/8] ethoc: fix function return type
From: Jonas Bonn @ 2010-11-25 12:30 UTC (permalink / raw)
  To: netdev; +Cc: Jonas Bonn
In-Reply-To: <1290688232-25142-1-git-send-email-jonas@southpole.se>

update_ethoc_tx_stats doesn't need to return anything so make its return
type void in order to avoid an unnecessary cast when the function is called.

Signed-off-by: Jonas Bonn <jonas@southpole.se>
---
 drivers/net/ethoc.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index f3048fa..93b50d6 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -462,7 +462,7 @@ static int ethoc_rx(struct net_device *dev, int limit)
 	return count;
 }
 
-static int ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd)
+static void ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd)
 {
 	struct net_device *netdev = dev->netdev;
 
@@ -492,7 +492,6 @@ static int ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd)
 	netdev->stats.collisions += (bd->stat >> 4) & 0xf;
 	netdev->stats.tx_bytes += bd->stat >> 16;
 	netdev->stats.tx_packets++;
-	return 0;
 }
 
 static int ethoc_tx(struct net_device *dev, int limit)
@@ -523,7 +522,7 @@ static int ethoc_tx(struct net_device *dev, int limit)
 				break;
 		}
 
-		(void)ethoc_update_tx_stats(priv, &bd);
+		ethoc_update_tx_stats(priv, &bd);
 		priv->dty_tx++;
 	}
 
-- 
1.7.1


^ permalink raw reply related


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