Netdev List
 help / color / mirror / Atom feed
* [PATCH] tehuti: possible leak in bdx_probe
From: Florin Malita @ 2007-10-13 17:03 UTC (permalink / raw)
  To: baum, andy; +Cc: Andrew Morton, netdev

If pci_enable_device fails, bdx_probe returns without freeing the 
allocated pci_nic structure.

Coverity CID 1908.

Signed-off-by: Florin Malita <fmalita@gmail.com>
---

 drivers/net/tehuti.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c
index 8d04654..4e1b84e 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/tehuti.c
@@ -1906,7 +1906,7 @@ bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
     /************** pci *****************/
 	if ((err = pci_enable_device(pdev)))	/* it trigers interrupt, dunno why. */
-		RET(err);			/* it's not a problem though */
+		goto err_pci;			/* it's not a problem though */
 
 	if (!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK)) &&
 	    !(err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))) {
@@ -2076,6 +2076,7 @@ err_out_res:
 	pci_release_regions(pdev);
 err_dma:
 	pci_disable_device(pdev);
+err_pci:
 	vfree(nic);
 
 	RET(err);


^ permalink raw reply related

* Re: Bonding support for eth1394?
From: Roland Dreier @ 2007-10-13 15:34 UTC (permalink / raw)
  To: Stefan Richter; +Cc: Karl Svec, linux1394-devel, netdev
In-Reply-To: <47106EDE.1090601@s5r6.in-berlin.de>

 > The bonding sources have a few occurrences of EOPNOTSUPP.  Unless I
 > missed something, they are all related to setting the hardware address
 > of the interface.  AFAICS this is impossible with IP over FireWire.  If
 > it is crucial to bonding to be able to change the slaves' hardware
 > addresses, then you are out of luck.

There are a few changes to the bonding driver pending that will add
support for bonding IP-over-InfiniBand interfaces.  IPoIB also cannot
change its HW address, so the patches address that issue.

Once those patches land, bonding eth1394 interfaces may "just work".

 - R.

^ permalink raw reply

* Re: [patch 8/8] ssb bus needs <linux/io.h>
From: Michael Buesch @ 2007-10-13 15:23 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Linus Torvalds, Andrew Morton, linux-m68k, linux-kernel, netdev,
	John Linville
In-Reply-To: <20071013123414.647276232@mail.of.borg>

On Saturday 13 October 2007 14:31:31 Geert Uytterhoeven wrote:
> ssb bus needs <linux/io.h>
> 
> linux/drivers/ssb/main.c: In function 'ssb_ssb_read16':
> linux/drivers/ssb/main.c:518: error: implicit declaration of function 'readw'
> linux/drivers/ssb/main.c: In function 'ssb_ssb_read32':
> linux/drivers/ssb/main.c:526: error: implicit declaration of function 'readl'
> linux/drivers/ssb/main.c: In function 'ssb_ssb_write16':
> linux/drivers/ssb/main.c:534: error: implicit declaration of function 'writew'
> linux/drivers/ssb/main.c: In function 'ssb_ssb_write32':
> linux/drivers/ssb/main.c:542: error: implicit declaration of function 'writel'
> 
> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>

Signed-off-by: Michael Buesch <mb@bu3sch.de>

> ---
>  drivers/ssb/main.c |    1 +
>  1 file changed, 1 insertion(+)
> 
> --- a/drivers/ssb/main.c
> +++ b/drivers/ssb/main.c
> @@ -11,6 +11,7 @@
>  #include "ssb_private.h"
>  
>  #include <linux/delay.h>
> +#include <linux/io.h>
>  #include <linux/ssb/ssb.h>
>  #include <linux/ssb/ssb_regs.h>
>  #include <linux/dma-mapping.h>
> 



-- 
Greetings Michael.

^ permalink raw reply

* Re: [PATCH] IB/ipoib: Bound the net device to the ipoib_neigh structue
From: Moni Shoua @ 2007-10-13 15:24 UTC (permalink / raw)
  To: Roland Dreier
  Cc: Moni Shoua, Jay Vosburgh, jeff, David Miller, ogerlitz, netdev,
	Moni Levy
In-Reply-To: <ada3awhcsnw.fsf@cisco.com>

I will be near my lab only tomorrow...
I will check this and let you know.

On 10/11/07, Roland Dreier <rdreier@cisco.com> wrote:
>  > It happens only when ib interfaces are slaves of a bonding device.
>  > I thought before that the stuck is in napi_disable() but it's almost right.
>  > I put prints before and after call to napi_disable and see that it is called twice.
>  > I'll try to investigate in this direction.
>  >
>  > ib0: stopping interface
>  > ib0: before napi_disable
>  > ib0: after napi_disable
>  > ib0: downing ib_dev
>  > ib0: All sends and receives done.
>  > ib0: stopping interface
>  > ib0: before napi_disable
>
> Yes, two napi_disable()s in a row without a matching napi_enable()
> will deadlock.  I guess the question is why the ipoib interface is
> being stopped twice.
>
> If you just take the net-2.6.24 tree (without bonding patches), does
> bonding for ethernet interfaces work OK, or is there a similar problem
> with double napi_disable()?  How about bonding of ethernet after this
> batch of bonding patches?
>
>  - R.
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: Non-linear SKBs
From: Kristian Evensen @ 2007-10-13 14:53 UTC (permalink / raw)
  To: kristrev; +Cc: David Miller, netdev, linux-net
In-Reply-To: <50601.129.240.228.53.1192194927.squirrel@webmail.uio.no>

Reading through my mail again, I see that I was a bit unclear. What I 
want to achieve is to share a frag between to skbs (where one has no 
earlier referance to it). Sorry.

kristrev@student.matnat.uio.no wrote:
>> If the underlying device can do scatter-gather and checksumming,
>> the TCP code builds outgoing packets by copying user date into
>> full system pages, and then attaching those pages into the SKB.
>> The protocol headers sit under the skb->data linear area, and
>> the user data mostly sits in the user pages under
>> skb_shinfo(skb)->frags[]
>>
>> This increases the density of data packed into the memory allocated
>> compared to using skb->data for it.  It also enormously simplifies
>> the logic necessary to support TSO.
>>     
>
> Thank you very much, I think I am starting to get it now and coming to
> think of it this will make my patch much more elegant. I have spent the
> day reading more code, and am wondering if something along the likes of
> this piece of code will do what I want ("copy" the data from the next skb
> in the retransmission queue into this skb):
>
> //Do preliminary checks to see if the "new" packet will be within mss,
> that this_skb->nr_frags + next_skb->nr_frags < MAX_SKB_FRAGS and so on
>
> int i;
> int this_frags = this_skb->nr_frags;
>
> for(i=0; i<next_skb->nr_frags; i++)
>         //Does the "copy"
>         this_skb->frags[this_frags + i] = next_skb->frags[i];
>
> this_skb->data_len += next_skb->data_len;
> this_skb->truesize += next_skb->data_len;
> this_skb->nr_frags += next_skb->nr_frags;
>
> //Update TSO?
>
> By the way, am I correct in my assumption that one SKB's frags is stored
> linearly in the frags-array? Or have I made a horrible misunderstanding?
> :)
>
> One of the things that I have yet to understand is the frag_list in the
> skb_shared_info-struct. Does this contain all skb's that "use" this frag
> and works as a sort of referance counter (frag won't be removed until the
> variable is NULL and I have to append this_skb to the list), or is it
> something else?
>
> Thanks again for all help.
>
> -Kristian
>
>   


^ permalink raw reply

* Re: Bluetooth wireless mouse very sluggish when there is moderate network activity
From: Ismail Dönmez @ 2007-10-13 14:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: netdev
In-Reply-To: <200710131713.03345.ismail@pardus.org.tr>

s/moderated/moderate of course :-/

Saturday 13 October 2007 Tarihinde 17:13:03 yazmıştınız:
> Hi all,
>
> I got a Bluetooth wireless mouse identifed as,
>
> input: Microsoft Microsoft� Wireless Notebook Presenter Mouse 8000
> as /devices/pci0000:00/0000:00:1d.1/usb3/3-1/3-1.3/3-1.3:1.0/input/input12
> input: USB HID v1.11 Mouse [Microsoft Microsoft� Wireless Notebook
> Presenter Mouse 8000] on usb-0000:00:1d.1-1.3
>
> Problem is when there is a moderate traffic on wireless interface
> (300-400k/s down 50-60k/up on 4Mbit) mouse is really sluggish. This is when
> I am running ktorrent. If I stop the torrent download/upload its fine
> again.
>
> Any idea how to debug this, or why it might be happening. Btw I confirmed
> this behaviour on
>
> kernel 2.8.18.8, 2.6.22, 2.6.23 and latest GIT tree.
>
> P.S: If it matters I am using ipw3945 driver with 2.6.18 kernel and iwl3945
> with the newer ones.
>
> Regards,
> ismail



-- 
Faith is believing what you know isn't so -- Mark Twain

^ permalink raw reply

* Bluetooth wireless mouse very sluggish when there is moderated network activity
From: Ismail Dönmez @ 2007-10-13 14:13 UTC (permalink / raw)
  To: linux-kernel; +Cc: netdev

Hi all,

I got a Bluetooth wireless mouse identifed as,

input: Microsoft Microsoft� Wireless Notebook Presenter Mouse 8000 
as /devices/pci0000:00/0000:00:1d.1/usb3/3-1/3-1.3/3-1.3:1.0/input/input12
input: USB HID v1.11 Mouse [Microsoft Microsoft� Wireless Notebook Presenter 
Mouse 8000] on usb-0000:00:1d.1-1.3

Problem is when there is a moderate traffic on wireless interface (300-400k/s 
down 50-60k/up on 4Mbit) mouse is really sluggish. This is when I am running 
ktorrent. If I stop the torrent download/upload its fine again.

Any idea how to debug this, or why it might be happening. Btw I confirmed this 
behaviour on

kernel 2.8.18.8, 2.6.22, 2.6.23 and latest GIT tree.

P.S: If it matters I am using ipw3945 driver with 2.6.18 kernel and iwl3945 
with the newer ones.

Regards,
ismail

-- 
Faith is believing what you know isn't so -- Mark Twain

^ permalink raw reply

* Re: 2.6.23-mm1 thread exit_group issue
From: Oleg Nesterov @ 2007-10-13 11:48 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Mathieu Desnoyers, viro, kamezawa.hiroyu, linux-kernel, sam,
	netdev, Pavel Emelyanov, Sukadev Bhattiprolu, Paul Menage,
	Eric W. Biederman
In-Reply-To: <20071012180349.b4b4c2c3.akpm@linux-foundation.org>

On 10/12, Andrew Morton wrote:
>
> On Fri, 12 Oct 2007 15:47:59 -0400
> Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> wrote:
> 
> > Hi Andrew,
> > 
> > I noticed a regression between 2.6.23-rc8-mm2 and 2.6.23-mm1 (with your
> > hotfixes). User space threads seems to receive a ERESTART_RESTARTBLOCK
> > as soon as a thread does a pthread_join on them. The previous behavior
> > was to wait for them to exit by taking a futex.

No, the reason is that pthread_join() succeeds while it shouldn't. The main
thread does exit_group() and kills the sub-thread sleeping in nanosleep.
ERESTART_RESTARTBLOCK is not delivered to the user-space (sub-thread is dying),
it is just reported by gdb.

> > I provide a toy program that shows the problem. On 2.6.23-rc8-mm2, it
> > loops forever (as it should). On 2.6.23-mm1, it exits after 10 seconds.

I bet something like this

	void *threda(void *arg)
	{
		for (;;)
			pause();
		return NULL;
	}

	int main(void)
	{
		pthread_t tid;

		pthread_create(&tid, NULL, thread, NULL);
		pthread_join(tid, NULL);

		return 0;
	}

won't work as well.

> > Any idea on what may cause this problem ?

Because do_fork() doesn't use parent_tidptr. At all! So it is very clear
why 2.6.23-mm1 is broken.

> Bisection shows that this problem is caused by these two patches:
>
> pid-namespaces-allow-cloning-of-new-namespace.patch

This? http://marc.info/?l=linux-mm-commits&m=118712242002039

Pavel, this patch has a subtle difference compared to what we discussed on
containers list. It moves put_user(parent_tidptr) from copy_process() to
do_fork(), so we don't report child's pid if copy_process() failed. I do
not think this is bad, but Eric seems to disagree with such a change.

But I can't understand why Andrew sees the same problem _after_ this patch!

And which patch removed the "put_user(nr, parent_tidptr)" chunk?

Andrew, could I get the kernel source after bisection somehow? (I am not
familiar with guilt, will try to study it later)

Mathieu, could you try the patch below?

Oleg.

--- kernel/fork.c~	2007-10-13 15:41:35.000000000 +0400
+++ kernel/fork.c	2007-10-13 15:41:41.000000000 +0400
@@ -1443,6 +1443,9 @@ long do_fork(unsigned long clone_flags,
 			task_pid_nr_ns(p, current->nsproxy->pid_ns) :
 				task_pid_vnr(p);
 
+		if (clone_flags & CLONE_PARENT_SETTID)
+			put_user(nr, parent_tidptr);
+
 		if (clone_flags & CLONE_VFORK) {
 			p->vfork_done = &vfork;
 			init_completion(&vfork);


^ permalink raw reply

* Re: [PATCH] Update function names in /proc/net/tcp documentation
From: Herbert Xu @ 2007-10-13 12:49 UTC (permalink / raw)
  To: Jean Delvare; +Cc: rick.jones2, netdev
In-Reply-To: <20071013144300.08128901@hyperion.delvare>

Jean Delvare <khali@linux-fr.org> wrote:
>
> I didn't know that, sorry. What is the new interface to access the
> TCP information?

inet_diag is the preferred interface.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [patch 6/8] m68k: fix net drivers after recent get_stats updates
From: Jeff Garzik @ 2007-10-13 12:44 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Linus Torvalds, Andrew Morton, linux-m68k, linux-kernel, netdev
In-Reply-To: <20071013123413.910864776@mail.of.borg>

ACK


^ permalink raw reply

* Re: [PATCH] Update function names in /proc/net/tcp documentation
From: Jean Delvare @ 2007-10-13 12:43 UTC (permalink / raw)
  To: Rick Jones; +Cc: netdev
In-Reply-To: <470FFF9C.7040402@hp.com>

Hi Rick,

On Fri, 12 Oct 2007 16:13:32 -0700, Rick Jones wrote:
> Jean Delvare wrote:
> > Update function name references to match the current code.
> > 
> > Signed-off-by: Jean Delvare <khali@linux-fr.org>
> > ---
> >  Documentation/networking/proc_net_tcp.txt |    4 ++--
> >  1 file changed, 2 insertions(+), 2 deletions(-)
> > 
> > --- linux-2.6.23.orig/Documentation/networking/proc_net_tcp.txt	2007-02-04 19:44:54.000000000 +0100
> > +++ linux-2.6.23/Documentation/networking/proc_net_tcp.txt	2007-10-12 22:50:15.000000000 +0200
> > @@ -1,8 +1,8 @@
> >  This document describes the interfaces /proc/net/tcp and /proc/net/tcp6.
> >  
> >  These /proc interfaces provide information about currently active TCP 
> > -connections, and are implemented by tcp_get_info() in net/ipv4/tcp_ipv4.c and
> > -tcp6_get_info() in net/ipv6/tcp_ipv6.c, respectively.
> > +connections, and are implemented by tcp4_seq_show() in net/ipv4/tcp_ipv4.c
> > +and tcp6_seq_show() in net/ipv6/tcp_ipv6.c, respectively.
> >  
> >  It will first list all listening TCP sockets, and next list all established
> >  TCP connections. A typical entry of /proc/net/tcp would look like this (split 
> 
> /proc/net/[tcp|tcp6] are deprecated now right?  Perhaps there should be 
> something to that effect added to the txt.

I didn't know that, sorry. What is the new interface to access the
TCP information?

-- 
Jean Delvare

^ permalink raw reply

* [patch 8/8] ssb bus needs <linux/io.h>
From: Geert Uytterhoeven @ 2007-10-13 12:31 UTC (permalink / raw)
  To: Linus Torvalds, Andrew Morton, Michael Buesch
  Cc: linux-m68k, linux-kernel, netdev
In-Reply-To: <20071013123123.085686856@mail.of.borg>

[-- Attachment #1: ssb-needs-linux-io.h.diff --]
[-- Type: text/plain, Size: 1239 bytes --]

ssb bus needs <linux/io.h>

linux/drivers/ssb/main.c: In function 'ssb_ssb_read16':
linux/drivers/ssb/main.c:518: error: implicit declaration of function 'readw'
linux/drivers/ssb/main.c: In function 'ssb_ssb_read32':
linux/drivers/ssb/main.c:526: error: implicit declaration of function 'readl'
linux/drivers/ssb/main.c: In function 'ssb_ssb_write16':
linux/drivers/ssb/main.c:534: error: implicit declaration of function 'writew'
linux/drivers/ssb/main.c: In function 'ssb_ssb_write32':
linux/drivers/ssb/main.c:542: error: implicit declaration of function 'writel'

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/ssb/main.c |    1 +
 1 file changed, 1 insertion(+)

--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -11,6 +11,7 @@
 #include "ssb_private.h"
 
 #include <linux/delay.h>
+#include <linux/io.h>
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_regs.h>
 #include <linux/dma-mapping.h>

-- 
Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds


^ permalink raw reply

* [patch 6/8] m68k: fix net drivers after recent get_stats updates
From: Geert Uytterhoeven @ 2007-10-13 12:31 UTC (permalink / raw)
  To: Linus Torvalds, Andrew Morton
  Cc: linux-m68k, linux-kernel, Jeff Garzik, netdev
In-Reply-To: <20071013123123.085686856@mail.of.borg>

[-- Attachment #1: m68k-net-fixes.diff --]
[-- Type: text/plain, Size: 1830 bytes --]

m68k: fix net drivers after recent get_stats updates

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/net/macmace.c |    6 +++---
 drivers/net/mvme147.c |    1 -
 2 files changed, 3 insertions(+), 4 deletions(-)

--- a/drivers/net/macmace.c
+++ b/drivers/net/macmace.c
@@ -538,8 +538,9 @@ static void mace_set_multicast(struct ne
 	local_irq_restore(flags);
 }
 
-static void mace_handle_misc_intrs(struct mace_data *mp, int intr)
+static void mace_handle_misc_intrs(struct net_device *dev, int intr)
 {
+	struct mace_data *mp = netdev_priv(dev);
 	volatile struct mace *mb = mp->mace;
 	static int mace_babbles, mace_jabbers;
 
@@ -571,7 +572,7 @@ static irqreturn_t mace_interrupt(int ir
 	local_irq_save(flags);
 
 	intr = mb->ir; /* read interrupt register */
-	mace_handle_misc_intrs(mp, intr);
+	mace_handle_misc_intrs(dev, intr);
 
 	if (intr & XMTINT) {
 		fs = mb->xmtfs;
@@ -645,7 +646,6 @@ static void mace_tx_timeout(struct net_d
 
 static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf)
 {
-	struct mace_data *mp = netdev_priv(dev);
 	struct sk_buff *skb;
 	unsigned int frame_status = mf->rcvsts;
 
--- a/drivers/net/mvme147.c
+++ b/drivers/net/mvme147.c
@@ -85,7 +85,6 @@ struct net_device * __init mvme147lance_
 	dev->open = &m147lance_open;
 	dev->stop = &m147lance_close;
 	dev->hard_start_xmit = &lance_start_xmit;
-	dev->get_stats = &lance_get_stats;
 	dev->set_multicast_list = &lance_set_multicast;
 	dev->tx_timeout = &lance_tx_timeout;
 	dev->dma = 0;

-- 
Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds


^ permalink raw reply

* Re: 2.6.23-mm1 thread exit_group issue
From: Oleg Nesterov @ 2007-10-13 12:02 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Mathieu Desnoyers, viro, kamezawa.hiroyu, linux-kernel, sam,
	netdev, Pavel Emelyanov, Sukadev Bhattiprolu, Paul Menage,
	Eric W. Biederman
In-Reply-To: <20071013114820.GA121@tv-sign.ru>

On 10/13, Oleg Nesterov wrote:
>
> On 10/12, Andrew Morton wrote:
> >
> > Bisection shows that this problem is caused by these two patches:
> >
> > pid-namespaces-allow-cloning-of-new-namespace.patch
> 
> This? http://marc.info/?l=linux-mm-commits&m=118712242002039
> 
> Pavel, this patch has a subtle difference compared to what we discussed on
> containers list. It moves put_user(parent_tidptr) from copy_process() to
> do_fork(), so we don't report child's pid if copy_process() failed. I do
> not think this is bad, but Eric seems to disagree with such a change.
> 
> But I can't understand why Andrew sees the same problem _after_ this patch!
>
> And which patch removed the "put_user(nr, parent_tidptr)" chunk?

Aha. I am looking at
	ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.23/2.6.23-mm1/broken-out/pid-namespaces-allow-cloning-of-new-namespace.patch

Looks like the original patch was damaged somehow, it doesn't have the
"put_user(nr, parent_tidptr)" code.

Oleg.

^ permalink raw reply

* [PATCH 4/5] myri10ge: add IPv6 TSO support
From: Brice Goglin @ 2007-10-13 10:34 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, David Miller, Herbert Xu
In-Reply-To: <47109E8A.8080205@myri.com>

Add support for IPv6 TSO to the myri10ge driver.

Signed-off-by: Brice Goglin <brice@myri.com>
---
 drivers/net/myri10ge/myri10ge.c |   87 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 79 insertions(+), 8 deletions(-)

Index: linux-git/drivers/net/myri10ge/myri10ge.c
===================================================================
--- linux-git.orig/drivers/net/myri10ge/myri10ge.c	2007-10-13 11:59:18.000000000 +0200
+++ linux-git/drivers/net/myri10ge/myri10ge.c	2007-10-13 12:03:46.000000000 +0200
@@ -214,6 +214,8 @@
 	unsigned long serial_number;
 	int vendor_specific_offset;
 	int fw_multicast_support;
+	unsigned long features;
+	u32 max_tso6;
 	u32 read_dma;
 	u32 write_dma;
 	u32 read_write_dma;
@@ -311,6 +313,7 @@
 #define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8)
 
 static void myri10ge_set_multicast_list(struct net_device *dev);
+static int myri10ge_sw_tso(struct sk_buff *skb, struct net_device *dev);
 
 static inline void put_be32(__be32 val, __be32 __iomem * p)
 {
@@ -612,6 +615,7 @@
 	__be32 buf[16];
 	u32 dma_low, dma_high, size;
 	int status, i;
+	struct myri10ge_cmd cmd;
 
 	size = 0;
 	status = myri10ge_load_hotplug_firmware(mgp, &size);
@@ -688,6 +692,14 @@
 	dev_info(&mgp->pdev->dev, "handoff confirmed\n");
 	myri10ge_dummy_rdma(mgp, 1);
 
+	/* probe for IPv6 TSO support */
+	mgp->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO;
+	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE,
+				   &cmd, 0);
+	if (status == 0) {
+		mgp->max_tso6 = cmd.data0;
+		mgp->features |= NETIF_F_TSO6;
+	}
 	return 0;
 }
 
@@ -1384,6 +1396,18 @@
 	return 0;
 }
 
+static int myri10ge_set_tso(struct net_device *netdev, u32 tso_enabled)
+{
+	struct myri10ge_priv *mgp = netdev_priv(netdev);
+	unsigned long flags = mgp->features & (NETIF_F_TSO6 | NETIF_F_TSO);
+
+	if (tso_enabled)
+		netdev->features |= flags;
+	else
+		netdev->features &= ~flags;
+	return 0;
+}
+
 static const char myri10ge_gstrings_stats[][ETH_GSTRING_LEN] = {
 	"rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors",
 	"tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions",
@@ -1508,7 +1532,7 @@
 	.set_rx_csum = myri10ge_set_rx_csum,
 	.set_tx_csum = ethtool_op_set_tx_hw_csum,
 	.set_sg = ethtool_op_set_sg,
-	.set_tso = ethtool_op_set_tso,
+	.set_tso = myri10ge_set_tso,
 	.get_link = ethtool_op_get_link,
 	.get_strings = myri10ge_get_strings,
 	.get_sset_count = myri10ge_get_sset_count,
@@ -2166,7 +2190,8 @@
 		pseudo_hdr_offset = cksum_offset + skb->csum_offset;
 		/* If the headers are excessively large, then we must
 		 * fall back to a software checksum */
-		if (unlikely(cksum_offset > 255 || pseudo_hdr_offset > 127)) {
+		if (unlikely(!mss && (cksum_offset > 255 ||
+				      pseudo_hdr_offset > 127))) {
 			if (skb_checksum_help(skb))
 				goto drop;
 			cksum_offset = 0;
@@ -2186,9 +2211,18 @@
 		/* negative cum_len signifies to the
 		 * send loop that we are still in the
 		 * header portion of the TSO packet.
-		 * TSO header must be at most 134 bytes long */
+		 * TSO header can be at most 1KB long */
 		cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb));
 
+		/* for IPv6 TSO, the checksum offset stores the
+		 * TCP header length, to save the firmware from
+		 * the need to parse the headers */
+		if (skb_is_gso_v6(skb)) {
+			cksum_offset = tcp_hdrlen(skb);
+			/* Can only handle headers <= max_tso6 long */
+			if (unlikely(-cum_len > mgp->max_tso6))
+				return myri10ge_sw_tso(skb, dev);
+		}
 		/* for TSO, pseudo_hdr_offset holds mss.
 		 * The firmware figures out where to put
 		 * the checksum by parsing the header. */
@@ -2303,10 +2337,12 @@
 			req++;
 			count++;
 			rdma_count++;
-			if (unlikely(cksum_offset > seglen))
-				cksum_offset -= seglen;
-			else
-				cksum_offset = 0;
+			if (cksum_offset != 0 && !(mss && skb_is_gso_v6(skb))) {
+				if (unlikely(cksum_offset > seglen))
+					cksum_offset -= seglen;
+				else
+					cksum_offset = 0;
+			}
 		}
 		if (frag_idx == frag_cnt)
 			break;
@@ -2389,6 +2425,41 @@
 
 }
 
+static int myri10ge_sw_tso(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sk_buff *segs, *curr;
+	struct myri10ge_priv *mgp = dev->priv;
+	int status;
+
+	segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO6);
+	if (unlikely(IS_ERR(segs)))
+		goto drop;
+
+	while (segs) {
+		curr = segs;
+		segs = segs->next;
+		curr->next = NULL;
+		status = myri10ge_xmit(curr, dev);
+		if (status != 0) {
+			dev_kfree_skb_any(curr);
+			if (segs != NULL) {
+				curr = segs;
+				segs = segs->next;
+				curr->next = NULL;
+				dev_kfree_skb_any(segs);
+			}
+			goto drop;
+		}
+	}
+	dev_kfree_skb_any(skb);
+	return 0;
+
+drop:
+	dev_kfree_skb_any(skb);
+	mgp->stats.tx_dropped += 1;
+	return 0;
+}
+
 static struct net_device_stats *myri10ge_get_stats(struct net_device *dev)
 {
 	struct myri10ge_priv *mgp = netdev_priv(dev);
@@ -3076,7 +3147,7 @@
 	netdev->change_mtu = myri10ge_change_mtu;
 	netdev->set_multicast_list = myri10ge_set_multicast_list;
 	netdev->set_mac_address = myri10ge_set_mac_address;
-	netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO;
+	netdev->features = mgp->features;
 	if (dac_enabled)
 		netdev->features |= NETIF_F_HIGHDMA;
 



^ permalink raw reply

* [PATCH 3/5] [IPV6]: Add skb_is_gso_v6
From: Brice Goglin @ 2007-10-13 10:33 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, David Miller, Herbert Xu
In-Reply-To: <47109E8A.8080205@myri.com>

Add skb_is_gso_v6().

Signed-off-by: Brice Goglin <brice@myri.com>
---
 include/linux/skbuff.h |    5 +++++
 1 file changed, 5 insertions(+)

Index: linux-git/include/linux/skbuff.h
===================================================================
--- linux-git.orig/include/linux/skbuff.h	2007-10-13 12:24:37.000000000 +0200
+++ linux-git/include/linux/skbuff.h	2007-10-13 12:25:04.000000000 +0200
@@ -1781,6 +1781,11 @@
 	return skb_shinfo(skb)->gso_size;
 }
 
+static inline int skb_is_gso_v6(const struct sk_buff *skb)
+{
+	return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6;
+}
+
 static inline void skb_forward_csum(struct sk_buff *skb)
 {
 	/* Unfortunately we don't support this one.  Any brave souls? */



^ permalink raw reply

* [PATCH 5/5] myri10ge: update driver version to 1.3.2-1.287
From: Brice Goglin @ 2007-10-13 10:34 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev
In-Reply-To: <47109E8A.8080205@myri.com>

The myri10ge driver is now at version 1.3.2-1.287.

Signed-off-by: Brice Goglin <brice@myri.com>
---
 drivers/net/myri10ge/myri10ge.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-git/drivers/net/myri10ge/myri10ge.c
===================================================================
--- linux-git.orig/drivers/net/myri10ge/myri10ge.c	2007-10-13 11:56:17.000000000 +0200
+++ linux-git/drivers/net/myri10ge/myri10ge.c	2007-10-13 11:56:24.000000000 +0200
@@ -75,7 +75,7 @@
 #include "myri10ge_mcp.h"
 #include "myri10ge_mcp_gen_header.h"
 
-#define MYRI10GE_VERSION_STR "1.3.2-1.269"
+#define MYRI10GE_VERSION_STR "1.3.2-1.287"
 
 MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
 MODULE_AUTHOR("Maintainer: help@myri.com");



^ permalink raw reply

* [PATCH 2/5] myri10ge: update firmware headers
From: Brice Goglin @ 2007-10-13 10:32 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev
In-Reply-To: <47109E8A.8080205@myri.com>

Update myri10ge firmware headers to latest upstream version with
TSO6 and RSS support.

Signed-off-by: Brice Goglin <brice@myri.com>
---
 drivers/net/myri10ge/myri10ge_mcp.h |   90 ++++++++++++++++++++++++++++++++++--
 1 file changed, 86 insertions(+), 4 deletions(-)

Index: linux-git/drivers/net/myri10ge/myri10ge_mcp.h
===================================================================
--- linux-git.orig/drivers/net/myri10ge/myri10ge_mcp.h	2007-10-13 11:58:18.000000000 +0200
+++ linux-git/drivers/net/myri10ge/myri10ge_mcp.h	2007-10-13 12:02:31.000000000 +0200
@@ -10,7 +10,7 @@
 	__be32 low;
 };
 
-/* 4 Bytes */
+/* 4 Bytes.  8 Bytes for NDIS drivers. */
 struct mcp_slot {
 	__sum16 checksum;
 	__be16 length;
@@ -205,8 +205,87 @@
 	/* same than DMA_TEST (same args) but abort with UNALIGNED on unaligned
 	 * chipset */
 
-	MXGEFW_CMD_UNALIGNED_STATUS
-	    /* return data = boolean, true if the chipset is known to be unaligned */
+	MXGEFW_CMD_UNALIGNED_STATUS,
+	/* return data = boolean, true if the chipset is known to be unaligned */
+
+	MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
+	/* data0 = number of big buffers to use.  It must be 0 or a power of 2.
+	 * 0 indicates that the NIC consumes as many buffers as they are required
+	 * for packet. This is the default behavior.
+	 * A power of 2 number indicates that the NIC always uses the specified
+	 * number of buffers for each big receive packet.
+	 * It is up to the driver to ensure that this value is big enough for
+	 * the NIC to be able to receive maximum-sized packets.
+	 */
+
+	MXGEFW_CMD_GET_MAX_RSS_QUEUES,
+	MXGEFW_CMD_ENABLE_RSS_QUEUES,
+	/* data0 = number of slices n (0, 1, ..., n-1) to enable
+	 * data1 = interrupt mode. 0=share one INTx/MSI, 1=use one MSI-X per queue.
+	 * If all queues share one interrupt, the driver must have set
+	 * RSS_SHARED_INTERRUPT_DMA before enabling queues.
+	 */
+	MXGEFW_CMD_GET_RSS_SHARED_INTERRUPT_MASK_OFFSET,
+	MXGEFW_CMD_SET_RSS_SHARED_INTERRUPT_DMA,
+	/* data0, data1 = bus address lsw, msw */
+	MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
+	/* get the offset of the indirection table */
+	MXGEFW_CMD_SET_RSS_TABLE_SIZE,
+	/* set the size of the indirection table */
+	MXGEFW_CMD_GET_RSS_KEY_OFFSET,
+	/* get the offset of the secret key */
+	MXGEFW_CMD_RSS_KEY_UPDATED,
+	/* tell nic that the secret key's been updated */
+	MXGEFW_CMD_SET_RSS_ENABLE,
+	/* data0 = enable/disable rss
+	 * 0: disable rss.  nic does not distribute receive packets.
+	 * 1: enable rss.  nic distributes receive packets among queues.
+	 * data1 = hash type
+	 * 1: IPV4
+	 * 2: TCP_IPV4
+	 * 3: IPV4 | TCP_IPV4
+	 */
+
+	MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE,
+	/* Return data = the max. size of the entire headers of a IPv6 TSO packet.
+	 * If the header size of a IPv6 TSO packet is larger than the specified
+	 * value, then the driver must not use TSO.
+	 * This size restriction only applies to IPv6 TSO.
+	 * For IPv4 TSO, the maximum size of the headers is fixed, and the NIC
+	 * always has enough header buffer to store maximum-sized headers.
+	 */
+
+	MXGEFW_CMD_SET_TSO_MODE,
+	/* data0 = TSO mode.
+	 * 0: Linux/FreeBSD style (NIC default)
+	 * 1: NDIS/NetBSD style
+	 */
+
+	MXGEFW_CMD_MDIO_READ,
+	/* data0 = dev_addr (PMA/PMD or PCS ...), data1 = register/addr */
+	MXGEFW_CMD_MDIO_WRITE,
+	/* data0 = dev_addr,  data1 = register/addr, data2 = value  */
+
+	MXGEFW_CMD_XFP_I2C_READ,
+	/* Starts to get a fresh copy of one byte or of the whole xfp i2c table, the
+	 * obtained data is cached inside the xaui-xfi chip :
+	 *   data0 : "all" flag : 0 => get one byte, 1=> get 256 bytes,
+	 *   data1 : if (data0 == 0): index of byte to refresh [ not used otherwise ]
+	 * The operation might take ~1ms for a single byte or ~65ms when refreshing all 256 bytes
+	 * During the i2c operation,  MXGEFW_CMD_XFP_I2C_READ or MXGEFW_CMD_XFP_BYTE attempts
+	 *  will return MXGEFW_CMD_ERROR_BUSY
+	 */
+	MXGEFW_CMD_XFP_BYTE,
+	/* Return the last obtained copy of a given byte in the xfp i2c table
+	 * (copy cached during the last relevant MXGEFW_CMD_XFP_I2C_READ)
+	 *   data0 : index of the desired table entry
+	 *  Return data = the byte stored at the requested index in the table
+	 */
+
+	MXGEFW_CMD_GET_VPUMP_OFFSET,
+	/* Return data = NIC memory offset of mcp_vpump_public_global */
+	MXGEFW_CMD_RESET_VPUMP,
+	/* Resets the VPUMP state */
 };
 
 enum myri10ge_mcp_cmd_status {
@@ -220,7 +299,10 @@
 	MXGEFW_CMD_ERROR_BAD_PORT,
 	MXGEFW_CMD_ERROR_RESOURCES,
 	MXGEFW_CMD_ERROR_MULTICAST,
-	MXGEFW_CMD_ERROR_UNALIGNED
+	MXGEFW_CMD_ERROR_UNALIGNED,
+	MXGEFW_CMD_ERROR_NO_MDIO,
+	MXGEFW_CMD_ERROR_XFP_FAILURE,
+	MXGEFW_CMD_ERROR_XFP_ABSENT
 };
 
 #define MXGEFW_OLD_IRQ_DATA_LEN 40



^ permalink raw reply

* [PATCH 0/5] myri10ge updates for 2.6.24
From: Brice Goglin @ 2007-10-13 10:31 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, David Miller, Herbert Xu

Hi Jeff,

Now that LRO and the new NAPI have landed in myri10ge for 2.6.24, here's
a couple updates. The big part is support for IPv6 TSO, which requires
the addition of skb_is_gso_v6(). I am CC'ing Dave and Herbert in the
corresponding mails. The other patches are trivial.

1. myri10ge: fix some indentation, white spaces, and comments
2. myri10ge: update firmware headers
3. [IPV6]: add skb_is_gso_v6()
4. myri10ge: add IPv6 TSO support
5. myri10ge: update driver version to 1.3.2-1.287

Please apply for 2.6.24.

Thanks,
Brice


^ permalink raw reply

* [PATCH 1/5] myri10ge: fix some indentation, white spaces, and comments
From: Brice Goglin @ 2007-10-13 10:32 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev
In-Reply-To: <47109E8A.8080205@myri.com>

Fix one comment in myri10ge.c and update indendation and white spaces
to match the code generated by indent from upstream CVS.

Signed-off-by: Brice Goglin <brice@myri.com>
---
 drivers/net/myri10ge/myri10ge.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

Index: linux-git/drivers/net/myri10ge/myri10ge.c
===================================================================
--- linux-git.orig/drivers/net/myri10ge/myri10ge.c	2007-10-13 10:19:43.000000000 +0200
+++ linux-git/drivers/net/myri10ge/myri10ge.c	2007-10-13 11:59:18.000000000 +0200
@@ -1047,7 +1047,8 @@
 
 	hlen = MYRI10GE_HLEN > len ? len : MYRI10GE_HLEN;
 
-	/* allocate an skb to attach the page(s) to. */
+	/* allocate an skb to attach the page(s) to. This is done
+	 * after trying LRO, so as to avoid skb allocation overheads */
 
 	skb = netdev_alloc_skb(dev, MYRI10GE_HLEN + 16);
 	if (unlikely(skb == NULL)) {
@@ -1217,7 +1218,8 @@
 
 static int myri10ge_poll(struct napi_struct *napi, int budget)
 {
-	struct myri10ge_priv *mgp = container_of(napi, struct myri10ge_priv, napi);
+	struct myri10ge_priv *mgp =
+	    container_of(napi, struct myri10ge_priv, napi);
 	struct net_device *netdev = mgp->dev;
 	struct myri10ge_rx_done *rx_done = &mgp->rx_done;
 	int work_done;
@@ -2706,7 +2708,6 @@
 }
 
 #ifdef CONFIG_PM
-
 static int myri10ge_suspend(struct pci_dev *pdev, pm_message_t state)
 {
 	struct myri10ge_priv *mgp;
@@ -2787,7 +2788,6 @@
 	return -EIO;
 
 }
-
 #endif				/* CONFIG_PM */
 
 static u32 myri10ge_read_reboot(struct myri10ge_priv *mgp)
@@ -2954,8 +2954,7 @@
 
 	mgp = netdev_priv(netdev);
 	mgp->dev = netdev;
-	netif_napi_add(netdev, &mgp->napi,
-		       myri10ge_poll, myri10ge_napi_weight);
+	netif_napi_add(netdev, &mgp->napi, myri10ge_poll, myri10ge_napi_weight);
 	mgp->pdev = pdev;
 	mgp->csum_flag = MXGEFW_FLAGS_CKSUM;
 	mgp->pause = myri10ge_flow_control;



^ permalink raw reply

* Re: MSI interrupts and disable_irq
From: Manfred Spraul @ 2007-10-13  9:30 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Ayaz Abdulla, nedev, Linux Kernel Mailing List, David Miller,
	Andrew Morton
In-Reply-To: <46FDBCB4.9090802@pobox.com>

Jeff Garzik wrote:
>
> I think the scenario you outline is an illustration of the approach's 
> fragility:  disable_irq() is a heavy hammer that originated with INTx, 
> and it relies on a chip-specific disable method (kernel/irq/manage.c) 
> that practically guarantees behavior will vary across MSI/INTx/etc.
>
I checked the code: IRQ_DISABLE is implemented in software, i.e. 
handle_level_irq() only calls handle_IRQ_event() [and then the nic irq 
handler] if IRQ_DISABLE is not set.
OTHO: The last trace looks as if nv_do_nic_poll() is interrupted by an irq.

Perhaps something corrupts dev->irq? The irq is requested with
    request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev)
and disabled with
    disable_irq_lockdep(dev->irq);

Someone around with a MSI capable board? The forcedeth driver does
    dev->irq = pci_dev->irq
in nv_probe(), especially before pci_enable_msi().
Does pci_enable_msi() change pci_dev->irq? Then we would disable the 
wrong interrupt....

--
    Manfred

^ permalink raw reply

* Re: tc_verd vs. skb_clone
From: jamal @ 2007-10-13  8:04 UTC (permalink / raw)
  To: Herbert Xu; +Cc: netdev
In-Reply-To: <20071012140026.GA27761@gondor.apana.org.au>

On Fri, 2007-12-10 at 22:00 +0800, Herbert Xu wrote:
> Hi Jamal:
> 
> I was wondering why we're not copying tc_verd as is in skb_clone.
> In particular, this makes pskb_copy behave differently compared
> to skb_clone and I can't find a justificatino for it.
> 
> Any ideas?

In the case of an action branching (from a graph), they clone. In the
case they want to trample on an skb, they copy; otherwise it continues
processing in the graph with none of the above. In the clone case,
certain metadata needs reseting (like the "it is ok to munge on this
packet" to "it is _not_ ok to munge on it" etc). We allow the copy case 
leeway to do whatever it wants since it is going to tranple on the skb
and therefore reset nothing.
Would something more verbose in Documentation/net help to make this
clear?

If it is causing trouble, then one idea would be to move the resetting
to a wrapper function which calls clone first and then resets the other
fields. All actions currently cloning would need to be mod-ed to use
that call.

> Also we don't seem to copy iif in copy_skb_header.

I cant think of a good reason why it shouldnt be copied or recall
whether thats how its been from the begining. The reason probably it
hasnt mattered so far is everything that needs to write the iif never
copies.

cheers,
jamal


^ permalink raw reply

* Re: Kernel panic (network stack)
From: xeb @ 2007-10-13  7:54 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev
In-Reply-To: <20071012102045.3f181605@freepuppy.rosehill>

> > Hello!
> > I develop network driver.
> > It works fine while less then 100 network interfaces exists.
> > Then i give kernel panic. What could cause it ?
>
> Since it probably in your code. Please code (or place to download)
> the driver code.

You can download from http://sourceforge.net/projects/accel-pptp

/*
 *  Point-to-Point Tunneling Protocol for Linux
 *
 *	Authors: Kozlov D. (xeb@mail.ru)
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 *
 */

#include <linux/string.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/ppp_channel.h>
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include "if_pppox.h"
#include <linux/notifier.h>
#include <linux/file.h>
#include <linux/proc_fs.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/version.h>
#include <linux/spinlock.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#include <linux/tqueue.h>
#include <linux/timer.h>
#else
#include <linux/workqueue.h>
#endif

#include <net/sock.h>
#include <net/protocol.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/route.h>

#include <asm/uaccess.h>

#define PPTP_DRIVER_VERSION "0.7.8-r1"

MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol for Linux");
MODULE_AUTHOR("Kozlov D. (xeb@mail.ru)");
MODULE_LICENSE("GPL");

static int log_level=0;
static int log_packets=10;
static int rx_stop=0;
static int min_window=5;
static int max_window=100;
static int statistics=0;
static int stat_collect_time=3;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
MODULE_PARM(min_window,"i");
MODULE_PARM(max_window,"i");
MODULE_PARM(log_level,"i");
MODULE_PARM(log_packets,"i");
MODULE_PARM(statistics,"i");
MODULE_PARM(stat_collect_time,"i");
#else
module_param(min_window,int,0);
module_param(max_window,int,0);
module_param(log_level,int,0);
module_param(log_packets,int,0);
module_param(statistics,int,0);
module_param(stat_collect_time,int,0);
#endif
MODULE_PARM_DESC(min_window,"Minimum sliding window size (default=3)");
MODULE_PARM_DESC(max_window,"Maximum sliding window size (default=100)");
MODULE_PARM_DESC(log_level,"Logging level (default=0)");
MODULE_PARM_DESC(statistics,"Performance statistics collection");
MODULE_PARM_DESC(stat_collect_time,"Statistics collect time (sec)");


#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#define __wait_event_timeout(wq, condition, ret)			\
do {									\
	wait_queue_t __wait;						\
	init_waitqueue_entry(&__wait, current);				\
									\
	for (;;) {							\
		set_current_state(TASK_UNINTERRUPTIBLE);		\
		if (condition)						\
			break;						\
		ret = schedule_timeout(ret);				\
		if (!ret)						\
			break;						\
	}								\
	current->state = TASK_RUNNING;					\
	remove_wait_queue(&wq, &__wait);				\
} while (0)
#define wait_event_timeout(wq, condition, timeout)			\
({									\
	long __ret = timeout;						\
	if (!(condition)) 						\
		__wait_event_timeout(wq, condition, __ret);		\
	__ret;								\
})

#define INIT_TIMER(_timer,_routine,_data) \
do { \
	(_timer)->function=_routine; \
	(_timer)->data=_data; \
	init_timer(_timer); \
} while (0); 

static inline void *kzalloc(size_t size,int gfp)
{
	void *p=kmalloc(size,gfp);
	memset(p,0,size);
	return p;
}

static inline void skb_get_timestamp(const struct sk_buff *skb, struct timeval 
*stamp)
{
	*stamp = skb->stamp;
}
static inline void __net_timestamp(struct sk_buff *skb, const struct timeval 
*stamp)
{
	struct timeval tv;
	do_gettimeofday(&tv);
	skb->stamp = *stamp;
}
#endif

//============= performance statistics =============
//static struct timeval stat_last_update={0,0};
unsigned long stat_last_update=0;
static unsigned int tx_packets_avg=0;
static unsigned int rx_packets_avg=0;
static unsigned int ack_timeouts_avg=0;
static unsigned int ack_works_avg=0;
static unsigned int buf_works_avg=0;
static unsigned int tx_packets_cur=0;
static unsigned int rx_packets_cur=0;
static unsigned int ack_timeouts_cur=0;
static unsigned int ack_works_cur=0;
static unsigned int buf_works_cur=0;
static rwlock_t stat_lock=RW_LOCK_UNLOCKED;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
struct tq_struct stat_work;
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
struct delayed_work stat_work;
#else
struct work_struct stat_work;
#endif
static void inc_stat_counter(unsigned int *counter)
{
	if (statistics)
	{
		write_lock_bh(&stat_lock);
		(*counter)++;
		write_unlock_bh(&stat_lock);		
	}
}
#define INC_TX_PACKETS inc_stat_counter(&tx_packets_cur)
#define INC_RX_PACKETS inc_stat_counter(&rx_packets_cur)
#define INC_ACK_TIMEOUTS inc_stat_counter(&ack_timeouts_cur)
#define INC_ACK_WORKS inc_stat_counter(&ack_works_cur)
#define INC_BUF_WORKS inc_stat_counter(&buf_works_cur)

static void do_stat_work(void*p)
{
	unsigned long delta_jiff;
	
	write_lock_bh(&stat_lock);
	
	delta_jiff=jiffies-stat_last_update;
	stat_last_update=jiffies;
	if (delta_jiff<=0) goto exit;
	
	tx_packets_avg=(tx_packets_avg+tx_packets_cur*HZ/delta_jiff)/2;
	rx_packets_avg=(rx_packets_avg+rx_packets_cur*HZ/delta_jiff)/2;
	ack_timeouts_avg=(ack_timeouts_avg+ack_timeouts_cur*HZ/delta_jiff)/2;
	ack_works_avg=(ack_works_avg+ack_works_cur*HZ/delta_jiff)/2;
	buf_works_avg=(buf_works_avg+buf_works_cur*HZ/delta_jiff)/2;
	
	tx_packets_cur=0;
	rx_packets_cur=0;
	ack_timeouts_cur=0;
	ack_works_cur=0;
	buf_works_cur=0;
	
exit:
	write_unlock_bh(&stat_lock);		

	schedule_delayed_work(&stat_work,stat_collect_time*HZ);
}
//==================================================



static struct proc_dir_entry* proc_dir;

#define HASH_SIZE  32
#define HASH(addr) ((addr^(addr>>3))&0x1F)

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
static rwlock_t chan_lock=RW_LOCK_UNLOCKED;
#define SK_STATE(sk) (sk)->state
#else
static DEFINE_RWLOCK(chan_lock);
#define SK_STATE(sk) (sk)->sk_state
#endif
static struct pppox_sock *chans[HASH_SIZE];

static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb);
static int read_proc(char *page, char **start, off_t off,int count,
                     int *eof, void *data);
static int __pptp_rcv(struct pppox_sock *po,struct sk_buff *skb,int new);

static struct ppp_channel_ops pptp_chan_ops= {
	.start_xmit = pptp_xmit,
};


#define MISSING_WINDOW 20
#define WRAPPED( curseq, lastseq) \
    ((((curseq) & 0xffffff00) == 0) && \
     (((lastseq) & 0xffffff00 ) == 0xffffff00))

/* gre header structure: -------------------------------------------- */

#define PPTP_GRE_PROTO  0x880B
#define PPTP_GRE_VER    0x1

#define PPTP_GRE_FLAG_C	0x80
#define PPTP_GRE_FLAG_R	0x40
#define PPTP_GRE_FLAG_K	0x20
#define PPTP_GRE_FLAG_S	0x10
#define PPTP_GRE_FLAG_A	0x80

#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)

struct pptp_gre_header {
  u_int8_t flags;		/* bitfield */
  u_int8_t ver;			/* should be PPTP_GRE_VER (enhanced GRE) */
  u_int16_t protocol;		/* should be PPTP_GRE_PROTO (ppp-encaps) */
  u_int16_t payload_len;	/* size of ppp payload, not inc. gre header */
  u_int16_t call_id;		/* peer's call_id for this session */
  u_int32_t seq;		/* sequence number.  Present if S==1 */
  u_int32_t ack;		/* seq number of highest packet recieved by */
  				/*  sender in this session */
};
#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header))

struct gre_statistics {
  /* statistics for GRE receive */
  unsigned int rx_accepted;  // data packet accepted
  unsigned int rx_lost;      // data packet did not arrive before timeout
  unsigned int rx_underwin;  // data packet was under window (arrived too late
                             // or duplicate packet)
  unsigned int rx_buffered;  // data packet arrived earlier than expected,
                             // packet(s) before it were lost or reordered
  unsigned int rx_errors;    // OS error on receive
  unsigned int rx_truncated; // truncated packet
  unsigned int rx_invalid;   // wrong protocol or invalid flags
  unsigned int rx_acks;      // acknowledgement only

  /* statistics for GRE transmit */
  unsigned int tx_sent;      // data packet sent
  unsigned int tx_failed;    //
  unsigned int tx_acks;      // sent packet with just ACK

  __u32 pt_seq;
  struct timeval pt_time;
  unsigned int rtt;
};

static struct pppox_sock * lookup_chan(__u16 call_id)
{
	struct pppox_sock *po;
	read_lock_bh(&chan_lock);
	for(po=chans[HASH(call_id)]; po; po=po->next)
		if (po->proto.pptp.src_addr.call_id==call_id)
			break;
	read_unlock_bh(&chan_lock);
	return po;
}

static void add_chan(struct pppox_sock *po)
{
	write_lock_bh(&chan_lock);
	po->next=chans[HASH(po->proto.pptp.src_addr.call_id)];
	chans[HASH(po->proto.pptp.src_addr.call_id)]=po;
	write_unlock_bh(&chan_lock);
}

static void add_free_chan(struct pppox_sock *po)
{
	static __u16 call_id=0;
	struct pppox_sock *p;

	write_lock_bh(&chan_lock);
	while (1) {
		if (++call_id==0) continue;
		for(p=chans[HASH(call_id)]; p; p=p->next)
			if (p->proto.pptp.src_addr.call_id==call_id)
				break;
		if (!p){
			po->proto.pptp.src_addr.call_id=call_id;
			po->next=chans[HASH(call_id)];
			chans[HASH(call_id)]=po;
			break;
		}
	}
	write_unlock_bh(&chan_lock);
}

static void del_chan(struct pppox_sock *po)
{
	struct pppox_sock *p1,*p2;

	write_lock_bh(&chan_lock);
	for(p2=NULL,p1=chans[HASH(po->proto.pptp.src_addr.call_id)]; p1 && p1!=po;
				p2=p1,p1=p1->next);
	if (p1){
	    if (p2) p2->next=p1->next;
	    else chans[HASH(po->proto.pptp.src_addr.call_id)]=p1->next;
	}
	write_unlock_bh(&chan_lock);
}

static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
{
	struct sock *sk = (struct sock *) chan->private;
	struct pppox_sock *po = pppox_sk(sk);
	struct pptp_opt *opt=&po->proto.pptp;
	struct pptp_gre_header *hdr;
	unsigned int header_len=sizeof(*hdr);
	int len=skb?skb->len:0;
	int err=0;
	int window;

	struct rtable *rt;     			/* Route to the other host */
	struct net_device *tdev;			/* Device to other host */
	struct iphdr  *iph;			/* Our new IP header */
	int    max_headroom;			/* The extra header space needed */

	INC_TX_PACKETS;

	spin_lock_bh(&opt->xmit_lock);
	
	window=WRAPPED(opt->ack_recv,opt->seq_sent)?(__u32)
0xffffffff-opt->seq_sent+opt->ack_recv:opt->seq_sent-opt->ack_recv;

	if (!skb){
	    if (opt->ack_sent == opt->seq_recv) goto exit;
	}else if (window>opt->window){
		__set_bit(PPTP_FLAG_PAUSE,(unsigned long*)&opt->flags);
		#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
		mod_timer(&opt->ack_timeout_timer,opt->stat->rtt/100*HZ/10000);
		#else
		schedule_delayed_work(&opt->ack_timeout_work,opt->stat->rtt/100*HZ/10000);
		#endif
		goto exit;
	}

	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	{
		struct rt_key key = {
			.dst=opt->dst_addr.sin_addr.s_addr,
			.src=opt->src_addr.sin_addr.s_addr,
			.tos=RT_TOS(0),
		};
		if ((err=ip_route_output_key(&rt, &key))) {
			goto tx_error;
		}
	}
	#else
	{
		struct flowi fl = { .oif = 0,
				    .nl_u = { .ip4_u =
					      { .daddr = opt->dst_addr.sin_addr.s_addr,
						.saddr = opt->src_addr.sin_addr.s_addr,
						.tos = RT_TOS(0) } },
				    .proto = IPPROTO_GRE };
		if ((err=ip_route_output_key(&rt, &fl))) {
			goto tx_error;
		}
	}
	#endif
	tdev = rt->u.dst.dev;
	
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	max_headroom = ((tdev->hard_header_len+15)&~15) + 
sizeof(*iph)+sizeof(*hdr)+2;
	#else
	max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph)+sizeof(*hdr)+2;
	#endif
	

	if (!skb){
		skb=dev_alloc_skb(max_headroom);
		if (!skb) {
			ip_rt_put(rt);
			goto tx_error;
		}
		skb_reserve(skb,max_headroom-skb_headroom(skb));
	}else if (skb_headroom(skb) < max_headroom ||
						skb_cloned(skb) || skb_shared(skb)) {
		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
		if (!new_skb) {
			ip_rt_put(rt);
			goto tx_error;
		}
		if (skb->sk)
		skb_set_owner_w(new_skb, skb->sk);
		kfree_skb(skb);
		skb = new_skb;
	}

	if (skb->len){
		int islcp;
		unsigned char *data=skb->data;
		islcp=((data[0] << 8) + data[1])== PPP_LCP && 1 <= data[2] && data[2] <= 7;
		if (islcp) {
			data=skb_push(skb,2);
			data[0]=0xff;
			data[1]=0x03;
		}
	}
	len=skb->len;

	if (len==0) header_len-=sizeof(hdr->seq);
	if (opt->ack_sent == opt->seq_recv) header_len-=sizeof(hdr->ack);

	// Push down and install GRE header
	skb_push(skb,header_len);
	hdr=(struct pptp_gre_header *)(skb->data);

	hdr->flags       = PPTP_GRE_FLAG_K;
	hdr->ver         = PPTP_GRE_VER;
	hdr->protocol    = htons(PPTP_GRE_PROTO);
	hdr->call_id     = htons(opt->dst_addr.call_id);

	if (!len){
		hdr->payload_len = 0;
		hdr->ver |= PPTP_GRE_FLAG_A;
		/* ack is in odd place because S == 0 */
		hdr->seq = htonl(opt->seq_recv);
		opt->ack_sent = opt->seq_recv;
		opt->stat->tx_acks++;
	}else {
		//if (!opt->seq_sent){
		//}

		hdr->flags |= PPTP_GRE_FLAG_S;
		hdr->seq    = htonl(opt->seq_sent++);
		if (log_level>=3 && opt->seq_sent<=log_packets)
			printk("PPTP[%i]: send packet: 
seq=%i",opt->src_addr.call_id,opt->seq_sent);
		if (opt->ack_sent != opt->seq_recv)	{
		/* send ack with this message */
			hdr->ver |= PPTP_GRE_FLAG_A;
			hdr->ack  = htonl(opt->seq_recv);
			opt->ack_sent = opt->seq_recv;
			if (log_level>=3 && opt->seq_sent<=log_packets)
				printk(" ack=%i",opt->seq_recv);
		}
		hdr->payload_len = htons(len);
		if (log_level>=3 && opt->seq_sent<=log_packets)
			printk("\n");
	}

	/*
	 *	Push down and install the IP header.
	 */

	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
	skb->transport_header = skb->network_header;
	skb_push(skb, sizeof(*iph));
	skb_reset_network_header(skb);
	#else
	skb->h.raw = skb->nh.raw;
	skb->nh.raw = skb_push(skb, sizeof(*iph));
	#endif
	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
			      IPSKB_REROUTED);
	#endif

	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
	iph 			=	ip_hdr(skb);
	#else
	iph 			=	skb->nh.iph;
	#endif
	iph->version		=	4;
	iph->ihl		=	sizeof(struct iphdr) >> 2;
	iph->frag_off		=	0;//df;
	iph->protocol		=	IPPROTO_GRE;
	iph->tos		=	0;
	iph->daddr		=	rt->rt_dst;
	iph->saddr		=	rt->rt_src;
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	iph->ttl = sysctl_ip_default_ttl;
	#else
	iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
	#endif
	iph->tot_len = htons(skb->len);

	dst_release(skb->dst);
	skb->dst = &rt->u.dst;
	
	nf_reset(skb);

	skb->ip_summed = CHECKSUM_NONE;
	ip_select_ident(iph, &rt->u.dst, NULL);
	ip_send_check(iph);

	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, 
dst_output);
	
	wake_up(&opt->wait);

	if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) {
		opt->stat->tx_sent++;
		if (!opt->stat->pt_seq){
			opt->stat->pt_seq  = opt->seq_sent;
			do_gettimeofday(&opt->stat->pt_time);
		}
	}else goto tx_error;

	spin_unlock_bh(&opt->xmit_lock);
	return 1;

tx_error:
	opt->stat->tx_failed++;
	if (!len) kfree_skb(skb);
	spin_unlock_bh(&opt->xmit_lock);
	return 1;
exit:
	spin_unlock_bh(&opt->xmit_lock);
	return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void ack_work(struct work_struct *work)
{
    struct pptp_opt *opt=container_of(work,struct pptp_opt,ack_work);
    struct pppox_sock *po=container_of(opt,struct pppox_sock,proto.pptp);
#else
static void ack_work(struct pppox_sock *po)
{
	struct pptp_opt *opt=&po->proto.pptp;
#endif
	INC_ACK_WORKS;
	pptp_xmit(&po->chan,0);

	if (!test_and_set_bit(PPTP_FLAG_PROC,(unsigned long*)&opt->flags)){
		if (ppp_unit_number(&po->chan)!=-1){
			char unit[10];
			sprintf(unit,"ppp%i",ppp_unit_number(&po->chan));
			create_proc_read_entry(unit,0,proc_dir,read_proc,po);
		}else clear_bit(PPTP_FLAG_PROC,(unsigned long*)&opt->flags);
	}
}


static void do_ack_timeout_work(struct pppox_sock *po)
{
    struct pptp_opt *opt=&po->proto.pptp;
    int paused;
    
    spin_lock_bh(&opt->xmit_lock);
    paused=__test_and_clear_bit(PPTP_FLAG_PAUSE,(unsigned long*)&opt->flags);
    if (paused){
			if (opt->window>min_window) --opt->window;
			opt->ack_recv=opt->seq_sent;
    }
    spin_unlock_bh(&opt->xmit_lock);
    if (paused) ppp_output_wakeup(&po->chan);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void _ack_timeout_work(struct work_struct *work)
{
    struct pptp_opt *opt=container_of(work,struct 
pptp_opt,ack_timeout_work.work);
    struct pppox_sock *po=container_of(opt,struct pppox_sock,proto.pptp);
		INC_ACK_TIMEOUTS;
    do_ack_timeout_work(po);
}
#else
static void do_ack_timeout_work(struct pppox_sock *po)
{
	INC_ACK_TIMEOUTS;
  do_ack_timeout_work(po);
}
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
static void ack_timeout_timer(struct pppox_sock *po)
{
    struct pptp_opt *opt;
    opt=&po->proto.pptp;
    schedule_task(&opt->ack_timeout_work);
}
#endif

static int get_seq(struct sk_buff *skb)
{
	struct iphdr *iph;
	u8 *payload;
	struct pptp_gre_header *header;

	iph = (struct iphdr*)skb->data;
	payload = skb->data + (iph->ihl << 2);
	header = (struct pptp_gre_header *)(payload);

	return ntohl(header->seq);
}
static void do_buf_work(struct pppox_sock *po)
{
	struct timeval tv1,tv2;
	struct sk_buff *skb;
	struct pptp_opt *opt=&po->proto.pptp;
	unsigned int t;

	spin_lock_bh(&opt->rcv_lock);
	do_gettimeofday(&tv1);
	while((skb=skb_dequeue(&opt->skb_buf))){
		if (!__pptp_rcv(po,skb,0)){
			skb_get_timestamp(skb,&tv2);
			t=(tv1.tv_sec-tv2.tv_sec)*1000000+(tv1.tv_usec-tv2.tv_usec);
			if (t<opt->stat->rtt){
				skb_queue_head(&opt->skb_buf,skb);
				#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
				mod_timer(&opt->buf_timer,t/100*HZ/10000);
				#else
				schedule_delayed_work(&opt->buf_work,t/100*HZ/10000);
				#endif
				goto exit;
			}
			t=get_seq(skb)-1;
			opt->stat->rx_lost+=t-opt->seq_recv;
			opt->seq_recv=t;
			if (log_level>=2)
				printk("PPTP[%i]: unbuffer packet %i\n",opt->src_addr.call_id,t+1);
			__pptp_rcv(po,skb,0);
		}
	}
exit:
	spin_unlock_bh(&opt->rcv_lock);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void inline _buf_work(struct work_struct *work)
{
	struct pptp_opt *opt=container_of(work,struct pptp_opt,buf_work.work);
	struct pppox_sock *po=container_of(opt,struct pppox_sock,proto.pptp);
	
	INC_BUF_WORKS;
	do_buf_work(po);
}
#else
static void buf_work(struct pppox_sock *po)
{
	INC_BUF_WORKS;
	do_buf_work(po);
}
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
static void buf_timer(struct pppox_sock *po)
{
    struct pptp_opt *opt;
    opt=&po->proto.pptp;
    schedule_task(&opt->buf_work);
}
#endif

static int __pptp_rcv(struct pppox_sock *po,struct sk_buff *skb,int new)
{
	struct pptp_opt *opt=&po->proto.pptp;
	int headersize,payload_len,seq;
	__u8 *payload;
	struct pptp_gre_header *header;

	INC_RX_PACKETS;
	
	header = (struct pptp_gre_header *)(skb->data);

	if (log_level>=4) printk("PPTP[%i]: 
__pptv_rcv %i\n",opt->src_addr.call_id,new);

	if (new){
		/* test if acknowledgement present */
		if (PPTP_GRE_IS_A(header->ver)){
				int paused;
				__u32 ack = (PPTP_GRE_IS_S(header->flags))?
						header->ack:header->seq; /* ack in different place if S = 0 */
				
				ack = ntohl( ack);

				spin_lock_bh(&opt->xmit_lock);

				if (ack > opt->ack_recv) opt->ack_recv = ack;
				/* also handle sequence number wrap-around  */
				if (WRAPPED(ack,opt->ack_recv)) opt->ack_recv = ack;

				paused=__test_and_clear_bit(PPTP_FLAG_PAUSE,(unsigned long*)&opt->flags);
				spin_unlock_bh(&opt->xmit_lock);
				
				if (paused){
						#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
						del_timer(&opt->ack_timeout_timer);
						#else
				    cancel_delayed_work(&opt->ack_timeout_work);
				    #endif
				    ppp_output_wakeup(&po->chan);
				}else if (opt->window<opt->max_window) ++opt->window;


				if (opt->stat->pt_seq && opt->ack_recv > opt->stat->pt_seq){
					struct timeval tv;
					unsigned int rtt;
					do_gettimeofday(&tv);
					rtt = (tv.tv_sec - opt->stat->pt_time.tv_sec)*1000000+
						tv.tv_usec-opt->stat->pt_time.tv_usec;
					opt->stat->rtt = (opt->stat->rtt + rtt) / 2;
					if (opt->stat->rtt>opt->timeout) opt->stat->rtt=opt->timeout;
					opt->stat->pt_seq=0;
				}
		}else do_ack_timeout_work(po);

		/* test if payload present */
		if (!PPTP_GRE_IS_S(header->flags)){
			opt->stat->rx_acks++;
			goto drop;
		}
	}

	headersize  = sizeof(*header);
	payload_len = ntohs(header->payload_len);
	seq         = ntohl(header->seq);

	/* no ack present? */
	if (!PPTP_GRE_IS_A(header->ver)) headersize -= sizeof(header->ack);
	/* check for incomplete packet (length smaller than expected) */
	if (skb->len- headersize < payload_len){
		if (log_level>=1)
			printk("PPTP: discarding truncated packet (expected %d, got %d bytes)\n",
						payload_len, skb->len- headersize);
		opt->stat->rx_truncated++;
		goto drop;
	}

	payload=skb->data+headersize;
	/* check for expected sequence number */
	if ((seq == opt->seq_recv + 1) || (!opt->timeout &&
			(seq > opt->seq_recv + 1 || WRAPPED(seq, opt->seq_recv)))){
		if ( log_level >= 3 && opt->seq_sent<=log_packets)
			printk("PPTP[%i]: accepting packet %d size=%i 
(%02x %02x %02x %02x %02x %02x)\n",opt->src_addr.call_id, seq,payload_len,
				*(payload +0),
				*(payload +1),
				*(payload +2),
				*(payload +3),
				*(payload +4),
				*(payload +5));
		opt->stat->rx_accepted++;
		opt->stat->rx_lost+=seq-(opt->seq_recv + 1);
		opt->seq_recv = seq;
		#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
		schedule_task(&opt->ack_work);
		#else
		schedule_work(&opt->ack_work);
		#endif

		skb_pull(skb,headersize);

		if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI){
			/* chop off address/control */
			if (skb->len < 3)
				goto drop;
			skb_pull(skb,2);
		}

		if ((*skb->data) & 1){
			/* protocol is compressed */
			skb_push(skb, 1)[0] = 0;
		}
		
		skb->ip_summed=CHECKSUM_NONE;
		ppp_input(&po->chan,skb);

		return 1;
	/* out of order, check if the number is too low and discard the packet.
	* (handle sequence number wrap-around, and try to do it right) */
	}else if ( seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq) ){
		if ( log_level >= 1)
			printk("PPTP[%i]: discarding duplicate or old packet %d 
(expecting %d)\n",opt->src_addr.call_id,
							seq, opt->seq_recv + 1);
		opt->stat->rx_underwin++;
	/* sequence number too high, is it reasonably close? */
	}else /*if ( seq < opt->seq_recv + MISSING_WINDOW ||
						 WRAPPED(seq, opt->seq_recv + MISSING_WINDOW) )*/{
		opt->stat->rx_buffered++;
		if ( log_level >= 2 && new )
				printk("PPTP[%i]: buffering packet %d (expecting %d, lost or 
reordered)\n",opt->src_addr.call_id,
						seq, opt->seq_recv+1);
		return 0;
	/* no, packet must be discarded */
	}/*else{
		if ( log_level >= 1 )
			printk("PPTP[%i]: discarding bogus packet %d 
(expecting %d)\n",opt->src_addr.call_id,
							seq, opt->seq_recv + 1);
	}*/
drop:
	kfree_skb(skb);
	return -1;
}


static int pptp_rcv(struct sk_buff *skb)
{
	struct pptp_gre_header *header;
	struct pppox_sock *po;
	struct pptp_opt *opt;
	
	if (log_level>=4) printk("PPTP: pptp_rcv rx_stop=%i\n",rx_stop);
	
	if (rx_stop) goto drop;

  if (!pskb_may_pull(skb, 12))
		goto drop;

	header = (struct pptp_gre_header *)skb->data;

	if (    /* version should be 1 */
					((header->ver & 0x7F) != PPTP_GRE_VER) ||
					/* PPTP-GRE protocol for PPTP */
					(ntohs(header->protocol) != PPTP_GRE_PROTO)||
					/* flag C should be clear   */
					PPTP_GRE_IS_C(header->flags) ||
					/* flag R should be clear   */
					PPTP_GRE_IS_R(header->flags) ||
					/* flag K should be set     */
					(!PPTP_GRE_IS_K(header->flags)) ||
					/* routing and recursion ctrl = 0  */
					((header->flags&0xF) != 0)){
			/* if invalid, discard this packet */
		if (log_level>=1)
			printk("PPTP: Discarding GRE: %X %X %X %X %X %X\n",
							header->ver&0x7F, ntohs(header->protocol),
							PPTP_GRE_IS_C(header->flags),
							PPTP_GRE_IS_R(header->flags),
							PPTP_GRE_IS_K(header->flags),
							header->flags & 0xF);
		goto drop;
	}

	dst_release(skb->dst);
	skb->dst = NULL;
	nf_reset(skb);

	if ((po=lookup_chan(htons(header->call_id)))) {
		if (!(SK_STATE(sk_pppox(po))&PPPOX_BOUND))
			goto drop;
		if (!po->chan.ppp){
			printk("PPTP: received packed, but ppp is down\n");
			goto drop;
		}
		opt=&po->proto.pptp;
		spin_lock_bh(&opt->rcv_lock);
		if (__pptp_rcv(po,skb,1)){
			spin_unlock_bh(&opt->rcv_lock);
			do_buf_work(po);
		}else{
			__net_timestamp(skb);
			skb_queue_tail(&opt->skb_buf, skb);
			#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
			mod_timer(&opt->buf_timer,opt->stat->rtt/100*HZ/100000);
			#else
			schedule_delayed_work(&opt->buf_work,opt->stat->rtt/100*HZ/10000);
			#endif
			spin_unlock_bh(&opt->rcv_lock);
		}
		goto out;
	}else {
		if (log_level>=1)
			printk("PPTP: Discarding packet from unknown 
call_id %i\n",header->call_id);
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
	}

drop:
	kfree_skb(skb);
out:
	return 0;
}


//=============== PROC =======================
static int proc_output (struct pppox_sock *po,char *buf)
{
	struct pptp_opt *opt=&po->proto.pptp;
	struct gre_statistics *stat=opt->stat;
	char *p=buf;
	p+=sprintf(p,"rx accepted  = %d\n",stat->rx_accepted);
	p+=sprintf(p,"rx lost      = %d\n",stat->rx_lost);
	p+=sprintf(p,"rx under win = %d\n",stat->rx_underwin);
	p+=sprintf(p,"rx buffered  = %d\n",stat->rx_buffered);
	p+=sprintf(p,"rx invalid   = %d\n",stat->rx_invalid);
	p+=sprintf(p,"rx acks      = %d\n",stat->rx_acks);
	p+=sprintf(p,"tx sent      = %d\n",stat->tx_sent);
	p+=sprintf(p,"tx failed    = %d\n",stat->tx_failed);
	p+=sprintf(p,"tx acks      = %d\n",stat->tx_acks);
	p+=sprintf(p,"rtt          = %d\n",stat->rtt);
	p+=sprintf(p,"timeout      = %d\n",opt->timeout);
	p+=sprintf(p,"window       = %d\n",opt->window);
	p+=sprintf(p,"max window   = %d\n",opt->max_window);

	return p-buf;
}
static int read_proc(char *page, char **start, off_t off,int count, int *eof, 
void *data)
{
	struct pppox_sock *po = data;
	int len = proc_output (po,page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len>count) len = count;
	if (len<0) len = 0;
	return len;
}

int ctrl_write_proc(struct file* file,const char __user *buffer,unsigned long 
count,void *data)
{
	int res=count;
	char *tmp_buf=kmalloc(count+1,GFP_KERNEL);
	if (copy_from_user(tmp_buf,buffer,count))
		res=-EFAULT;
	else{
		int val;
		tmp_buf[count]=0;
		if (sscanf(tmp_buf," log_level = %i",&val)==1) log_level=val;
		else if (sscanf(tmp_buf," log_packets = %i",&val)==1) log_packets=val;
		else if (sscanf(tmp_buf," rx_stop = %i",&val)==1) rx_stop=val;
		else res=-EINVAL;
	}
	kfree(tmp_buf);
	return res;
}
int stat_write_proc(struct file* file,const char __user *buffer,unsigned long 
count,void *data)
{
	int res=count;
	char *tmp_buf=kmalloc(count+1,GFP_KERNEL);
	if (copy_from_user(tmp_buf,buffer,count))
		res=-EFAULT;
	else{
		int val;
		tmp_buf[count]=0;
		if (sscanf(tmp_buf," enable = %i",&val)==1){
			if (!statistics && val){
				stat_last_update=jiffies;
				schedule_delayed_work(&stat_work,stat_collect_time*HZ);
			}
			else if (statistics && !val){
				cancel_delayed_work(&stat_work);
			}
			statistics=val;
		}
		else if (sscanf(tmp_buf," collect_time = %i",&val)==1){
			if (val<=0) res=-EINVAL;
			else stat_collect_time=val;
		}
		else res=-EINVAL;
	}
	kfree(tmp_buf);
	return res;
}
static int stat_read_proc(char *page, char **start, off_t off,int count, int 
*eof, void *data)
{
	int len;
	char *p=page;
	p+=sprintf(p,"enabled      = %i\n",statistics);
	p+=sprintf(p,"collect_time = %i\n",stat_collect_time);
	p+=sprintf(p,"\nperformance counters per sec:\n");
	p+=sprintf(p,"tx_packets   = %i\n",tx_packets_avg);
	p+=sprintf(p,"rx_packets   = %i\n",rx_packets_avg);
	p+=sprintf(p,"ack_timeouts = %i\n",ack_timeouts_avg);
	p+=sprintf(p,"ack_works    = %i\n",ack_works_avg);
	p+=sprintf(p,"buf_works    = %i\n",buf_works_avg);
	len=p-page;
	
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len>count) len = count;
	if (len<0) len = 0;
	return len;
}
//============================================



static int pptp_bind(struct socket *sock,struct sockaddr *uservaddr,int 
sockaddr_len)
{
	struct sock *sk = sock->sk;
	struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
	struct pppox_sock *po = pppox_sk(sk);
	struct pptp_opt *opt=&po->proto.pptp;
	int error=0;

	if (log_level>=1)
		printk("PPTP: bind: addr=%X call_id=%i\n",sp->sa_addr.pptp.sin_addr.s_addr,
						sp->sa_addr.pptp.call_id);
	lock_sock(sk);

	opt->src_addr=sp->sa_addr.pptp;
	if (sp->sa_addr.pptp.call_id){
		if (lookup_chan(sp->sa_addr.pptp.call_id)){
			error=-EBUSY;
			goto end;
		}
		add_chan(po);
	}else{
		add_free_chan(po);
		if (!opt->src_addr.call_id)
			error=-EBUSY;
		if (log_level>=1)
			printk("PPTP: using call_id %i\n",opt->src_addr.call_id);
	}

 end:
	release_sock(sk);
	return error;
}

static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
		  int sockaddr_len, int flags)
{
	struct sock *sk = sock->sk;
	struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
	struct pppox_sock *po = pppox_sk(sk);
	struct pptp_opt *opt=&po->proto.pptp;
	struct rtable *rt;     			/* Route to the other host */
	int error=0;

	if (log_level>=1)
		printk("PPTP[%i]: connect: addr=%X call_id=%i\n",opt->src_addr.call_id,
						sp->sa_addr.pptp.sin_addr.s_addr,sp->sa_addr.pptp.call_id);

	lock_sock(sk);

	if (sp->sa_protocol != PX_PROTO_PPTP){
		error = -EINVAL;
		goto end;
	}

	/* Check for already bound sockets */
	if (SK_STATE(sk) & PPPOX_CONNECTED){
		error = -EBUSY;
		goto end;
	}

	/* Check for already disconnected sockets, on attempts to disconnect */
	if (SK_STATE(sk) & PPPOX_DEAD){
		error = -EALREADY;
		goto end;
	}

	if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr){
		error = -EINVAL;
		goto end;
	}

	po->chan.private=sk;
	po->chan.ops=&pptp_chan_ops;
	
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	{
		struct rt_key key = {
			.dst=opt->dst_addr.sin_addr.s_addr,
			.src=opt->src_addr.sin_addr.s_addr,
			.tos=RT_TOS(0),
		};
		if (ip_route_output_key(&rt, &key)) {
			return -EHOSTUNREACH;
		}
	}
	#else
	{
		struct flowi fl = {
				    .nl_u = { .ip4_u =
					      { .daddr = opt->dst_addr.sin_addr.s_addr,
						.saddr = opt->src_addr.sin_addr.s_addr,
						.tos = RT_CONN_FLAGS(sk) } },
				    .proto = IPPROTO_GRE };
		security_sk_classify_flow(sk, &fl);
		if (ip_route_output_key(&rt, &fl))
			return -EHOSTUNREACH;
		sk_setup_caps(sk, &rt->u.dst);
	}
	#endif
	po->chan.mtu=dst_mtu(&rt->u.dst);
	if (!po->chan.mtu) po->chan.mtu=PPP_MTU;
	po->chan.mtu-=PPTP_HEADER_OVERHEAD;
	
	po->chan.hdrlen=2+sizeof(struct pptp_gre_header);
	error = ppp_register_channel(&po->chan);
	if (error){
		printk(KERN_ERR "PPTP: failed to register PPP channel (%d)\n",error);
		goto end;
	}

	opt->dst_addr=sp->sa_addr.pptp;
	SK_STATE(sk) = PPPOX_CONNECTED;

 end:
	release_sock(sk);
	return error;
}

static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
		  int *usockaddr_len, int peer)
{
	int len = sizeof(struct sockaddr_pppox);
	struct sockaddr_pppox sp;

	sp.sa_family	= AF_PPPOX;
	sp.sa_protocol	= PX_PROTO_PPTP;
	sp.sa_addr.pptp=pppox_sk(sock->sk)->proto.pptp.src_addr;

	memcpy(uaddr, &sp, len);

	*usockaddr_len = len;

	return 0;
}

static int pptp_setsockopt(struct socket *sock, int level, int optname,
	char* optval, int optlen)
{
	struct sock *sk = sock->sk;
	struct pppox_sock *po = pppox_sk(sk);
	struct pptp_opt *opt=&po->proto.pptp;
	int val;
	
	if (optlen!=sizeof(int))
		return -EINVAL;

	if (get_user(val,(int __user*)optval))
		return -EFAULT;

	switch(optname) {
		case PPTP_SO_TIMEOUT:
			opt->timeout=val;
			break;
		case PPTP_SO_WINDOW:
			opt->max_window=val;
			opt->window=val/2;
			break;
		default:
				return -ENOPROTOOPT;
	}

	return 0;
}

static int pptp_getsockopt(struct socket *sock, int level, int optname,
	char* optval, int *optlen)
{
	struct sock *sk = sock->sk;
	struct pppox_sock *po = pppox_sk(sk);
	struct pptp_opt *opt=&po->proto.pptp;
	int len,val;

	if (get_user(len,(int __user*)optlen))
		return -EFAULT;

	if (len<sizeof(int))
		return -EINVAL;

	switch(optname) {
		case PPTP_SO_TIMEOUT:
			val=opt->timeout;
			break;
		case PPTP_SO_WINDOW:
			val=opt->window;
			break;
		default:
				return -ENOPROTOOPT;
	}

	if (put_user(sizeof(int),(int __user*)optlen))
		return -EFAULT;

	if (put_user(val,(int __user*)optval))
		return -EFAULT;

	return 0;
}

static int pptp_release(struct socket *sock)
{
	struct sock *sk = sock->sk;
	struct pppox_sock *po;
	struct pptp_opt *opt;
	int error = 0;

	if (!sk)
		return 0;

	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)	
	if (sk->dead)
	#else
	if (sock_flag(sk, SOCK_DEAD))
	#endif
		return -EBADF;
	SK_STATE(sk) = PPPOX_DEAD;
	po = pppox_sk(sk);
	opt=&po->proto.pptp;

	if (log_level>=1)
		printk("PPTP[%i]: release\n",opt->src_addr.call_id);

	wake_up(&opt->wait);

	if (opt->src_addr.sin_addr.s_addr) {
		#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
		del_timer(&opt->buf_timer);
		del_timer(&opt->ack_timeout_timer);
		flush_scheduled_tasks();
		#else
		cancel_delayed_work(&opt->buf_work);
		cancel_delayed_work(&opt->ack_timeout_work);
		flush_scheduled_work();
		#endif
		skb_queue_purge(&opt->skb_buf);
		del_chan(po);

		if (test_bit(PPTP_FLAG_PROC,(unsigned long*)&opt->flags)) {
			char unit[10];
			sprintf(unit,"ppp%i",ppp_unit_number(&po->chan));
			remove_proc_entry(unit,proc_dir);
		}
	}

	pppox_unbind_sock(sk);

	kfree(opt->stat);

	sock_orphan(sk);
	sock->sk = NULL;

	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)	
	skb_queue_purge(&sk->receive_queue);
	#else
	skb_queue_purge(&sk->sk_receive_queue);
	#endif
	sock_put(sk);

	return error;
}


#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
static struct proto pptp_sk_proto = {
	.name	  = "PPTP",
	.owner	  = THIS_MODULE,
	.obj_size = sizeof(struct pppox_sock),
};
#endif

static struct proto_ops pptp_ops = {
    .family		= AF_PPPOX,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
    .owner		= THIS_MODULE,
#endif    
    .release		= pptp_release,
    .bind		=  pptp_bind,
    .connect		= pptp_connect,
    .socketpair		= sock_no_socketpair,
    .accept		= sock_no_accept,
    .getname		= pptp_getname,
    .poll		= sock_no_poll,
    .listen		= sock_no_listen,
    .shutdown		= sock_no_shutdown,
    .setsockopt		= pptp_setsockopt,
    .getsockopt		= pptp_getsockopt,
    .sendmsg		= sock_no_sendmsg,
    .recvmsg		= sock_no_recvmsg,
    .mmap		= sock_no_mmap,
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
    .ioctl		= pppox_ioctl,
    #endif
};


#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
static void pptp_sock_destruct(struct sock *sk)
{
	if (sk->protinfo.destruct_hook)
		kfree(sk->protinfo.destruct_hook);
		
	MOD_DEC_USE_COUNT;
}

static int pptp_create(struct socket *sock)
{
	int error = -ENOMEM;
	struct sock *sk;
	struct pppox_sock *po;
	struct pptp_opt *opt;

	MOD_INC_USE_COUNT;

	sk = sk_alloc(PF_PPPOX, GFP_KERNEL, 1);
	if (!sk)
		goto out;

	sock_init_data(sock, sk);

	sock->state = SS_UNCONNECTED;
	sock->ops   = &pptp_ops;

	//sk->sk_backlog_rcv = pppoe_rcv_core;
	sk->state	   = PPPOX_NONE;
	sk->type	   = SOCK_STREAM;
	sk->family	   = PF_PPPOX;
	sk->protocol	   = PX_PROTO_PPTP;

	sk->protinfo.pppox=kzalloc(sizeof(struct pppox_sock),GFP_KERNEL);
	sk->destruct=pptp_sock_destruct;
	sk->protinfo.destruct_hook=sk->protinfo.pppox;
	
	po = pppox_sk(sk);
	po->sk=sk;
	opt=&po->proto.pptp;

	opt->window=max_window/2;
	opt->max_window=max_window;
	opt->timeout=0;
	opt->flags=0;
	opt->seq_sent=0; opt->seq_recv=-1;
	opt->ack_recv=0; opt->ack_sent=-1;
	skb_queue_head_init(&opt->skb_buf);
  INIT_TQUEUE(&opt->ack_work,(void(*)(void*))ack_work,po);
	INIT_TQUEUE(&opt->buf_work,(void(*)(void*))buf_work,po);
	INIT_TQUEUE(&opt->ack_timeout_work,(void(*)(void*))ack_timeout_work,po);
	INIT_TIMER(&opt->buf_timer,(void(*)(unsigned long))buf_timer,(long int)po);
	INIT_TIMER(&opt->ack_timeout_timer,(void(*)(unsigned long))ack_timeout_timer,
(long int)po);
	opt->stat=kzalloc(sizeof(*opt->stat),GFP_KERNEL);
	init_waitqueue_head(&opt->wait);
	spin_lock_init(&opt->xmit_lock);
	spin_lock_init(&opt->rcv_lock);

	error = 0;
out:
	return error;
}
#else
static int pptp_create(struct socket *sock)
{
	int error = -ENOMEM;
	struct sock *sk;
	struct pppox_sock *po;
	struct pptp_opt *opt;

	sk = sk_alloc(PF_PPPOX, GFP_KERNEL, &pptp_sk_proto, 1);
	if (!sk)
		goto out;

	sock_init_data(sock, sk);

	sock->state = SS_UNCONNECTED;
	sock->ops   = &pptp_ops;

	//sk->sk_backlog_rcv = pppoe_rcv_core;
	sk->sk_state	   = PPPOX_NONE;
	sk->sk_type	   = SOCK_STREAM;
	sk->sk_family	   = PF_PPPOX;
	sk->sk_protocol	   = PX_PROTO_PPTP;

	po = pppox_sk(sk);
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	po->sk=sk;
	#endif
	opt=&po->proto.pptp;

	opt->window=max_window/2;
	opt->max_window=max_window;
	opt->timeout=0;
	opt->flags=0;
	opt->seq_sent=0; opt->seq_recv=-1;
	opt->ack_recv=0; opt->ack_sent=-1;
	skb_queue_head_init(&opt->skb_buf);
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
  INIT_WORK(&opt->ack_work,(work_func_t)ack_work);
	INIT_DELAYED_WORK(&opt->buf_work,(work_func_t)_buf_work);
	INIT_DELAYED_WORK(&opt->ack_timeout_work,(work_func_t)_ack_timeout_work);
  #else
	INIT_WORK(&opt->ack_work,(void(*)(void*))ack_work,po);
	INIT_WORK(&opt->buf_work,(void(*)(void*))buf_work,po);
	INIT_WORK(&opt->ack_timeout_work,(void(*)(void*))ack_timeout_work,po);
  #endif
	opt->stat=kzalloc(sizeof(*opt->stat),GFP_KERNEL);
	init_waitqueue_head(&opt->wait);
	spin_lock_init(&opt->xmit_lock);
	spin_lock_init(&opt->rcv_lock);

	error = 0;
out:
	return error;
}
#endif


static int pptp_ioctl(struct socket *sock, unsigned int cmd, unsigned long 
arg)
{
	struct sock *sk = sock->sk;
	struct pppox_sock *po = pppox_sk(sk);
	struct pptp_opt *opt=&po->proto.pptp;
	int res=-EINVAL;

	switch (cmd) {
	case PPPTPIOWFP:
		release_sock(sk);
		res=wait_event_timeout(opt->wait,opt->seq_sent||SK_STATE(sk) == 
PPPOX_DEAD,arg*HZ);
		res=(res&&res<HZ)?1:res/HZ;
		lock_sock(sk);
		break;
	}
	
	return res;
}



static struct pppox_proto pppox_pptp_proto = {
    .create	= pptp_create,
    .ioctl	= pptp_ioctl,
	  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
    .owner	= THIS_MODULE,
    #endif
};


#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
static struct inet_protocol net_pptp_protocol = {
	.handler	= pptp_rcv,
	//.err_handler	=	pptp_err,
	.protocol = IPPROTO_GRE,
	.name     = "PPTP",
};
#else
static struct net_protocol net_pptp_protocol = {
	.handler	= pptp_rcv,
	//.err_handler	=	pptp_err,
};
#endif

static int pptp_init_module(void)
{
	struct proc_dir_entry *res;
	int err=0;
	printk(KERN_INFO "PPTP driver version " PPTP_DRIVER_VERSION "\n");

	if (stat_collect_time==0)
	{
		printk(KERN_ERR "PPTP: stat_collect_time must be >0\n");
		return -EINVAL;
	}

	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	inet_add_protocol(&net_pptp_protocol);
	#else
	if (inet_add_protocol(&net_pptp_protocol, IPPROTO_GRE) < 0) {
		printk(KERN_INFO "PPTP: can't add protocol\n");
		goto out;
	}
	#endif

	#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	err = proto_register(&pptp_sk_proto, 0);
	if (err){
		printk(KERN_INFO "PPTP: can't register sk_proto\n");
		goto out_inet_del_protocol;
	}
	#endif

 	err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto);
	if (err){
		printk(KERN_INFO "PPTP: can't register pppox_proto\n");
		goto out_unregister_sk_proto;
	}

	proc_dir=proc_mkdir("net/pptp",NULL);
	if (proc_dir){
	    proc_dir->owner=THIS_MODULE;
	    
	    res=create_proc_entry("ctrl",0,proc_dir);
	    if (!res)printk(KERN_ERR "PPTP: failed to create ctrl proc entry\n");
	    else res->write_proc=ctrl_write_proc;
	
	    res=create_proc_entry("stat",0,proc_dir);
	    if (!res)printk(KERN_ERR "PPTP: failed to create stat proc entry\n");
	    else{
	    	res->write_proc=stat_write_proc;
	    	res->read_proc=stat_read_proc;
	    }
	}else printk(KERN_ERR "PPTP: failed to create proc dir\n");
	//console_verbose();

  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
  // TODO: not implemented
  #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
	INIT_DELAYED_WORK(&stat_work,(work_func_t)do_stat_work);
  #else
	INIT_WORK(&stat_work,(void(*)(void*))do_stat_work,NULL);
  #endif
	
	stat_last_update=jiffies;
	if (statistics)
		schedule_delayed_work(&stat_work,stat_collect_time*HZ);

out:
	return err;
out_unregister_sk_proto:
	#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	proto_unregister(&pptp_sk_proto);
	#endif
out_inet_del_protocol:
	
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	inet_del_protocol(&net_pptp_protocol);
	#else
	inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE);
	#endif
	goto out;
}

static void pptp_exit_module(void)
{
	cancel_delayed_work(&stat_work);
	flush_scheduled_work();
	
	unregister_pppox_proto(PX_PROTO_PPTP);
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	inet_del_protocol(&net_pptp_protocol);
	#else
	proto_unregister(&pptp_sk_proto);
	inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE);
	#endif

	if (proc_dir)
	{
		remove_proc_entry("ctrl",proc_dir);
		remove_proc_entry("stat",proc_dir);
		remove_proc_entry("pptp",NULL);
	}
}


module_init(pptp_init_module);
module_exit(pptp_exit_module);


^ permalink raw reply

* Re: Bonding support for eth1394?
From: Stefan Richter @ 2007-10-13  7:08 UTC (permalink / raw)
  To: Karl Svec; +Cc: netdev, linux1394-devel
In-Reply-To: <FFB081ED-646B-489D-8836-FBA619A57E3E@gmail.com>

(Adding Cc: netdev)

Karl Svec wrote to linux1394-devel:
> I'm trying to create a bonded network interface for an Xdmx setup out  
> of a regular ethernet device (sungem) and a firewire ethernet  
> (eth1394) device. However, ifenslave fails on the firewire device,  
> and gives me the following output:
> 
> Master 'bond0' : Error : SIOCBONDENSLAVE failed : Operation not  
> supported
> 
> I'm assuming that this error message means that bonding support is  
> not implemented in the eth1394 driver. If this is the case, I'd like  
> to request that bonding support be added to the eth1394 driver. Is  
> this a reasonable request (i.e. is this something that was left out  
> of the driver because it is difficult to do, or was it left out  
> because nobody anticipated anyone using this feature)?
> 
> I'm happy to do any testing.
> 
> Thanks for your time,

What criteria has a networking driver to fulfill to be interoperable
with the bonding driver?  From a brief look at the top of the files in
linux/drivers/net/bonding/, it appears that it is meant for Ethernet
drivers.

Unlike eth1394's name suggests, it is not Ethernet over FireWire but
IPv4 over FireWire, in a partial implementation of RFC 2734.
http://www.faqs.org/rfcs/rfc2734.html
What is missing to fully support RFC 2734 is multicast capability.
http://lxr.linux.no/source/drivers/ieee1394/eth1394.c#L25

The bonding sources have a few occurrences of EOPNOTSUPP.  Unless I
missed something, they are all related to setting the hardware address
of the interface.  AFAICS this is impossible with IP over FireWire.  If
it is crucial to bonding to be able to change the slaves' hardware
addresses, then you are out of luck.
-- 
Stefan Richter
-=====-=-=== =-=- -==-=
http://arcgraph.de/sr/

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/

^ permalink raw reply

* [PATCH 5/5] net core: fix kernel-doc for new function parameters
From: Randy Dunlap @ 2007-10-13  4:17 UTC (permalink / raw)
  To: lkml, netdev; +Cc: torvalds, akpm, davem

From: Randy Dunlap <randy.dunlap@oracle.com>

Fix networking code kernel-doc for newly added parameters.

Warning(linux-2.6.23-git2//net/core/sock.c:879): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:570): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:594): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:617): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:641): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:667): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:722): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:959): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:1195): No description found for parameter 'dev'
Warning(linux-2.6.23-git2//net/core/dev.c:2105): No description found for parameter 'n'
Warning(linux-2.6.23-git2//net/core/dev.c:3272): No description found for parameter 'net'
Warning(linux-2.6.23-git2//net/core/dev.c:3445): No description found for parameter 'net'
Warning(linux-2.6.23-git2//include/linux/netdevice.h:1301): No description found for parameter 'cpu'

Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
---
 include/linux/netdevice.h |    1 +
 net/core/dev.c            |   13 +++++++++++--
 net/core/sock.c           |    1 +
 3 files changed, 13 insertions(+), 2 deletions(-)

--- linux-2.6.23-git2.orig/include/linux/netdevice.h
+++ linux-2.6.23-git2/include/linux/netdevice.h
@@ -1294,6 +1294,7 @@ static inline void netif_rx_complete(str
 /**
  *	netif_tx_lock - grab network device transmit lock
  *	@dev: network device
+ *	@cpu: cpu number of lock owner
  *
  * Get network device transmit lock
  */
--- linux-2.6.23-git2.orig/net/core/sock.c
+++ linux-2.6.23-git2/net/core/sock.c
@@ -869,6 +869,7 @@ static inline void sock_lock_init(struct
 
 /**
  *	sk_alloc - All socket objects are allocated here
+ *	@net: the applicable net namespace
  *	@family: protocol family
  *	@priority: for allocation (%GFP_KERNEL, %GFP_ATOMIC, etc)
  *	@prot: struct proto associated with this new sock instance
--- linux-2.6.23-git2.orig/net/core/dev.c
+++ linux-2.6.23-git2/net/core/dev.c
@@ -557,6 +557,7 @@ __setup("netdev=", netdev_boot_setup);
 
 /**
  *	__dev_get_by_name	- find a device by its name
+ *	@net: the applicable net namespace
  *	@name: name to find
  *
  *	Find an interface by name. Must be called under RTNL semaphore
@@ -581,6 +582,7 @@ struct net_device *__dev_get_by_name(str
 
 /**
  *	dev_get_by_name		- find a device by its name
+ *	@net: the applicable net namespace
  *	@name: name to find
  *
  *	Find an interface by name. This can be called from any
@@ -604,6 +606,7 @@ struct net_device *dev_get_by_name(struc
 
 /**
  *	__dev_get_by_index - find a device by its ifindex
+ *	@net: the applicable net namespace
  *	@ifindex: index of device
  *
  *	Search for an interface by index. Returns %NULL if the device
@@ -629,6 +632,7 @@ struct net_device *__dev_get_by_index(st
 
 /**
  *	dev_get_by_index - find a device by its ifindex
+ *	@net: the applicable net namespace
  *	@ifindex: index of device
  *
  *	Search for an interface by index. Returns NULL if the device
@@ -651,6 +655,7 @@ struct net_device *dev_get_by_index(stru
 
 /**
  *	dev_getbyhwaddr - find a device by its hardware address
+ *	@net: the applicable net namespace
  *	@type: media type of device
  *	@ha: hardware address
  *
@@ -709,6 +714,7 @@ EXPORT_SYMBOL(dev_getfirstbyhwtype);
 
 /**
  *	dev_get_by_flags - find any device with given flags
+ *	@net: the applicable net namespace
  *	@if_flags: IFF_* values
  *	@mask: bitmask of bits in if_flags to check
  *
@@ -948,6 +954,7 @@ void netdev_state_change(struct net_devi
 
 /**
  *	dev_load 	- load a network module
+ *	@net: the applicable net namespace
  *	@name: name of interface
  *
  *	If a network interface is not present and the process has suitable
@@ -1185,7 +1192,7 @@ int unregister_netdevice_notifier(struct
 /**
  *	call_netdevice_notifiers - call all network notifier blocks
  *      @val: value passed unmodified to notifier function
- *      @v:   pointer passed unmodified to notifier function
+ *      @dev: net_device pointer passed unmodified to notifier function
  *
  *	Call all network notifier blocks.  Parameters and return value
  *	are as for raw_notifier_call_chain().
@@ -2097,7 +2104,7 @@ static int process_backlog(struct napi_s
 
 /**
  * __napi_schedule - schedule for receive
- * @napi: entry to schedule
+ * @n: entry to schedule
  *
  * The entry's receive function will be scheduled to run
  */
@@ -3259,6 +3266,7 @@ static int dev_ifsioc(struct net *net, s
 
 /**
  *	dev_ioctl	-	network device ioctl
+ *	@net: the applicable net namespace
  *	@cmd: command to issue
  *	@arg: pointer to a struct ifreq in user space
  *
@@ -3436,6 +3444,7 @@ int dev_ioctl(struct net *net, unsigned 
 
 /**
  *	dev_new_index	-	allocate an ifindex
+ *	@net: the applicable net namespace
  *
  *	Returns a suitable unique value for a new device interface
  *	number.  The caller must hold the rtnl semaphore or the

^ permalink raw reply


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