* Re: netif_tx_disable and lockless TX
From: Herbert Xu @ 2006-05-31 6:31 UTC (permalink / raw)
To: David Miller; +Cc: mchan, jgarzik, netdev
In-Reply-To: <20060530.232626.00456312.davem@davemloft.net>
On Tue, May 30, 2006 at 11:26:26PM -0700, David Miller wrote:
>
> I don't think it will be worthwhile to keep NAPI around just for
> TX completion. Sure the dev_kfree_skb() will schedule software
> interrupt work to do the actual free, but the TX ring walking
> and dev_kfree_skb() calls will be in hard IRQ context.
Sure we won't need all of NAPI. But would it be that bad to schedule
a tasklet to do the TX completion?
> > > The only regret I have about that is we will go back to not being able
> > > to profile ->hard_start_xmit() very well in such drivers.
> >
> > Can you elborate on that? I think I've already removed all references
> > to this in my memory :)
>
> If you disable IRQs in the ->hard_start_xmit() handler, you don't
> get timer based profiling ticks. Currently we do.
Oh, I'm not proposing that we disable IRQs on xmit_lock at all. What
I'm saying is that for tg3, since both xmit_lock and tx_lock are BH-
disabling locks, they are equivalent and therefore we can replace
tx_lock with xmit_lock.
For other lockless NICs that have IRQ disabling tx_locks, they can
be converted to BH-disabling ones and then be able to use xmit_lock.
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: netif_tx_disable and lockless TX
From: David Miller @ 2006-05-31 6:26 UTC (permalink / raw)
To: herbert; +Cc: mchan, jgarzik, netdev
In-Reply-To: <20060531051451.GA7110@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 31 May 2006 15:14:51 +1000
> On Tue, May 30, 2006 at 10:11:17PM -0700, David Miller wrote:
> >
> > It will come back when net channels arrive :-) This is because net
> > channel drivers will do all the work in hard IRQs and not in a NAPI or
> > NAPI-like context.
>
> I thought the current channel stuff is RX only. Is TX completion moving
> to IRQ context as well?
I don't think it will be worthwhile to keep NAPI around just for
TX completion. Sure the dev_kfree_skb() will schedule software
interrupt work to do the actual free, but the TX ring walking
and dev_kfree_skb() calls will be in hard IRQ context.
> > The only regret I have about that is we will go back to not being able
> > to profile ->hard_start_xmit() very well in such drivers.
>
> Can you elborate on that? I think I've already removed all references
> to this in my memory :)
If you disable IRQs in the ->hard_start_xmit() handler, you don't
get timer based profiling ticks. Currently we do.
^ permalink raw reply
* Re: [PATCH-2.4] forcedeth update to 0.50
From: Willy Tarreau @ 2006-05-31 5:54 UTC (permalink / raw)
To: Manfred Spraul; +Cc: marcelo, linux-kernel, netdev, Ayaz Abdulla
In-Reply-To: <447D2EA8.8020001@colorfullife.com>
On Wed, May 31, 2006 at 07:50:32AM +0200, Manfred Spraul wrote:
> Hi Willy,
>
> Willy Tarreau wrote:
>
> >I started from the latest backport you sent in september (0.42) and
> >incrementally applied 2.6 updates. I stopped at 0.50 which provides
> >VLAN support, because after this one, there are some 2.4-incompatible
> >changes (64bit consistent memory allocation for rings, and MSI/MSIX
> >support).
> >
> >
> >
> I agree, 2.4 needs a backport. Either a full backport as you did, or a
> minimal one-liner fix.
> Right now, the driver is not usable due to an incorrect initialization.
> Or to be more accurate:
> # modprobe
> # ifup
> works.
> But
> # modprobe
> # ifup
> # ifdown
> # ifup
> causes a misconfiguration, and the nic hangs hard after a few MB. And
> recent distros do the equivalent of ifup/ifdown/ifup somewhere in the
> initialization.
That's what I read in one of the changelogs, but I'm not sure at all that
it's what happened, because I had the problem after an ifup only. What I
was doing with this box was pure performance tests which drew me to compare
the broadcom and nforce performance. My tests measured 3 creteria :
- number of HTTP/1.0 hits/s
- maximum data rate
- maximum packets/s
on tg3, I got around 45 khits/s, 949 Mbps (TCP, =1.0 Gbps on wire) and
1.05 Mpps receive (I want to build a high speed load-balancer and a sniffer).
This was stable.
On the nforce, I tried with the hits/s first because it's a good indication
of hardware-based and driver-based optimizations. It reached 18 khits/s with
a lot of difficulty and the machine was stuck at 100% of one CPU. But it ran
for a few minutes like this. Then I tried data rate (which is the same test
with 1MB objects), and it failed after about 2 seconds and few megabytes (or
hundreds of megabytes) transferred.
I had to reboot to get it to work again. And I'm fairly sure that I did not
do down/up this time as well, but the test came to the same end.
That's why I'm not sure at all that the one-liner will be enough.
Moreover, after the update, I reached the same performance as with the
broadcom, with a slight improvement on packet reception (1.09 Mpps), and
low CPU usage (15%). So basically, the upgrade rendered the driver from
barely usable for SSH to very performant.
> Marcelo: Do you need a one-liner, or could you apply a large backport
> patch?
I would really vote for the full backport, and I can break it into pieces
if needed (I have them at hand, just have to re-inject the changelogs).
However, I have separate changes from 0.42 to 0.50, because I started
with your 0.30-0.42 backport patch.
I have this machine till the end of the week, so I can perform other tests
if you're interested in trying specific things.
> --
> Manfred
Cheers,
Willy
^ permalink raw reply
* Re: [PATCH-2.4] forcedeth update to 0.50
From: Manfred Spraul @ 2006-05-31 5:50 UTC (permalink / raw)
To: Willy Tarreau; +Cc: marcelo, linux-kernel, netdev, Ayaz Abdulla
In-Reply-To: <20060530220319.GA6945@w.ods.org>
Hi Willy,
Willy Tarreau wrote:
>I started from the latest backport you sent in september (0.42) and
>incrementally applied 2.6 updates. I stopped at 0.50 which provides
>VLAN support, because after this one, there are some 2.4-incompatible
>changes (64bit consistent memory allocation for rings, and MSI/MSIX
>support).
>
>
>
I agree, 2.4 needs a backport. Either a full backport as you did, or a
minimal one-liner fix.
Right now, the driver is not usable due to an incorrect initialization.
Or to be more accurate:
# modprobe
# ifup
works.
But
# modprobe
# ifup
# ifdown
# ifup
causes a misconfiguration, and the nic hangs hard after a few MB. And
recent distros do the equivalent of ifup/ifdown/ifup somewhere in the
initialization.
Marcelo: Do you need a one-liner, or could you apply a large backport patch?
--
Manfred
^ permalink raw reply
* Re: netif_tx_disable and lockless TX
From: Herbert Xu @ 2006-05-31 5:33 UTC (permalink / raw)
To: Michael Chan; +Cc: David Miller, jgarzik, netdev
In-Reply-To: <1551EAE59135BE47B544934E30FC4FC041BC92@NT-IRVA-0751.brcm.ad.broadcom.com>
On Tue, May 30, 2006 at 10:30:25PM -0700, Michael Chan wrote:
>
> I also don't see any problem. It looks like we don't have to set
> xmit_lock_owner, right?
Right, unless you want to call dev_queue_xmit recursively :)
--
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: netif_tx_disable and lockless TX
From: Michael Chan @ 2006-05-31 5:30 UTC (permalink / raw)
To: David Miller, herbert; +Cc: jgarzik, netdev
David Miller wrote:
> From: Herbert Xu <herbert@gondor.apana.org.au>
> Date: Wed, 31 May 2006 14:58:11 +1000
>
> > Yes, TG3 does not disable IRQs when taking its TX lock. So do you
see
> > any problems with replacing the TG3 TX lock using xmit_lock?
>
> I don't see any.
>
I also don't see any problem. It looks like we don't have to set
xmit_lock_owner, right?
^ permalink raw reply
* Re: netif_tx_disable and lockless TX
From: Herbert Xu @ 2006-05-31 5:14 UTC (permalink / raw)
To: David Miller; +Cc: mchan, jgarzik, netdev
In-Reply-To: <20060530.221117.85394939.davem@davemloft.net>
On Tue, May 30, 2006 at 10:11:17PM -0700, David Miller wrote:
>
> It will come back when net channels arrive :-) This is because net
> channel drivers will do all the work in hard IRQs and not in a NAPI or
> NAPI-like context.
I thought the current channel stuff is RX only. Is TX completion moving
to IRQ context as well?
> The only regret I have about that is we will go back to not being able
> to profile ->hard_start_xmit() very well in such drivers.
Can you elborate on that? I think I've already removed all references
to this in my memory :)
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: netif_tx_disable and lockless TX
From: David Miller @ 2006-05-31 5:11 UTC (permalink / raw)
To: herbert; +Cc: mchan, jgarzik, netdev
In-Reply-To: <20060531045811.GA6977@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 31 May 2006 14:58:11 +1000
> Hi Michael:
>
> On Tue, May 30, 2006 at 09:51:03PM -0700, Michael Chan wrote:
> >
> > > That's why I suggest that every NIC that uses this feature be forced
> > > to do what TG3 does so only BH disabling is needed. Once that's done
> > > they can just use xmit_lock and everyone will be happy again.
> >
> > As long as the tx completion is all done in NAPI, it can use BH
> > disabling
> > without irq disabling.
>
> Yes, TG3 does not disable IRQs when taking its TX lock. So do you see
> any problems with replacing the TG3 TX lock using xmit_lock?
I don't see any.
Thanks for reminding me about the IRQ vs. BH disabling issue.
It will come back when net channels arrive :-) This is because net
channel drivers will do all the work in hard IRQs and not in a NAPI or
NAPI-like context.
The only regret I have about that is we will go back to not being able
to profile ->hard_start_xmit() very well in such drivers.
^ permalink raw reply
* Re: netif_tx_disable and lockless TX
From: Herbert Xu @ 2006-05-31 4:58 UTC (permalink / raw)
To: Michael Chan; +Cc: David Miller, jgarzik, netdev
In-Reply-To: <1551EAE59135BE47B544934E30FC4FC041BC90@NT-IRVA-0751.brcm.ad.broadcom.com>
Hi Michael:
On Tue, May 30, 2006 at 09:51:03PM -0700, Michael Chan wrote:
>
> > That's why I suggest that every NIC that uses this feature be forced
> > to do what TG3 does so only BH disabling is needed. Once that's done
> > they can just use xmit_lock and everyone will be happy again.
>
> As long as the tx completion is all done in NAPI, it can use BH
> disabling
> without irq disabling.
Yes, TG3 does not disable IRQs when taking its TX lock. So do you see
any problems with replacing the TG3 TX lock using xmit_lock?
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: netif_tx_disable and lockless TX
From: Michael Chan @ 2006-05-31 4:51 UTC (permalink / raw)
To: Herbert Xu, David Miller; +Cc: jgarzik, netdev
Herbert Xu wrote:
> On Tue, May 30, 2006 at 09:13:22PM -0700, David Miller wrote:
> > As per the bug, I always keep thinking about changing how we
> > do the lockless stuff by simply making xmit_lock a pointer.
> > Drivers like tg3 can make it point to their local driver lock.
>
> This is equivalent to just using the xmit_lock in the driver, right?
> IIRC the problem was with IRQ disabling vs. BH disabling.
For drivers that always take a private tx lock between tx completion
and hard_start_xmit on top of the xmit_lock, getting rid of the
xmit_lock using LLTX is a net gain.
For drivers that don't need a private tx lock or rarely need it between
tx completion and hard_start_xmit, it makes little difference whether
you use xmit_lock or private lock with LLTX.
>
> That's why I suggest that every NIC that uses this feature be forced
> to do what TG3 does so only BH disabling is needed. Once that's done
> they can just use xmit_lock and everyone will be happy again.
>
As long as the tx completion is all done in NAPI, it can use BH
disabling
without irq disabling.
TG3 uses LLTX with a private tx_lock. BNX2 uses xmit_lock with a private
tx_lock that is rarely used. Both drivers use BH disabling. The profiles
of the 2 drivers are very similar.
^ permalink raw reply
* Re: netif_tx_disable and lockless TX
From: Herbert Xu @ 2006-05-31 4:17 UTC (permalink / raw)
To: David Miller; +Cc: mchan, jgarzik, netdev
In-Reply-To: <20060530.211322.51700292.davem@davemloft.net>
On Tue, May 30, 2006 at 09:13:22PM -0700, David Miller wrote:
>
> There were numbers posted for real drivers at one point, I
> do remember that, and the gains were non-trivial. At least
> enough to justify the feature.
I wonder if there are other ways of achieving the same improvement.
> As per the bug, I always keep thinking about changing how we
> do the lockless stuff by simply making xmit_lock a pointer.
> Drivers like tg3 can make it point to their local driver lock.
This is equivalent to just using the xmit_lock in the driver, right?
IIRC the problem was with IRQ disabling vs. BH disabling.
That's why I suggest that every NIC that uses this feature be forced
to do what TG3 does so only BH disabling is needed. Once that's done
they can just use xmit_lock and everyone will be happy again.
> This leaves still the cases like loopback which do no locking
> whatsoever. I don't know how to fit it into the above idea.
As far as I'm concerned loopback can do whatever it wants as long as
it doesn't create bugs in real NICs :)
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: netif_tx_disable and lockless TX
From: Roland Dreier @ 2006-05-31 4:13 UTC (permalink / raw)
To: Herbert Xu; +Cc: David S. Miller, mchan, Jeff Garzik, netdev
In-Reply-To: <20060531040307.GA6447@gondor.apana.org.au>
Herbert> However, lockless drivers do not take the xmit_lock so
Herbert> this method is ineffective. Such drivers need to do
Herbert> their own checking inside whatever locks that they do
Herbert> take. For example, tg3 could get around this by checking
Herbert> whether the queue is stopped in its hard_start_xmit
Herbert> function.
Yes, I had to add this to the IPoIB driver, because calling
netif_stop_queue() when the transmit ring was full was sometimes still
allowing hard_start_xmit to be called again:
/*
* Check if our queue is stopped. Since we have the LLTX bit
* set, we can't rely on netif_stop_queue() preventing our
* xmit function from being called with a full queue.
*/
if (unlikely(netif_queue_stopped(dev))) {
spin_unlock_irqrestore(&priv->tx_lock, flags);
return NETDEV_TX_BUSY;
}
this bug started a long thread a while back, but I don't remember if
there was any resolution.
Herbert> I must say though that I'm becoming less and less
Herbert> impressed by the lockless feature based on the number of
Herbert> problems that it has caused. Does anyone have any hard
Herbert> figures as to its effectiveness (excluding any stats
Herbert> relating to the loopback interface which can be easily
Herbert> separated from normal NIC drivers).
I don't have exact figures at hand, but I remember something like a 2 or
3 percent throughput improvement for IPoIB.
- R.
^ permalink raw reply
* Re: netif_tx_disable and lockless TX
From: David Miller @ 2006-05-31 4:13 UTC (permalink / raw)
To: herbert; +Cc: mchan, jgarzik, netdev
In-Reply-To: <20060531040307.GA6447@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 31 May 2006 14:03:07 +1000
> I must say though that I'm becoming less and less impressed by the
> lockless feature based on the number of problems that it has caused.
> Does anyone have any hard figures as to its effectiveness (excluding
> any stats relating to the loopback interface which can be easily
> separated from normal NIC drivers).
There were numbers posted for real drivers at one point, I
do remember that, and the gains were non-trivial. At least
enough to justify the feature.
As per the bug, I always keep thinking about changing how we
do the lockless stuff by simply making xmit_lock a pointer.
Drivers like tg3 can make it point to their local driver lock.
This leaves still the cases like loopback which do no locking
whatsoever. I don't know how to fit it into the above idea.
^ permalink raw reply
* netif_tx_disable and lockless TX
From: Herbert Xu @ 2006-05-31 4:03 UTC (permalink / raw)
To: David S. Miller, mchan, Jeff Garzik, netdev
Hi:
It has occured to me that the function netif_tx_disable may be unsafe
when used in drivers that do lockless transmission. In particular,
the function relies on taking the xmit_lock to ensure that no further
transmissions will occur until the queue is started again.
However, lockless drivers do not take the xmit_lock so this method
is ineffective. Such drivers need to do their own checking inside
whatever locks that they do take. For example, tg3 could get around
this by checking whether the queue is stopped in its hard_start_xmit
function.
I must say though that I'm becoming less and less impressed by the
lockless feature based on the number of problems that it has caused.
Does anyone have any hard figures as to its effectiveness (excluding
any stats relating to the loopback interface which can be easily
separated from normal NIC drivers).
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: Refactor Netlink connector?
From: Thomas Graf @ 2006-05-31 3:00 UTC (permalink / raw)
To: James Morris; +Cc: Evgeniy Polyakov, netdev, David S. Miller, Stephen Smalley
In-Reply-To: <Pine.LNX.4.64.0605271303050.13104@d.namei>
* James Morris <jmorris@namei.org> 2006-05-27 13:21
> Actually, a possible solution here is to completely remove all internal
> knowledge of netlink messages from SELinux and have the netfilter
> framework and protocols provide methods to determine message types and
> permissions.
Right, regarding generic netlink we can extend struct genl_ops to
include a policy stating what permissions are required. Besides
that we can extend struct nla_policy to support validating of
attributes.
^ permalink raw reply
* Re: [PATCH 2/9] [I/OAT] Driver for the Intel(R) I/OAT DMA engine
From: Adrian Bunk @ 2006-05-30 23:27 UTC (permalink / raw)
To: Chris Leech; +Cc: linux-kernel, netdev
In-Reply-To: <20060524002013.19403.57410.stgit@gitlost.site>
On Tue, May 23, 2006 at 05:20:13PM -0700, Chris Leech wrote:
> Adds a new ioatdma driver
>
> Signed-off-by: Chris Leech <christopher.leech@intel.com>
> ---
>
> drivers/dma/Kconfig | 9
> drivers/dma/Makefile | 1
> drivers/dma/ioatdma.c | 839 +++++++++++++++++++++++++++++++++++++++
> drivers/dma/ioatdma.h | 126 ++++++
> drivers/dma/ioatdma_hw.h | 52 ++
> drivers/dma/ioatdma_io.h | 118 +++++
> drivers/dma/ioatdma_registers.h | 126 ++++++
> 7 files changed, 1271 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> index f9ac4bc..0f15e76 100644
> --- a/drivers/dma/Kconfig
> +++ b/drivers/dma/Kconfig
> @@ -10,4 +10,13 @@ config DMA_ENGINE
> DMA engines offload copy operations from the CPU to dedicated
> hardware, allowing the copies to happen asynchronously.
>
> +comment "DMA Devices"
> +
> +config INTEL_IOATDMA
> + tristate "Intel I/OAT DMA support"
> + depends on DMA_ENGINE && PCI
> + default m
> + ---help---
> + Enable support for the Intel(R) I/OAT DMA engine.
> +
> endmenu
>...
- please don't use "default m"
- can you enhance the help text?
e.g. you could list which hardware contains this DMA engine
cu
Adrian
--
"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed
^ permalink raw reply
* [RFT] TCP congestion control and cleanup
From: Stephen Hemminger @ 2006-05-30 23:14 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
The following changes since commit d9ec5ad24ce80b7ef69a0717363db661d13aada5:
Linus Torvalds:
Merge branch 'upstream-fixes' of master.kernel.org:/.../jgarzik/libata-dev
are found in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/tcp-2.6.git
Bin Zhou:
TCP Veno congestion control
Angelo P. Castellani
TCP Compound congestion control
Stephen Hemminger:
TCP Compound quad root function
TCP minimum congestion window consolidation
TCP Probe congestion window tracing
Wong Edison:
TCP Low Priority congestion control
include/net/tcp.h | 4
net/Kconfig | 15 ++
net/ipv4/Kconfig | 32 +++
net/ipv4/Makefile | 4
net/ipv4/tcp_bic.c | 7 -
net/ipv4/tcp_compound.c | 448 +++++++++++++++++++++++++++++++++++++++++++++++
net/ipv4/tcp_cong.c | 6 -
net/ipv4/tcp_cubic.c | 6 -
net/ipv4/tcp_htcp.c | 9 -
net/ipv4/tcp_input.c | 13 +
net/ipv4/tcp_lp.c | 338 +++++++++++++++++++++++++++++++++++
net/ipv4/tcp_probe.c | 179 +++++++++++++++++++
net/ipv4/tcp_veno.c | 231 ++++++++++++++++++++++++
net/ipv4/tcp_westwood.c | 18 +-
14 files changed, 1270 insertions(+), 40 deletions(-)
create mode 100644 net/ipv4/tcp_compound.c
create mode 100644 net/ipv4/tcp_lp.c
create mode 100644 net/ipv4/tcp_probe.c
create mode 100644 net/ipv4/tcp_veno.c
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 3c989db..575636f 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -628,7 +628,7 @@ struct tcp_congestion_ops {
/* return slow start threshold (required) */
u32 (*ssthresh)(struct sock *sk);
/* lower bound for congestion window (optional) */
- u32 (*min_cwnd)(struct sock *sk);
+ u32 (*min_cwnd)(const struct sock *sk);
/* do new cwnd calculation (required) */
void (*cong_avoid)(struct sock *sk, u32 ack,
u32 rtt, u32 in_flight, int good_ack);
@@ -663,7 +663,7 @@ extern struct tcp_congestion_ops tcp_ini
extern u32 tcp_reno_ssthresh(struct sock *sk);
extern void tcp_reno_cong_avoid(struct sock *sk, u32 ack,
u32 rtt, u32 in_flight, int flag);
-extern u32 tcp_reno_min_cwnd(struct sock *sk);
+extern u32 tcp_reno_min_cwnd(const struct sock *sk);
extern struct tcp_congestion_ops tcp_reno;
static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state)
diff --git a/net/Kconfig b/net/Kconfig
index 4193cdc..ff0db80 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -215,6 +215,21 @@ config NET_PKTGEN
To compile this code as a module, choose M here: the
module will be called pktgen.
+config NET_TCPPROBE
+ tristate "TCP connection probing"
+ depends on INET && EXPERIMENTAL && PROC_FS && KPROBES
+ ---help---
+ This module allows for capturing the changes to TCP connection
+ state in response to incoming patckets. It is used for debugging
+ TCP congestion avoidance modules. If you don't understand
+ what was just said, you don't need it: say N.
+
+ Documentation on how to use the packet generator can be found
+ at http://linux-net.osdl.org/index.php/TcpProbe
+
+ To compile this code as a module, choose M here: the
+ module will be called tcp_probe.
+
endmenu
endmenu
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index e40f753..2032411 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -532,6 +532,38 @@ config TCP_CONG_SCALABLE
properties, though is known to have fairness issues.
See http://www-lce.eng.cam.ac.uk/~ctk21/scalable/
+config TCP_CONG_LP
+ tristate "TCP Low Priority"
+ depends on EXPERIMENTAL
+ default n
+ ---help---
+ TCP Low Priority (TCP-LP), a distributed algorithm whose goal is
+ to utiliza only the excess network bandwidth as compared to the
+ ``fair share`` of bandwidth as targeted by TCP.
+ See http://www-ece.rice.edu/networks/TCP-LP/
+
+config TCP_CONG_VENO
+ tristate "TCP Veno"
+ depends on EXPERIMENTAL
+ default n
+ ---help---
+ TCP Veno is a sender-side only enhancement of TCP to obtain better
+ throughput over wireless networks. TCP Veno makes use of state
+ distinguishing to circumvent the difficult judgment of the packet loss
+ type. TCP Veno cuts down less congestion window in response to random
+ loss packets.
+ See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf
+
+config TCP_CONG_COMPOUND
+ tristate "TCP Compound"
+ depends on EXPERIMENTAL
+ default n
+ ---help---
+ TCP Compound is a sender-side only change to TCP that uses
+ a mixed Reno/Vegas approach to calculate the cwnd.
+ For further details look here:
+ ftp://ftp.research.microsoft.com/pub/tr/TR-2005-86.pdf
+
endmenu
config TCP_CONG_BIC
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 9ef50a0..ac7eb0e 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_IP_VS) += ipvs/
obj-$(CONFIG_INET_DIAG) += inet_diag.o
obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o
obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
+obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o
obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o
@@ -41,7 +42,10 @@ obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_high
obj-$(CONFIG_TCP_CONG_HYBLA) += tcp_hybla.o
obj-$(CONFIG_TCP_CONG_HTCP) += tcp_htcp.o
obj-$(CONFIG_TCP_CONG_VEGAS) += tcp_vegas.o
+obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o
obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
+obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
+obj-$(CONFIG_TCP_CONG_COMPOUND) += tcp_compound.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o
diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c
index 035f209..b2d9021 100644
--- a/net/ipv4/tcp_bic.c
+++ b/net/ipv4/tcp_bic.c
@@ -198,12 +198,6 @@ static u32 bictcp_undo_cwnd(struct sock
return max(tp->snd_cwnd, ca->last_max_cwnd);
}
-static u32 bictcp_min_cwnd(struct sock *sk)
-{
- const struct tcp_sock *tp = tcp_sk(sk);
- return tp->snd_ssthresh;
-}
-
static void bictcp_state(struct sock *sk, u8 new_state)
{
if (new_state == TCP_CA_Loss)
@@ -231,7 +225,6 @@ static struct tcp_congestion_ops bictcp
.cong_avoid = bictcp_cong_avoid,
.set_state = bictcp_state,
.undo_cwnd = bictcp_undo_cwnd,
- .min_cwnd = bictcp_min_cwnd,
.pkts_acked = bictcp_acked,
.owner = THIS_MODULE,
.name = "bic",
diff --git a/net/ipv4/tcp_compound.c b/net/ipv4/tcp_compound.c
new file mode 100644
index 0000000..bc54f7e
--- /dev/null
+++ b/net/ipv4/tcp_compound.c
@@ -0,0 +1,448 @@
+/*
+ * TCP Vegas congestion control
+ *
+ * This is based on the congestion detection/avoidance scheme described in
+ * Lawrence S. Brakmo and Larry L. Peterson.
+ * "TCP Vegas: End to end congestion avoidance on a global internet."
+ * IEEE Journal on Selected Areas in Communication, 13(8):1465--1480,
+ * October 1995. Available from:
+ * ftp://ftp.cs.arizona.edu/xkernel/Papers/jsac.ps
+ *
+ * See http://www.cs.arizona.edu/xkernel/ for their implementation.
+ * The main aspects that distinguish this implementation from the
+ * Arizona Vegas implementation are:
+ * o We do not change the loss detection or recovery mechanisms of
+ * Linux in any way. Linux already recovers from losses quite well,
+ * using fine-grained timers, NewReno, and FACK.
+ * o To avoid the performance penalty imposed by increasing cwnd
+ * only every-other RTT during slow start, we increase during
+ * every RTT during slow start, just like Reno.
+ * o Largely to allow continuous cwnd growth during slow start,
+ * we use the rate at which ACKs come back as the "actual"
+ * rate, rather than the rate at which data is sent.
+ * o To speed convergence to the right rate, we set the cwnd
+ * to achieve the right ("actual") rate when we exit slow start.
+ * o To filter out the noise caused by delayed ACKs, we use the
+ * minimum RTT sample observed during the last RTT to calculate
+ * the actual rate.
+ * o When the sender re-starts from idle, it waits until it has
+ * received ACKs for an entire flight of new data before making
+ * a cwnd adjustment decision. The original Vegas implementation
+ * assumed senders never went idle.
+ *
+ *
+ * TCP Compound based on TCP Vegas
+ *
+ * further details can be found here:
+ * ftp://ftp.research.microsoft.com/pub/tr/TR-2005-86.pdf
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/inet_diag.h>
+
+#include <net/tcp.h>
+
+/* Default values of the Vegas variables, in fixed-point representation
+ * with V_PARAM_SHIFT bits to the right of the binary point.
+ */
+#define V_PARAM_SHIFT 1
+
+#define TCP_COMPOUND_ALPHA 3U
+#define TCP_COMPOUND_BETA 1U
+#define TCP_COMPOUND_GAMMA 30
+#define TCP_COMPOUND_ZETA 1
+
+/* TCP compound variables */
+struct compound {
+ u32 beg_snd_nxt; /* right edge during last RTT */
+ u32 beg_snd_una; /* left edge during last RTT */
+ u32 beg_snd_cwnd; /* saves the size of the cwnd */
+ u8 doing_vegas_now; /* if true, do vegas for this RTT */
+ u16 cntRTT; /* # of RTTs measured within last RTT */
+ u32 minRTT; /* min of RTTs measured within last RTT (in usec) */
+ u32 baseRTT; /* the min of all Vegas RTT measurements seen (in usec) */
+
+ u32 cwnd;
+ u32 dwnd;
+};
+
+/* There are several situations when we must "re-start" Vegas:
+ *
+ * o when a connection is established
+ * o after an RTO
+ * o after fast recovery
+ * o when we send a packet and there is no outstanding
+ * unacknowledged data (restarting an idle connection)
+ *
+ * In these circumstances we cannot do a Vegas calculation at the
+ * end of the first RTT, because any calculation we do is using
+ * stale info -- both the saved cwnd and congestion feedback are
+ * stale.
+ *
+ * Instead we must wait until the completion of an RTT during
+ * which we actually receive ACKs.
+ */
+static inline void vegas_enable(struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct compound *vegas = inet_csk_ca(sk);
+
+ /* Begin taking Vegas samples next time we send something. */
+ vegas->doing_vegas_now = 1;
+
+ /* Set the beginning of the next send window. */
+ vegas->beg_snd_nxt = tp->snd_nxt;
+
+ vegas->cntRTT = 0;
+ vegas->minRTT = 0x7fffffff;
+}
+
+/* Stop taking Vegas samples for now. */
+static inline void vegas_disable(struct sock *sk)
+{
+ struct compound *vegas = inet_csk_ca(sk);
+
+ vegas->doing_vegas_now = 0;
+}
+
+static void tcp_compound_init(struct sock *sk)
+{
+ struct compound *vegas = inet_csk_ca(sk);
+ const struct tcp_sock *tp = tcp_sk(sk);
+
+ vegas->baseRTT = 0x7fffffff;
+ vegas_enable(sk);
+
+ vegas->dwnd = 0;
+ vegas->cwnd = tp->snd_cwnd;
+}
+
+/* Do RTT sampling needed for Vegas.
+ * Basically we:
+ * o min-filter RTT samples from within an RTT to get the current
+ * propagation delay + queuing delay (we are min-filtering to try to
+ * avoid the effects of delayed ACKs)
+ * o min-filter RTT samples from a much longer window (forever for now)
+ * to find the propagation delay (baseRTT)
+ */
+static void tcp_compound_rtt_calc(struct sock *sk, u32 usrtt)
+{
+ struct compound *vegas = inet_csk_ca(sk);
+ u32 vrtt = usrtt + 1; /* Never allow zero rtt or baseRTT */
+
+ /* Filter to find propagation delay: */
+ if (vrtt < vegas->baseRTT)
+ vegas->baseRTT = vrtt;
+
+ /* Find the min RTT during the last RTT to find
+ * the current prop. delay + queuing delay:
+ */
+
+ vegas->minRTT = min(vegas->minRTT, vrtt);
+ vegas->cntRTT++;
+}
+
+static void tcp_compound_state(struct sock *sk, u8 ca_state)
+{
+
+ if (ca_state == TCP_CA_Open)
+ vegas_enable(sk);
+ else
+ vegas_disable(sk);
+}
+
+
+/* 64bit divisor, dividend and result. dynamic precision */
+static inline u64 div64_64(u64 dividend, u64 divisor)
+{
+ u32 d = divisor;
+
+ if (divisor > 0xffffffffULL) {
+ unsigned int shift = fls(divisor >> 32);
+
+ d = divisor >> shift;
+ dividend >>= shift;
+ }
+
+ /* avoid 64 bit division if possible */
+ if (dividend >> 32)
+ do_div(dividend, d);
+ else
+ dividend = (u32) dividend / d;
+
+ return dividend;
+}
+
+/* calculate the quartic root of "a" using Newton-Raphson */
+static u32 qroot(u64 a)
+{
+ u32 x, x1;
+
+ /* Initial estimate is based on:
+ * qrt(x) = exp(log(x) / 4)
+ */
+ x = 1u << (fls64(a) >> 2);
+
+ /*
+ * Iteration based on:
+ * 3
+ * x = ( 3 * x + a / x ) / 4
+ * k+1 k k
+ */
+ do {
+ u64 x3 = x;
+
+ x1 = x;
+ x3 *= x;
+ x3 *= x;
+
+ x = (3 * x + (u32) div64_64(a, x3)) / 4;
+ } while (abs(x1 - x) > 1);
+
+ return x;
+}
+
+
+/*
+ * If the connection is idle and we are restarting,
+ * then we don't want to do any Vegas calculations
+ * until we get fresh RTT samples. So when we
+ * restart, we reset our Vegas state to a clean
+ * slate. After we get acks for this flight of
+ * packets, _then_ we can make Vegas calculations
+ * again.
+ */
+static void tcp_compound_cwnd_event(struct sock *sk, enum tcp_ca_event event)
+{
+ if (event == CA_EVENT_CWND_RESTART || event == CA_EVENT_TX_START)
+ tcp_compound_init(sk);
+}
+
+static void tcp_compound_cong_avoid(struct sock *sk, u32 ack,
+ u32 seq_rtt, u32 in_flight, int flag)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct compound *vegas = inet_csk_ca(sk);
+ u8 inc = 0;
+
+ if (vegas->cwnd + vegas->dwnd > tp->snd_cwnd) {
+ if (vegas->cwnd > tp->snd_cwnd || vegas->dwnd > tp->snd_cwnd) {
+ vegas->cwnd = tp->snd_cwnd;
+ vegas->dwnd = 0;
+ } else
+ vegas->cwnd = tp->snd_cwnd - vegas->dwnd;
+
+ }
+
+ if (!tcp_is_cwnd_limited(sk, in_flight))
+ return;
+
+ if (vegas->cwnd <= tp->snd_ssthresh)
+ inc = 1;
+ else if (tp->snd_cwnd_cnt < tp->snd_cwnd)
+ tp->snd_cwnd_cnt++;
+
+ if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+ inc = 1;
+ tp->snd_cwnd_cnt = 0;
+ }
+
+ if (inc && tp->snd_cwnd < tp->snd_cwnd_clamp)
+ vegas->cwnd++;
+
+ /* The key players are v_beg_snd_una and v_beg_snd_nxt.
+ *
+ * These are so named because they represent the approximate values
+ * of snd_una and snd_nxt at the beginning of the current RTT. More
+ * precisely, they represent the amount of data sent during the RTT.
+ * At the end of the RTT, when we receive an ACK for v_beg_snd_nxt,
+ * we will calculate that (v_beg_snd_nxt - v_beg_snd_una) outstanding
+ * bytes of data have been ACKed during the course of the RTT, giving
+ * an "actual" rate of:
+ *
+ * (v_beg_snd_nxt - v_beg_snd_una) / (rtt duration)
+ *
+ * Unfortunately, v_beg_snd_una is not exactly equal to snd_una,
+ * because delayed ACKs can cover more than one segment, so they
+ * don't line up nicely with the boundaries of RTTs.
+ *
+ * Another unfortunate fact of life is that delayed ACKs delay the
+ * advance of the left edge of our send window, so that the number
+ * of bytes we send in an RTT is often less than our cwnd will allow.
+ * So we keep track of our cwnd separately, in v_beg_snd_cwnd.
+ */
+
+ if (after(ack, vegas->beg_snd_nxt)) {
+ /* Do the Vegas once-per-RTT cwnd adjustment. */
+ u32 old_wnd, old_snd_cwnd;
+
+ /* Here old_wnd is essentially the window of data that was
+ * sent during the previous RTT, and has all
+ * been acknowledged in the course of the RTT that ended
+ * with the ACK we just received. Likewise, old_snd_cwnd
+ * is the cwnd during the previous RTT.
+ */
+ if (!tp->mss_cache)
+ return;
+
+ old_wnd = (vegas->beg_snd_nxt - vegas->beg_snd_una) /
+ tp->mss_cache;
+ old_snd_cwnd = vegas->beg_snd_cwnd;
+
+ /* Save the extent of the current window so we can use this
+ * at the end of the next RTT.
+ */
+ vegas->beg_snd_una = vegas->beg_snd_nxt;
+ vegas->beg_snd_nxt = tp->snd_nxt;
+ vegas->beg_snd_cwnd = tp->snd_cwnd;
+
+ /* We do the Vegas calculations only if we got enough RTT
+ * samples that we can be reasonably sure that we got
+ * at least one RTT sample that wasn't from a delayed ACK.
+ * If we only had 2 samples total,
+ * then that means we're getting only 1 ACK per RTT, which
+ * means they're almost certainly delayed ACKs.
+ * If we have 3 samples, we should be OK.
+ */
+
+ if (vegas->cntRTT > 2) {
+ u32 rtt, target_cwnd, diff;
+ u32 brtt, dwnd;
+
+ /* We have enough RTT samples, so, using the Vegas
+ * algorithm, we determine if we should increase or
+ * decrease cwnd, and by how much.
+ */
+
+ /* Pluck out the RTT we are using for the Vegas
+ * calculations. This is the min RTT seen during the
+ * last RTT. Taking the min filters out the effects
+ * of delayed ACKs, at the cost of noticing congestion
+ * a bit later.
+ */
+ rtt = vegas->minRTT;
+
+ /* Calculate the cwnd we should have, if we weren't
+ * going too fast.
+ *
+ * This is:
+ * (actual rate in segments) * baseRTT
+ * We keep it as a fixed point number with
+ * V_PARAM_SHIFT bits to the right of the binary point.
+ */
+ if (!rtt)
+ return;
+
+ brtt = vegas->baseRTT;
+ target_cwnd = ((old_wnd * brtt)
+ << V_PARAM_SHIFT) / rtt;
+
+ /* Calculate the difference between the window we had,
+ * and the window we would like to have. This quantity
+ * is the "Diff" from the Arizona Vegas papers.
+ *
+ * Again, this is a fixed point number with
+ * V_PARAM_SHIFT bits to the right of the binary
+ * point.
+ */
+
+ diff = (old_wnd << V_PARAM_SHIFT) - target_cwnd;
+
+ dwnd = vegas->dwnd;
+
+ if (diff < (TCP_COMPOUND_GAMMA << V_PARAM_SHIFT)) {
+ u64 v;
+ u32 x;
+
+ /*
+ * The TCP Compound paper describes the choice
+ * of "k" determines the agressiveness,
+ * ie. slope of the response function.
+ *
+ * For same value as HSTCP would be 0.8
+ * but for computaional reasons, both the
+ * original authors and this implementation
+ * use 0.75.
+ */
+ v = old_wnd;
+ x = qroot(v * v * v) >> TCP_COMPOUND_ALPHA;
+ if (x > 1)
+ dwnd = x - 1;
+ else
+ dwnd = 0;
+
+ dwnd += vegas->dwnd;
+
+ } else if ((dwnd << V_PARAM_SHIFT) <
+ (diff * TCP_COMPOUND_BETA))
+ dwnd = 0;
+ else
+ dwnd =
+ ((dwnd << V_PARAM_SHIFT) -
+ (diff *
+ TCP_COMPOUND_BETA)) >> V_PARAM_SHIFT;
+
+ vegas->dwnd = dwnd;
+
+ }
+
+ /* Wipe the slate clean for the next RTT. */
+ vegas->cntRTT = 0;
+ vegas->minRTT = 0x7fffffff;
+ }
+
+ tp->snd_cwnd = vegas->cwnd + vegas->dwnd;
+}
+
+/* Extract info for Tcp socket info provided via netlink. */
+static void tcp_compound_get_info(struct sock *sk, u32 ext, struct sk_buff *skb)
+{
+ const struct compound *ca = inet_csk_ca(sk);
+ if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
+ struct tcpvegas_info *info;
+
+ info = RTA_DATA(__RTA_PUT(skb, INET_DIAG_VEGASINFO,
+ sizeof(*info)));
+
+ info->tcpv_enabled = ca->doing_vegas_now;
+ info->tcpv_rttcnt = ca->cntRTT;
+ info->tcpv_rtt = ca->baseRTT;
+ info->tcpv_minrtt = ca->minRTT;
+ rtattr_failure:;
+ }
+}
+
+static struct tcp_congestion_ops tcp_compound = {
+ .init = tcp_compound_init,
+ .ssthresh = tcp_reno_ssthresh,
+ .cong_avoid = tcp_compound_cong_avoid,
+ .rtt_sample = tcp_compound_rtt_calc,
+ .set_state = tcp_compound_state,
+ .cwnd_event = tcp_compound_cwnd_event,
+ .get_info = tcp_compound_get_info,
+
+ .owner = THIS_MODULE,
+ .name = "compound",
+};
+
+static int __init tcp_compound_register(void)
+{
+ BUG_ON(sizeof(struct compound) > ICSK_CA_PRIV_SIZE);
+ tcp_register_congestion_control(&tcp_compound);
+ return 0;
+}
+
+static void __exit tcp_compound_unregister(void)
+{
+ tcp_unregister_congestion_control(&tcp_compound);
+}
+
+module_init(tcp_compound_register);
+module_exit(tcp_compound_unregister);
+
+MODULE_AUTHOR("Angelo P. Castellani, Stephen Hemminger");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TCP Compound");
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 91c2f41..857eefc 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -38,7 +38,7 @@ int tcp_register_congestion_control(stru
int ret = 0;
/* all algorithms must implement ssthresh and cong_avoid ops */
- if (!ca->ssthresh || !ca->cong_avoid || !ca->min_cwnd) {
+ if (!ca->ssthresh || !ca->cong_avoid) {
printk(KERN_ERR "TCP %s does not implement required ops\n",
ca->name);
return -EINVAL;
@@ -251,8 +251,8 @@ u32 tcp_reno_ssthresh(struct sock *sk)
}
EXPORT_SYMBOL_GPL(tcp_reno_ssthresh);
-/* Lower bound on congestion window. */
-u32 tcp_reno_min_cwnd(struct sock *sk)
+/* Lower bound on congestion window with halving. */
+u32 tcp_reno_min_cwnd(const struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
return tp->snd_ssthresh/2;
diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c
index 31a4986..78b7a6b 100644
--- a/net/ipv4/tcp_cubic.c
+++ b/net/ipv4/tcp_cubic.c
@@ -325,11 +325,6 @@ static u32 bictcp_undo_cwnd(struct sock
return max(tcp_sk(sk)->snd_cwnd, ca->last_max_cwnd);
}
-static u32 bictcp_min_cwnd(struct sock *sk)
-{
- return tcp_sk(sk)->snd_ssthresh;
-}
-
static void bictcp_state(struct sock *sk, u8 new_state)
{
if (new_state == TCP_CA_Loss)
@@ -357,7 +352,6 @@ static struct tcp_congestion_ops cubictc
.cong_avoid = bictcp_cong_avoid,
.set_state = bictcp_state,
.undo_cwnd = bictcp_undo_cwnd,
- .min_cwnd = bictcp_min_cwnd,
.pkts_acked = bictcp_acked,
.owner = THIS_MODULE,
.name = "cubic",
diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c
index 1b2ff53..3d92c18 100644
--- a/net/ipv4/tcp_htcp.c
+++ b/net/ipv4/tcp_htcp.c
@@ -246,14 +246,6 @@ static void htcp_cong_avoid(struct sock
}
}
-/* Lower bound on congestion window. */
-static u32 htcp_min_cwnd(struct sock *sk)
-{
- const struct tcp_sock *tp = tcp_sk(sk);
- return tp->snd_ssthresh;
-}
-
-
static void htcp_init(struct sock *sk)
{
struct htcp *ca = inet_csk_ca(sk);
@@ -285,7 +277,6 @@ static void htcp_state(struct sock *sk,
static struct tcp_congestion_ops htcp = {
.init = htcp_init,
.ssthresh = htcp_recalc_ssthresh,
- .min_cwnd = htcp_min_cwnd,
.cong_avoid = htcp_cong_avoid,
.set_state = htcp_state,
.undo_cwnd = htcp_cwnd_undo,
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 4a538bc..718d0f2 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1690,17 +1690,26 @@ static inline void tcp_moderate_cwnd(str
tp->snd_cwnd_stamp = tcp_time_stamp;
}
+/* Lower bound on congestion window is slow start threshold
+ * unless congestion avoidance choice decides to overide it.
+ */
+static inline u32 tcp_cwnd_min(const struct sock *sk)
+{
+ const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
+
+ return ca_ops->min_cwnd ? ca_ops->min_cwnd(sk) : tcp_sk(sk)->snd_ssthresh;
+}
+
/* Decrease cwnd each second ack. */
static void tcp_cwnd_down(struct sock *sk)
{
- const struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
int decr = tp->snd_cwnd_cnt + 1;
tp->snd_cwnd_cnt = decr&1;
decr >>= 1;
- if (decr && tp->snd_cwnd > icsk->icsk_ca_ops->min_cwnd(sk))
+ if (decr && tp->snd_cwnd > tcp_cwnd_min(sk))
tp->snd_cwnd -= decr;
tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)+1);
diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c
new file mode 100644
index 0000000..1f977b6
--- /dev/null
+++ b/net/ipv4/tcp_lp.c
@@ -0,0 +1,338 @@
+/*
+ * TCP Low Priority (TCP-LP)
+ *
+ * TCP Low Priority is a distributed algorithm whose goal is to utilize only
+ * the excess network bandwidth as compared to the ``fair share`` of
+ * bandwidth as targeted by TCP. Available from:
+ * http://www.ece.rice.edu/~akuzma/Doc/akuzma/TCP-LP.pdf
+ *
+ * Original Author:
+ * Aleksandar Kuzmanovic <akuzma@northwestern.edu>
+ *
+ * See http://www-ece.rice.edu/networks/TCP-LP/ for their implementation.
+ * As of 2.6.13, Linux supports pluggable congestion control algorithms.
+ * Due to the limitation of the API, we take the following changes from
+ * the original TCP-LP implementation:
+ * o We use newReno in most core CA handling. Only add some checking
+ * within cong_avoid.
+ * o Error correcting in remote HZ, therefore remote HZ will be keeped
+ * on checking and updating.
+ * o Handling calculation of One-Way-Delay (OWD) within rtt_sample, sicne
+ * OWD have a similar meaning as RTT. Also correct the buggy formular.
+ * o Handle reaction for Early Congestion Indication (ECI) within
+ * pkts_acked, as mentioned within pseudo code.
+ * o OWD is handled in relative format, where local time stamp will in
+ * tcp_time_stamp format.
+ *
+ * Port from 2.4.19 to 2.6.16 as module by:
+ * Wong Hoi Sing Edison <hswong3i@gmail.com>
+ * Hung Hing Lun <hlhung3i@gmail.com>
+ *
+ * Version: $Id: tcp_lp.c,v 1.22 2006-05-02 18:18:19 hswong3i Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <net/tcp.h>
+
+/* resolution of owd */
+#define LP_RESOL 1000
+
+/**
+ * enum tcp_lp_state
+ * @LP_VALID_RHZ: is remote HZ valid?
+ * @LP_VALID_OWD: is OWD valid?
+ * @LP_WITHIN_THR: are we within threshold?
+ * @LP_WITHIN_INF: are we within inference?
+ *
+ * TCP-LP's state flags.
+ * We create this set of state flag mainly for debugging.
+ */
+enum tcp_lp_state {
+ LP_VALID_RHZ = (1 << 0),
+ LP_VALID_OWD = (1 << 1),
+ LP_WITHIN_THR = (1 << 3),
+ LP_WITHIN_INF = (1 << 4),
+};
+
+/**
+ * struct lp
+ * @flag: TCP-LP state flag
+ * @sowd: smoothed OWD << 3
+ * @owd_min: min OWD
+ * @owd_max: max OWD
+ * @owd_max_rsv: resrved max owd
+ * @remote_hz: estimated remote HZ
+ * @remote_ref_time: remote reference time
+ * @local_ref_time: local reference time
+ * @last_drop: time for last active drop
+ * @inference: current inference
+ *
+ * TCP-LP's private struct.
+ * We get the idea from original TCP-LP implementation where only left those we
+ * found are really useful.
+ */
+struct lp {
+ u32 flag;
+ u32 sowd;
+ u32 owd_min;
+ u32 owd_max;
+ u32 owd_max_rsv;
+ u32 remote_hz;
+ u32 remote_ref_time;
+ u32 local_ref_time;
+ u32 last_drop;
+ u32 inference;
+};
+
+/**
+ * tcp_lp_init
+ *
+ * Init all required variables.
+ * Clone the handling from Vegas module implementation.
+ */
+static void tcp_lp_init(struct sock *sk)
+{
+ struct lp *lp = inet_csk_ca(sk);
+
+ lp->flag = 0;
+ lp->sowd = 0;
+ lp->owd_min = 0xffffffff;
+ lp->owd_max = 0;
+ lp->owd_max_rsv = 0;
+ lp->remote_hz = 0;
+ lp->remote_ref_time = 0;
+ lp->local_ref_time = 0;
+ lp->last_drop = 0;
+ lp->inference = 0;
+}
+
+/**
+ * tcp_lp_cong_avoid
+ *
+ * Implementation of cong_avoid.
+ * Will only call newReno CA when away from inference.
+ * From TCP-LP's paper, this will be handled in additive increasement.
+ */
+static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight,
+ int flag)
+{
+ struct lp *lp = inet_csk_ca(sk);
+
+ if (!(lp->flag & LP_WITHIN_INF))
+ tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag);
+}
+
+/**
+ * tcp_lp_remote_hz_estimator
+ *
+ * Estimate remote HZ.
+ * We keep on updating the estimated value, where original TCP-LP
+ * implementation only guest it for once and use forever.
+ */
+static u32 tcp_lp_remote_hz_estimator(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct lp *lp = inet_csk_ca(sk);
+ s64 rhz = lp->remote_hz << 6; /* remote HZ << 6 */
+ s64 m = 0;
+
+ /* not yet record reference time
+ * go away!! record it before come back!! */
+ if (lp->remote_ref_time == 0 || lp->local_ref_time == 0)
+ goto out;
+
+ /* we can't calc remote HZ with no different!! */
+ if (tp->rx_opt.rcv_tsval == lp->remote_ref_time
+ || tp->rx_opt.rcv_tsecr == lp->local_ref_time)
+ goto out;
+
+ m = HZ * (tp->rx_opt.rcv_tsval -
+ lp->remote_ref_time) / (tp->rx_opt.rcv_tsecr -
+ lp->local_ref_time);
+ if (m < 0)
+ m = -m;
+
+ if (rhz != 0) {
+ m -= rhz >> 6; /* m is now error in remote HZ est */
+ rhz += m; /* 63/64 old + 1/64 new */
+ } else
+ rhz = m << 6;
+
+ /* record time for successful remote HZ calc */
+ lp->flag |= LP_VALID_RHZ;
+
+ out:
+ /* record reference time stamp */
+ lp->remote_ref_time = tp->rx_opt.rcv_tsval;
+ lp->local_ref_time = tp->rx_opt.rcv_tsecr;
+
+ return rhz >> 6;
+}
+
+/**
+ * tcp_lp_owd_calculator
+ *
+ * Calculate one way delay (in relative format).
+ * Original implement OWD as minus of remote time difference to local time
+ * difference directly. As this time difference just simply equal to RTT, when
+ * the network status is stable, remote RTT will equal to local RTT, and result
+ * OWD into zero.
+ * It seems to be a bug and so we fixed it.
+ */
+static u32 tcp_lp_owd_calculator(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct lp *lp = inet_csk_ca(sk);
+ s64 owd = 0;
+
+ lp->remote_hz = tcp_lp_remote_hz_estimator(sk);
+
+ if (lp->flag & LP_VALID_RHZ) {
+ owd =
+ tp->rx_opt.rcv_tsval * (LP_RESOL / lp->remote_hz) -
+ tp->rx_opt.rcv_tsecr * (LP_RESOL / HZ);
+ if (owd < 0)
+ owd = -owd;
+ }
+
+ if (owd > 0)
+ lp->flag |= LP_VALID_OWD;
+ else
+ lp->flag &= ~LP_VALID_OWD;
+
+ return owd;
+}
+
+/**
+ * tcp_lp_rtt_sample
+ *
+ * Implementation or rtt_sample.
+ * Will take the following action,
+ * 1. calc OWD,
+ * 2. record the min/max OWD,
+ * 3. calc smoothed OWD (SOWD).
+ * Most ideas come from the original TCP-LP implementation.
+ */
+static void tcp_lp_rtt_sample(struct sock *sk, u32 usrtt)
+{
+ struct lp *lp = inet_csk_ca(sk);
+ s64 mowd = tcp_lp_owd_calculator(sk);
+
+ /* sorry that we don't have valid data */
+ if (!(lp->flag & LP_VALID_RHZ) || !(lp->flag & LP_VALID_OWD))
+ return;
+
+ /* record the next min owd */
+ if (mowd < lp->owd_min)
+ lp->owd_min = mowd;
+
+ /* always forget the max of the max
+ * we just set owd_max as one below it */
+ if (mowd > lp->owd_max) {
+ if (mowd > lp->owd_max_rsv) {
+ if (lp->owd_max_rsv == 0)
+ lp->owd_max = mowd;
+ else
+ lp->owd_max = lp->owd_max_rsv;
+ lp->owd_max_rsv = mowd;
+ } else
+ lp->owd_max = mowd;
+ }
+
+ /* calc for smoothed owd */
+ if (lp->sowd != 0) {
+ mowd -= lp->sowd >> 3; /* m is now error in owd est */
+ lp->sowd += mowd; /* owd = 7/8 owd + 1/8 new */
+ } else
+ lp->sowd = mowd << 3; /* take the measured time be owd */
+}
+
+/**
+ * tcp_lp_pkts_acked
+ *
+ * Implementation of pkts_acked.
+ * Deal with active drop under Early Congestion Indication.
+ * Only drop to half and 1 will be handle, because we hope to use back
+ * newReno in increase case.
+ * We work it out by following the idea from TCP-LP's paper directly
+ */
+static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct lp *lp = inet_csk_ca(sk);
+
+ /* calc inference */
+ if (tcp_time_stamp > tp->rx_opt.rcv_tsecr)
+ lp->inference = 3 * (tcp_time_stamp - tp->rx_opt.rcv_tsecr);
+
+ /* test if within inference */
+ if (lp->last_drop && (tcp_time_stamp - lp->last_drop < lp->inference))
+ lp->flag |= LP_WITHIN_INF;
+ else
+ lp->flag &= ~LP_WITHIN_INF;
+
+ /* test if within threshold */
+ if (lp->sowd >> 3 <
+ lp->owd_min + 15 * (lp->owd_max - lp->owd_min) / 100)
+ lp->flag |= LP_WITHIN_THR;
+ else
+ lp->flag &= ~LP_WITHIN_THR;
+
+ pr_debug("TCP-LP: %05o|%5u|%5u|%15u|%15u|%15u\n", lp->flag,
+ tp->snd_cwnd, lp->remote_hz, lp->owd_min, lp->owd_max,
+ lp->sowd >> 3);
+
+ if (lp->flag & LP_WITHIN_THR)
+ return;
+
+ /* FIXME: try to reset owd_min and owd_max here
+ * so decrease the chance the min/max is no longer suitable
+ * and will usually within threshold when whithin inference */
+ lp->owd_min = lp->sowd >> 3;
+ lp->owd_max = lp->sowd >> 2;
+ lp->owd_max_rsv = lp->sowd >> 2;
+
+ /* happened within inference
+ * drop snd_cwnd into 1 */
+ if (lp->flag & LP_WITHIN_INF)
+ tp->snd_cwnd = 1U;
+
+ /* happened after inference
+ * cut snd_cwnd into half */
+ else
+ tp->snd_cwnd = max(tp->snd_cwnd >> 1U, 1U);
+
+ /* record this drop time */
+ lp->last_drop = tcp_time_stamp;
+}
+
+static struct tcp_congestion_ops tcp_lp = {
+ .init = tcp_lp_init,
+ .ssthresh = tcp_reno_ssthresh,
+ .cong_avoid = tcp_lp_cong_avoid,
+ .min_cwnd = tcp_reno_min_cwnd,
+ .rtt_sample = tcp_lp_rtt_sample,
+ .pkts_acked = tcp_lp_pkts_acked,
+
+ .owner = THIS_MODULE,
+ .name = "lp"
+};
+
+static int __init tcp_lp_register(void)
+{
+ BUG_ON(sizeof(struct lp) > ICSK_CA_PRIV_SIZE);
+ return tcp_register_congestion_control(&tcp_lp);
+}
+
+static void __exit tcp_lp_unregister(void)
+{
+ tcp_unregister_congestion_control(&tcp_lp);
+}
+
+module_init(tcp_lp_register);
+module_exit(tcp_lp_unregister);
+
+MODULE_AUTHOR("Wong Hoi Sing Edison, Hung Hing Lun");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TCP Low Priority");
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
new file mode 100644
index 0000000..0688e5f
--- /dev/null
+++ b/net/ipv4/tcp_probe.c
@@ -0,0 +1,179 @@
+/*
+ * tcpprobe - Observe the TCP flow with kprobes.
+ *
+ * The idea for this came from Werner Almesberger's umlsim
+ * Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/socket.h>
+#include <linux/tcp.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/kfifo.h>
+#include <linux/vmalloc.h>
+
+#include <net/tcp.h>
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
+MODULE_DESCRIPTION("TCP cwnd snooper");
+MODULE_LICENSE("GPL");
+
+static int port = 0;
+MODULE_PARM_DESC(port, "Port to match (0=all)");
+module_param(port, int, 0);
+
+static int bufsize = 64*1024;
+MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)");
+module_param(bufsize, int, 0);
+
+static const char procname[] = "tcpprobe";
+
+struct {
+ struct kfifo *fifo;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ struct timeval tstart;
+} tcpw;
+
+static void printl(const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ struct timeval now;
+ char tbuf[256];
+
+ va_start(args, fmt);
+ do_gettimeofday(&now);
+
+ now.tv_sec -= tcpw.tstart.tv_sec;
+ now.tv_usec -= tcpw.tstart.tv_usec;
+ if (now.tv_usec < 0) {
+ --now.tv_sec;
+ now.tv_usec += 1000000;
+ }
+
+ len = sprintf(tbuf, "%lu.%06lu ", now.tv_sec, now.tv_usec);
+ len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args);
+ va_end(args);
+
+ kfifo_put(tcpw.fifo, tbuf, len);
+ wake_up(&tcpw.wait);
+}
+
+static int jtcp_sendmsg(struct kiocb *iocb, struct sock *sk,
+ struct msghdr *msg, size_t size)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ const struct inet_sock *inet = inet_sk(sk);
+
+ if (port == 0 || ntohs(inet->dport) == port ||
+ ntohs(inet->sport) == port) {
+ printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n",
+ NIPQUAD(inet->saddr), ntohs(inet->sport),
+ NIPQUAD(inet->daddr), ntohs(inet->dport),
+ size, tp->snd_nxt, tp->snd_una,
+ tp->snd_cwnd, tcp_current_ssthresh(sk),
+ tp->snd_wnd);
+ }
+
+ jprobe_return();
+ return 0;
+}
+
+static struct jprobe tcp_send_probe = {
+ .kp = { .addr = (kprobe_opcode_t *) &tcp_sendmsg, },
+ .entry = (kprobe_opcode_t *) &jtcp_sendmsg,
+};
+
+
+static int tcpprobe_open(struct inode * inode, struct file * file)
+{
+ kfifo_reset(tcpw.fifo);
+ do_gettimeofday(&tcpw.tstart);
+ return 0;
+}
+
+static ssize_t tcpprobe_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ int error = 0, cnt;
+ unsigned char *tbuf;
+
+ if (!buf || len < 0)
+ return -EINVAL;
+
+ if (len == 0)
+ return 0;
+
+ tbuf = vmalloc(len);
+ if (!tbuf)
+ return -ENOMEM;
+
+ error = wait_event_interruptible(tcpw.wait,
+ __kfifo_len(tcpw.fifo) != 0);
+ if (error)
+ return error;
+
+ cnt = kfifo_get(tcpw.fifo, tbuf, len);
+ error = copy_to_user(buf, tbuf, cnt);
+
+ vfree(tbuf);
+
+ return error ? error : cnt;
+}
+
+static struct file_operations tcpprobe_fops = {
+ .owner = THIS_MODULE,
+ .open = tcpprobe_open,
+ .read = tcpprobe_read,
+};
+
+static __init int tcpprobe_init(void)
+{
+ int ret = -ENOMEM;
+
+ init_waitqueue_head(&tcpw.wait);
+ spin_lock_init(&tcpw.lock);
+ tcpw.fifo = kfifo_alloc(bufsize, GFP_KERNEL, &tcpw.lock);
+
+ if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops))
+ goto err0;
+
+ ret = register_jprobe(&tcp_send_probe);
+ if (ret)
+ goto err1;
+
+ pr_info("TCP watch registered (port=%d)\n", port);
+ return 0;
+ err1:
+ proc_net_remove(procname);
+ err0:
+ kfifo_free(tcpw.fifo);
+ return ret;
+}
+module_init(tcpprobe_init);
+
+static __exit void tcpprobe_exit(void)
+{
+ kfifo_free(tcpw.fifo);
+ proc_net_remove(procname);
+ unregister_jprobe(&tcp_send_probe);
+
+}
+module_exit(tcpprobe_exit);
diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c
new file mode 100644
index 0000000..11b42a7
--- /dev/null
+++ b/net/ipv4/tcp_veno.c
@@ -0,0 +1,231 @@
+/*
+ * TCP Veno congestion control
+ *
+ * This is based on the congestion detection/avoidance scheme described in
+ * C. P. Fu, S. C. Liew.
+ * "TCP Veno: TCP Enhancement for Transmission over Wireless Access Networks."
+ * IEEE Journal on Selected Areas in Communication,
+ * Feb. 2003.
+ * See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/inet_diag.h>
+
+#include <net/tcp.h>
+
+/* Default values of the Veno variables, in fixed-point representation
+ * with V_PARAM_SHIFT bits to the right of the binary point.
+ */
+#define V_PARAM_SHIFT 1
+static const int beta = 3 << V_PARAM_SHIFT;
+
+/* Veno variables */
+struct veno {
+ u8 doing_veno_now; /* if true, do veno for this rtt */
+ u16 cntrtt; /* # of rtts measured within last rtt */
+ u32 minrtt; /* min of rtts measured within last rtt (in usec) */
+ u32 basertt; /* the min of all Veno rtt measurements seen (in usec) */
+ u32 inc; /* decide whether to increase cwnd */
+ u32 diff; /* calculate the diff rate */
+};
+
+/* There are several situations when we must "re-start" Veno:
+ *
+ * o when a connection is established
+ * o after an RTO
+ * o after fast recovery
+ * o when we send a packet and there is no outstanding
+ * unacknowledged data (restarting an idle connection)
+ *
+ */
+static inline void veno_enable(struct sock *sk)
+{
+ struct veno *veno = inet_csk_ca(sk);
+
+ /* turn on Veno */
+ veno->doing_veno_now = 1;
+
+ veno->minrtt = 0x7fffffff;
+}
+
+static inline void veno_disable(struct sock *sk)
+{
+ struct veno *veno = inet_csk_ca(sk);
+
+ /* turn off Veno */
+ veno->doing_veno_now = 0;
+}
+
+static void tcp_veno_init(struct sock *sk)
+{
+ struct veno *veno = inet_csk_ca(sk);
+
+ veno->basertt = 0x7fffffff;
+ veno->inc = 1;
+ veno_enable(sk);
+}
+
+/* Do rtt sampling needed for Veno. */
+static void tcp_veno_rtt_calc(struct sock *sk, u32 usrtt)
+{
+ struct veno *veno = inet_csk_ca(sk);
+ u32 vrtt = usrtt + 1; /* Never allow zero rtt or basertt */
+
+ /* Filter to find propagation delay: */
+ if (vrtt < veno->basertt)
+ veno->basertt = vrtt;
+
+ /* Find the min rtt during the last rtt to find
+ * the current prop. delay + queuing delay:
+ */
+ veno->minrtt = min(veno->minrtt, vrtt);
+ veno->cntrtt++;
+}
+
+static void tcp_veno_state(struct sock *sk, u8 ca_state)
+{
+ if (ca_state == TCP_CA_Open)
+ veno_enable(sk);
+ else
+ veno_disable(sk);
+}
+
+/*
+ * If the connection is idle and we are restarting,
+ * then we don't want to do any Veno calculations
+ * until we get fresh rtt samples. So when we
+ * restart, we reset our Veno state to a clean
+ * state. After we get acks for this flight of
+ * packets, _then_ we can make Veno calculations
+ * again.
+ */
+static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event)
+{
+ if (event == CA_EVENT_CWND_RESTART || event == CA_EVENT_TX_START)
+ tcp_veno_init(sk);
+}
+
+static void tcp_veno_cong_avoid(struct sock *sk, u32 ack,
+ u32 seq_rtt, u32 in_flight, int flag)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct veno *veno = inet_csk_ca(sk);
+
+ if (!veno->doing_veno_now)
+ return tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag);
+
+ /* limited by applications */
+ if (!tcp_is_cwnd_limited(sk, in_flight))
+ return;
+
+ /* We do the Veno calculations only if we got enough rtt samples */
+ if (veno->cntrtt <= 2) {
+ /* We don't have enough rtt samples to do the Veno
+ * calculation, so we'll behave like Reno.
+ */
+ tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag);
+ } else {
+ u32 rtt, target_cwnd;
+
+ /* We have enough rtt samples, so, using the Veno
+ * algorithm, we determine the state of the network.
+ */
+
+ rtt = veno->minrtt;
+
+ target_cwnd = ((tp->snd_cwnd * veno->basertt)
+ << V_PARAM_SHIFT) / rtt;
+
+ veno->diff = (tp->snd_cwnd << V_PARAM_SHIFT) - target_cwnd;
+
+ if (tp->snd_cwnd <= tp->snd_ssthresh) {
+ /* Slow start. */
+ tcp_slow_start(tp);
+ } else {
+ /* Congestion avoidance. */
+ if (veno->diff < beta) {
+ /* In the "non-congestive state", increase cwnd
+ * every rtt.
+ */
+ if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+ if (tp->snd_cwnd < tp->snd_cwnd_clamp)
+ tp->snd_cwnd++;
+ tp->snd_cwnd_cnt = 0;
+ } else
+ tp->snd_cwnd_cnt++;
+ } else {
+ /* In the "congestive state", increase cwnd
+ * every other rtt.
+ */
+ if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+ if (veno->inc
+ && tp->snd_cwnd <
+ tp->snd_cwnd_clamp) {
+ tp->snd_cwnd++;
+ veno->inc = 0;
+ } else
+ veno->inc = 1;
+ tp->snd_cwnd_cnt = 0;
+ } else
+ tp->snd_cwnd_cnt++;
+ }
+
+ }
+ if (tp->snd_cwnd < 2)
+ tp->snd_cwnd = 2;
+ else if (tp->snd_cwnd > tp->snd_cwnd_clamp)
+ tp->snd_cwnd = tp->snd_cwnd_clamp;
+ }
+ /* Wipe the slate clean for the next rtt. */
+ /* veno->cntrtt = 0; */
+ veno->minrtt = 0x7fffffff;
+}
+
+/* Veno MD phase */
+static u32 tcp_veno_ssthresh(struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct veno *veno = inet_csk_ca(sk);
+
+ if (veno->diff < beta)
+ /* in "non-congestive state", cut cwnd by 1/5 */
+ return max(tp->snd_cwnd * 4 / 5, 2U);
+ else
+ /* in "congestive state", cut cwnd by 1/2 */
+ return max(tp->snd_cwnd >> 1U, 2U);
+}
+
+static struct tcp_congestion_ops tcp_veno = {
+ .init = tcp_veno_init,
+ .ssthresh = tcp_veno_ssthresh,
+ .cong_avoid = tcp_veno_cong_avoid,
+ .rtt_sample = tcp_veno_rtt_calc,
+ .set_state = tcp_veno_state,
+ .cwnd_event = tcp_veno_cwnd_event,
+
+ .owner = THIS_MODULE,
+ .name = "veno",
+};
+
+static int __init tcp_veno_register(void)
+{
+ BUG_ON(sizeof(struct veno) > ICSK_CA_PRIV_SIZE);
+ tcp_register_congestion_control(&tcp_veno);
+ return 0;
+}
+
+static void __exit tcp_veno_unregister(void)
+{
+ tcp_unregister_congestion_control(&tcp_veno);
+}
+
+module_init(tcp_veno_register);
+module_exit(tcp_veno_unregister);
+
+MODULE_AUTHOR("Bin Zhou, Cheng Peng Fu");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TCP Veno");
diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c
index 0c340c3..29eb258 100644
--- a/net/ipv4/tcp_westwood.c
+++ b/net/ipv4/tcp_westwood.c
@@ -162,12 +162,6 @@ static inline u32 westwood_acked_count(s
return w->cumul_ack;
}
-static inline u32 westwood_bw_rttmin(const struct sock *sk)
-{
- const struct tcp_sock *tp = tcp_sk(sk);
- const struct westwood *w = inet_csk_ca(sk);
- return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2);
-}
/*
* TCP Westwood
@@ -175,9 +169,11 @@ static inline u32 westwood_bw_rttmin(con
* in packets we use mss_cache). Rttmin is guaranteed to be >= 2
* so avoids ever returning 0.
*/
-static u32 tcp_westwood_cwnd_min(struct sock *sk)
+static u32 tcp_westwood_bw_rttmin(const struct sock *sk)
{
- return westwood_bw_rttmin(sk);
+ const struct tcp_sock *tp = tcp_sk(sk);
+ const struct westwood *w = inet_csk_ca(sk);
+ return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2);
}
static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event)
@@ -191,11 +187,11 @@ static void tcp_westwood_event(struct so
break;
case CA_EVENT_COMPLETE_CWR:
- tp->snd_cwnd = tp->snd_ssthresh = westwood_bw_rttmin(sk);
+ tp->snd_cwnd = tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
break;
case CA_EVENT_FRTO:
- tp->snd_ssthresh = westwood_bw_rttmin(sk);
+ tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
break;
case CA_EVENT_SLOW_ACK:
@@ -235,7 +231,7 @@ static struct tcp_congestion_ops tcp_wes
.init = tcp_westwood_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
- .min_cwnd = tcp_westwood_cwnd_min,
+ .min_cwnd = tcp_westwood_bw_rttmin,
.cwnd_event = tcp_westwood_event,
.get_info = tcp_westwood_info,
.pkts_acked = tcp_westwood_pkts_acked,
^ permalink raw reply related
* llc use-after-free
From: Andrew Morton @ 2006-05-30 22:41 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: netdev
My test box running rc5-mm1 was kinda sitting there doing nothing when it
decided to have an oops in the llc code. See
http://www.zip.com.au/~akpm/linux/patches/stuff/dsc03542.jpg
gdb says:
0x197 is in llc_rcv (net/llc/llc_input.c:68).
63 static __inline__ int llc_pdu_type(struct sk_buff *skb)
64 {
65 int type = LLC_DEST_CONN; /* I-PDU or S-PDU type */
66 struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
67
68 if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) != LLC_PDU_TYPE_U)
69 goto out;
70 switch (LLC_U_PDU_CMD(pdu)) {
71 case LLC_1_PDU_CMD_XID:
72 case LLC_1_PDU_CMD_UI:
^ permalink raw reply
* [PATCH-2.4] forcedeth update to 0.50
From: Willy Tarreau @ 2006-05-30 22:03 UTC (permalink / raw)
To: Manfred Spraul; +Cc: marcelo, linux-kernel, netdev
[-- Attachment #1: Type: text/plain, Size: 2133 bytes --]
Hi Manfred,
Today I had an opportunity to perform some functional and performance
tests on a SunFire X2100, which is a PCI Express-based Dual Core Opteron
equipped with a broadcom gigabit LAN chip (tg3) and an Nforce4 Pro
chipset offering a second LAN port (forcedeth).
With the forcedeth driver version 0.30 as shipped in 2.4.33-pre*, ping
was OK, but the driver hanged after a few megabytes of Gigabit-speed
outgoing traffic, with some "NETDEV transmit time out" messages. It was
necessary to unload then reload it. So I decided it was time to give
your updates a try.
I started from the latest backport you sent in september (0.42) and
incrementally applied 2.6 updates. I stopped at 0.50 which provides
VLAN support, because after this one, there are some 2.4-incompatible
changes (64bit consistent memory allocation for rings, and MSI/MSIX
support).
It compiled and worked immediately, and now shows very high performance !
Right now, there's a test running at 925 Mbps and 400 kpps, but I could
reach 1.09 Mpps of input traffic and full gigabit speed above 400 bytes
per packet without any trouble. The test above has been running for 6
hours now, which represents 2.5 TB and 8.6 billions of packets. Moreover,
the test only consumes 15% CPU after I set the poll_interval limit to 10
microseconds.
However, I had to increase the max_interrupt_work to 10, because at 5,
I would receive the following message almost every second (both in 0.30
and 0.50) : "too many iterations (6) in nv_nic_irq". At 10, it never
happened.
I guess we should raise it both in 2.4 and 2.6, but I did not do it in
the patch below because I wanted the code to be as similar as possible
between the two trees.
Given that the driver is almost not usable in 0.30, I suspect that very
few people currently use it as-is with kernel 2.4. And since it shows
excellent performance in 0.50, and does not exhibit any instability, I
think that there would be far more benefits than risks in merging it.
How do you feel about this ?
BTW, I have CCed John Linville who maintains network drivers for RHEL3
and who might be interested too.
Regard,
Willy
[-- Attachment #2: 2.4.32-forcedeth-0.50.diff --]
[-- Type: text/plain, Size: 75985 bytes --]
>From nobody Mon Sep 17 00:00:00 2001
From: Willy TARREAU <willy@pcw.(none)>
Date: Tue, 30 May 2006 23:26:28 +0200
Subject: [NETDRV] updated the forcedeth driver to 0.50
While testing the forcedeth driver on a SunFire X2100 (Opteron Dual
Core), I encountered problems with the driver hanging after a few
megabytes while pushing traffic at Gbps speed. The driver was at 0.30.
An mostly trivial update to 0.50 fixed all the problems and brought a
huge performance boost. However, max_interrupt_work should be set to
10 not 5 above 400 kpps.
---
drivers/net/forcedeth.c | 1567 ++++++++++++++++++++++++++++++++++++-----------
include/linux/pci_ids.h | 4
2 files changed, 1200 insertions(+), 371 deletions(-)
001a72816f856774c12fd2863f55b8ddf26d0095
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 7605eb7..bab7893 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -10,7 +10,7 @@
* trademarks of NVIDIA Corporation in the United States and other
* countries.
*
- * Copyright (C) 2003,4 Manfred Spraul
+ * Copyright (C) 2003,4,5 Manfred Spraul
* Copyright (C) 2004 Andrew de Quincey (wol support)
* Copyright (C) 2004 Carl-Daniel Hailfinger (invalid MAC handling, insane
* IRQ rate fixes, bigendian fixes, cleanups, verification)
@@ -79,6 +79,30 @@
* 0.30: 25 Sep 2004: rx checksum support for nf 250 Gb. Add rx reset
* into nv_close, otherwise reenabling for wol can
* cause DMA to kfree'd memory.
+ * 0.31: 14 Nov 2004: ethtool support for getting/setting link
+ * capabilities.
+ * 0.32: 16 Apr 2005: RX_ERROR4 handling added.
+ * 0.33: 16 May 2005: Support for MCP51 added.
+ * 0.34: 18 Jun 2005: Add DEV_NEED_LINKTIMER to all nForce nics.
+ * 0.35: 26 Jun 2005: Support for MCP55 added.
+ * 0.36: 28 Jun 2005: Add jumbo frame support.
+ * 0.37: 10 Jul 2005: Additional ethtool support, cleanup of pci id list
+ * 0.38: 16 Jul 2005: tx irq rewrite: Use global flags instead of
+ * per-packet flags.
+ * 0.39: 18 Jul 2005: Add 64bit descriptor support.
+ * 0.40: 19 Jul 2005: Add support for mac address change.
+ * 0.41: 30 Jul 2005: Write back original MAC in nv_close instead
+ * of nv_remove
+ * 0.42: 06 Aug 2005: Fix lack of link speed initialization
+ * in the second (and later) nv_open call
+ * 0.43: 10 Aug 2005: Add support for tx checksum.
+ * 0.44: 20 Aug 2005: Add support for scatter gather and segmentation.
+ * 0.45: 18 Sep 2005: Remove nv_stop/start_rx from every link check
+ * 0.46: 20 Oct 2005: Add irq optimization modes.
+ * 0.47: 26 Oct 2005: Add phyaddr 0 in phy scan.
+ * 0.48: 24 Dec 2005: Disable TSO, bugfix for pci_map_single
+ * 0.49: 10 Dec 2005: Fix tso for large buffers.
+ * 0.50: 20 Jan 2006: Add 8021pq tagging support.
*
* Known bugs:
* We suspect that on some hardware no TX done interrupts are generated.
@@ -90,7 +114,7 @@
* DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
* superfluous timer interrupts from the nic.
*/
-#define FORCEDETH_VERSION "0.30"
+#define FORCEDETH_VERSION "0.50"
#define DRV_NAME "forcedeth"
#include <linux/module.h>
@@ -108,6 +132,7 @@ #include <linux/skbuff.h>
#include <linux/mii.h>
#include <linux/random.h>
#include <linux/init.h>
+#include <linux/if_vlan.h>
#include <asm/irq.h>
#include <asm/io.h>
@@ -120,16 +145,22 @@ #else
#define dprintk(x...) do { } while (0)
#endif
+/* not present in 2.4 */
+#ifndef NETDEV_TX_OK
+#define NETDEV_TX_OK 0
+#define NETDEV_TX_BUSY 1
+#endif
/*
* Hardware access:
*/
-#define DEV_NEED_LASTPACKET1 0x0001 /* set LASTPACKET1 in tx flags */
-#define DEV_IRQMASK_1 0x0002 /* use NVREG_IRQMASK_WANTED_1 for irq mask */
-#define DEV_IRQMASK_2 0x0004 /* use NVREG_IRQMASK_WANTED_2 for irq mask */
-#define DEV_NEED_TIMERIRQ 0x0008 /* set the timer irq flag in the irq mask */
-#define DEV_NEED_LINKTIMER 0x0010 /* poll link settings. Relies on the timer irq */
+#define DEV_NEED_TIMERIRQ 0x0001 /* set the timer irq flag in the irq mask */
+#define DEV_NEED_LINKTIMER 0x0002 /* poll link settings. Relies on the timer irq */
+#define DEV_HAS_LARGEDESC 0x0004 /* device supports jumbo frames and needs packet format 2 */
+#define DEV_HAS_HIGH_DMA 0x0008 /* device supports 64bit dma */
+#define DEV_HAS_CHECKSUM 0x0010 /* device supports tx and rx checksum offloads */
+#define DEV_HAS_VLAN 0x0020 /* device supports vlan tagging and striping */
enum {
NvRegIrqStatus = 0x000,
@@ -140,13 +171,17 @@ #define NVREG_IRQ_RX_ERROR 0x0001
#define NVREG_IRQ_RX 0x0002
#define NVREG_IRQ_RX_NOBUF 0x0004
#define NVREG_IRQ_TX_ERR 0x0008
-#define NVREG_IRQ_TX2 0x0010
+#define NVREG_IRQ_TX_OK 0x0010
#define NVREG_IRQ_TIMER 0x0020
#define NVREG_IRQ_LINK 0x0040
+#define NVREG_IRQ_TX_ERROR 0x0080
#define NVREG_IRQ_TX1 0x0100
-#define NVREG_IRQMASK_WANTED_1 0x005f
-#define NVREG_IRQMASK_WANTED_2 0x0147
-#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
+#define NVREG_IRQMASK_THROUGHPUT 0x00df
+#define NVREG_IRQMASK_CPU 0x0040
+
+#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR| \
+ NVREG_IRQ_TX_OK|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX_ERROR| \
+ NVREG_IRQ_TX1))
NvRegUnknownSetupReg6 = 0x008,
#define NVREG_UNKSETUP6_VAL 3
@@ -156,7 +191,8 @@ #define NVREG_UNKSETUP6_VAL 3
* NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
*/
NvRegPollingInterval = 0x00c,
-#define NVREG_POLL_DEFAULT 970
+#define NVREG_POLL_DEFAULT_THROUGHPUT 970
+#define NVREG_POLL_DEFAULT_CPU 13
NvRegMisc1 = 0x080,
#define NVREG_MISC1_HD 0x02
#define NVREG_MISC1_FORCE 0x3b0f3c
@@ -211,6 +247,7 @@ #define NVREG_LINKSPEED_FORCE 0x10000
#define NVREG_LINKSPEED_10 1000
#define NVREG_LINKSPEED_100 100
#define NVREG_LINKSPEED_1000 50
+#define NVREG_LINKSPEED_MASK (0xFFF)
NvRegUnknownSetupReg5 = 0x130,
#define NVREG_UNKSETUP5_BIT31 (1<<31)
NvRegUnknownSetupReg3 = 0x13c,
@@ -222,6 +259,11 @@ #define NVREG_TXRXCTL_BIT2 0x0004
#define NVREG_TXRXCTL_IDLE 0x0008
#define NVREG_TXRXCTL_RESET 0x0010
#define NVREG_TXRXCTL_RXCHECK 0x0400
+#define NVREG_TXRXCTL_DESC_1 0
+#define NVREG_TXRXCTL_DESC_2 0x02100
+#define NVREG_TXRXCTL_DESC_3 0x02200
+#define NVREG_TXRXCTL_VLANSTRIP 0x00040
+#define NVREG_TXRXCTL_VLANINS 0x00080
NvRegMIIStatus = 0x180,
#define NVREG_MIISTAT_ERROR 0x0001
#define NVREG_MIISTAT_LINKCHANGE 0x0008
@@ -271,6 +313,8 @@ #define NVREG_POWERSTATE_D0 0x0000
#define NVREG_POWERSTATE_D1 0x0001
#define NVREG_POWERSTATE_D2 0x0002
#define NVREG_POWERSTATE_D3 0x0003
+ NvRegVlanControl = 0x300,
+#define NVREG_VLANCONTROL_ENABLE 0x2000
};
/* Big endian: should work, but is untested */
@@ -279,6 +323,18 @@ struct ring_desc {
u32 FlagLen;
};
+struct ring_desc_ex {
+ u32 PacketBufferHigh;
+ u32 PacketBufferLow;
+ u32 TxVlan;
+ u32 FlagLen;
+};
+
+typedef union _ring_type {
+ struct ring_desc* orig;
+ struct ring_desc_ex* ex;
+} ring_type;
+
#define FLAG_MASK_V1 0xffff0000
#define FLAG_MASK_V2 0xffffc000
#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
@@ -286,7 +342,7 @@ #define LEN_MASK_V2 (0xffffffff ^ FLAG_M
#define NV_TX_LASTPACKET (1<<16)
#define NV_TX_RETRYERROR (1<<19)
-#define NV_TX_LASTPACKET1 (1<<24)
+#define NV_TX_FORCED_INTERRUPT (1<<24)
#define NV_TX_DEFERRED (1<<26)
#define NV_TX_CARRIERLOST (1<<27)
#define NV_TX_LATECOLLISION (1<<28)
@@ -296,7 +352,7 @@ #define NV_TX_VALID (1<<31)
#define NV_TX2_LASTPACKET (1<<29)
#define NV_TX2_RETRYERROR (1<<18)
-#define NV_TX2_LASTPACKET1 (1<<23)
+#define NV_TX2_FORCED_INTERRUPT (1<<30)
#define NV_TX2_DEFERRED (1<<25)
#define NV_TX2_CARRIERLOST (1<<26)
#define NV_TX2_LATECOLLISION (1<<27)
@@ -304,6 +360,14 @@ #define NV_TX2_UNDERFLOW (1<<28)
/* error and valid are the same for both */
#define NV_TX2_ERROR (1<<30)
#define NV_TX2_VALID (1<<31)
+#define NV_TX2_TSO (1<<28)
+#define NV_TX2_TSO_SHIFT 14
+#define NV_TX2_TSO_MAX_SHIFT 14
+#define NV_TX2_TSO_MAX_SIZE (1<<NV_TX2_TSO_MAX_SHIFT)
+#define NV_TX2_CHECKSUM_L3 (1<<27)
+#define NV_TX2_CHECKSUM_L4 (1<<26)
+
+#define NV_TX3_VLAN_TAG_PRESENT (1<<18)
#define NV_RX_DESCRIPTORVALID (1<<16)
#define NV_RX_MISSEDFRAME (1<<17)
@@ -335,6 +399,9 @@ #define NV_RX2_FRAMINGERR (1<<24)
#define NV_RX2_ERROR (1<<30)
#define NV_RX2_AVAIL (1<<31)
+#define NV_RX3_VLAN_TAG_PRESENT (1<<16)
+#define NV_RX3_VLAN_TAG_MASK (0x0000FFFF)
+
/* Miscelaneous hardware related defines: */
#define NV_PCI_REGSZ 0x270
@@ -361,34 +428,39 @@ #define NV_WAKEUPMASKENTRIES 4
#define NV_WATCHDOG_TIMEO (5*HZ)
#define RX_RING 128
-#define TX_RING 64
-/*
+#define TX_RING 256
+/*
* If your nic mysteriously hangs then try to reduce the limits
* to 1/0: It might be required to set NV_TX_LASTPACKET in the
* last valid ring entry. But this would be impossible to
* implement - probably a disassembly error.
*/
-#define TX_LIMIT_STOP 63
-#define TX_LIMIT_START 62
+#define TX_LIMIT_STOP 255
+#define TX_LIMIT_START 254
/* rx/tx mac addr + type + vlan + align + slack*/
-#define RX_NIC_BUFSIZE (ETH_DATA_LEN + 64)
-/* even more slack */
-#define RX_ALLOC_BUFSIZE (ETH_DATA_LEN + 128)
+#define NV_RX_HEADERS (64)
+/* even more slack. */
+#define NV_RX_ALLOC_PAD (64)
+
+/* maximum mtu size */
+#define NV_PKTLIMIT_1 ETH_DATA_LEN /* hard limit not known */
+#define NV_PKTLIMIT_2 9100 /* Actual limit according to NVidia: 9202 */
#define OOM_REFILL (1+HZ/20)
#define POLL_WAIT (1+HZ/100)
#define LINK_TIMEOUT (3*HZ)
-/*
+/*
* desc_ver values:
- * This field has two purposes:
- * - Newer nics uses a different ring layout. The layout is selected by
- * comparing np->desc_ver with DESC_VER_xy.
- * - It contains bits that are forced on when writing to NvRegTxRxControl.
+ * The nic supports three different descriptor types:
+ * - DESC_VER_1: Original
+ * - DESC_VER_2: support for jumbo frames.
+ * - DESC_VER_3: 64-bit format.
*/
-#define DESC_VER_1 0x0
-#define DESC_VER_2 (0x02100|NVREG_TXRXCTL_RXCHECK)
+#define DESC_VER_1 1
+#define DESC_VER_2 2
+#define DESC_VER_3 3
/* PHY defines */
#define PHY_OUI_MARVELL 0x5043
@@ -442,6 +514,8 @@ struct fe_priv {
int in_shutdown;
u32 linkspeed;
int duplex;
+ int autoneg;
+ int fixed_mode;
int phyaddr;
int wolenabled;
unsigned int phy_oui;
@@ -453,15 +527,20 @@ struct fe_priv {
u32 orig_mac[2];
u32 irqmask;
u32 desc_ver;
+ u32 txrxctl_bits;
+ u32 vlanctl_bits;
+
+ void __iomem *base;
/* rx specific fields.
* Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
*/
- struct ring_desc *rx_ring;
+ ring_type rx_ring;
unsigned int cur_rx, refill_rx;
struct sk_buff *rx_skbuff[RX_RING];
dma_addr_t rx_dma[RX_RING];
unsigned int rx_buf_sz;
+ unsigned int pkt_limit;
struct timer_list oom_kick;
struct timer_list nic_poll;
@@ -473,11 +552,15 @@ struct fe_priv {
/*
* tx specific fields.
*/
- struct ring_desc *tx_ring;
+ ring_type tx_ring;
unsigned int next_tx, nic_tx;
struct sk_buff *tx_skbuff[TX_RING];
dma_addr_t tx_dma[TX_RING];
+ unsigned int tx_dma_len[TX_RING];
u32 tx_flags;
+
+ /* vlan fields */
+ struct vlan_group *vlangrp;
};
/*
@@ -486,17 +569,36 @@ struct fe_priv {
*/
static int max_interrupt_work = 5;
+/*
+ * Optimization can be either throuput mode or cpu mode
+ *
+ * Throughput Mode: Every tx and rx packet will generate an interrupt.
+ * CPU Mode: Interrupts are controlled by a timer.
+ */
+#define NV_OPTIMIZATION_MODE_THROUGHPUT 0
+#define NV_OPTIMIZATION_MODE_CPU 1
+static int optimization_mode = NV_OPTIMIZATION_MODE_THROUGHPUT;
+
+/*
+ * Poll interval for timer irq
+ *
+ * This interval determines how frequent an interrupt is generated.
+ * The is value is determined by [(time_in_micro_secs * 100) / (2^10)]
+ * Min = 0, and Max = 65535
+ */
+static int poll_interval = -1;
+
static inline struct fe_priv *get_nvpriv(struct net_device *dev)
{
- return (struct fe_priv *) dev->priv;
+ return netdev_priv(dev);
}
-static inline u8 *get_hwbase(struct net_device *dev)
+static inline u8 __iomem *get_hwbase(struct net_device *dev)
{
- return (u8 *) dev->base_addr;
+ return ((struct fe_priv *)netdev_priv(dev))->base;
}
-static inline void pci_push(u8 * base)
+static inline void pci_push(u8 __iomem *base)
{
/* force out pending posted writes */
readl(base);
@@ -508,10 +610,15 @@ static inline u32 nv_descr_getlength(str
& ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
}
+static inline u32 nv_descr_getlength_ex(struct ring_desc_ex *prd, u32 v)
+{
+ return le32_to_cpu(prd->FlagLen) & LEN_MASK_V2;
+}
+
static int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target,
int delay, int delaymax, const char *msg)
{
- u8 *base = get_hwbase(dev);
+ u8 __iomem *base = get_hwbase(dev);
pci_push(base);
do {
@@ -533,7 +640,7 @@ #define MII_READ (-1)
*/
static int mii_rw(struct net_device *dev, int addr, int miireg, int value)
{
- u8 *base = get_hwbase(dev);
+ u8 __iomem *base = get_hwbase(dev);
u32 reg;
int retval;
@@ -577,7 +684,7 @@ static int mii_rw(struct net_device *dev
static int phy_reset(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
u32 miicontrol;
unsigned int tries = 0;
@@ -604,7 +711,7 @@ static int phy_reset(struct net_device *
static int phy_init(struct net_device *dev)
{
struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ u8 __iomem *base = get_hwbase(dev);
u32 phyinterface, phy_reserved, mii_status, mii_control, mii_control_1000,reg;
/* set advertise register */
@@ -680,8 +787,8 @@ static int phy_init(struct net_device *d
static void nv_start_rx(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
dprintk(KERN_DEBUG "%s: nv_start_rx\n", dev->name);
/* Already running? Stop it. */
@@ -699,7 +806,7 @@ static void nv_start_rx(struct net_devic
static void nv_stop_rx(struct net_device *dev)
{
- u8 *base = get_hwbase(dev);
+ u8 __iomem *base = get_hwbase(dev);
dprintk(KERN_DEBUG "%s: nv_stop_rx\n", dev->name);
writel(0, base + NvRegReceiverControl);
@@ -713,7 +820,7 @@ static void nv_stop_rx(struct net_device
static void nv_start_tx(struct net_device *dev)
{
- u8 *base = get_hwbase(dev);
+ u8 __iomem *base = get_hwbase(dev);
dprintk(KERN_DEBUG "%s: nv_start_tx\n", dev->name);
writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
@@ -722,7 +829,7 @@ static void nv_start_tx(struct net_devic
static void nv_stop_tx(struct net_device *dev)
{
- u8 *base = get_hwbase(dev);
+ u8 __iomem *base = get_hwbase(dev);
dprintk(KERN_DEBUG "%s: nv_stop_tx\n", dev->name);
writel(0, base + NvRegTransmitterControl);
@@ -736,14 +843,14 @@ static void nv_stop_tx(struct net_device
static void nv_txrx_reset(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
dprintk(KERN_DEBUG "%s: nv_txrx_reset\n", dev->name);
- writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver, base + NvRegTxRxControl);
+ writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->txrxctl_bits, base + NvRegTxRxControl);
pci_push(base);
udelay(NV_TXRX_RESET_DELAY);
- writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl);
+ writel(NVREG_TXRXCTL_BIT2 | np->txrxctl_bits, base + NvRegTxRxControl);
pci_push(base);
}
@@ -755,7 +862,7 @@ static void nv_txrx_reset(struct net_dev
*/
static struct net_device_stats *nv_get_stats(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
/* It seems that the nic always generates interrupts and doesn't
* accumulate errors internally. Thus the current values in np->stats
@@ -764,50 +871,6 @@ static struct net_device_stats *nv_get_s
return &np->stats;
}
-static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- struct fe_priv *np = get_nvpriv(dev);
- strcpy(info->driver, "forcedeth");
- strcpy(info->version, FORCEDETH_VERSION);
- strcpy(info->bus_info, pci_name(np->pci_dev));
-}
-
-static void nv_get_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
-{
- struct fe_priv *np = get_nvpriv(dev);
- wolinfo->supported = WAKE_MAGIC;
-
- spin_lock_irq(&np->lock);
- if (np->wolenabled)
- wolinfo->wolopts = WAKE_MAGIC;
- spin_unlock_irq(&np->lock);
-}
-
-static int nv_set_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
-{
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
-
- spin_lock_irq(&np->lock);
- if (wolinfo->wolopts == 0) {
- writel(0, base + NvRegWakeUpFlags);
- np->wolenabled = 0;
- }
- if (wolinfo->wolopts & WAKE_MAGIC) {
- writel(NVREG_WAKEUPFLAGS_ENABLE, base + NvRegWakeUpFlags);
- np->wolenabled = 1;
- }
- spin_unlock_irq(&np->lock);
- return 0;
-}
-
-static struct ethtool_ops ops = {
- .get_drvinfo = nv_get_drvinfo,
- .get_link = ethtool_op_get_link,
- .get_wol = nv_get_wol,
- .set_wol = nv_set_wol,
-};
-
/*
* nv_alloc_rx: fill rx ring entries.
* Return 1 if the allocations for the skbs failed and the
@@ -815,7 +878,7 @@ static struct ethtool_ops ops = {
*/
static int nv_alloc_rx(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
unsigned int refill_rx = np->refill_rx;
int nr;
@@ -825,7 +888,7 @@ static int nv_alloc_rx(struct net_device
nr = refill_rx % RX_RING;
if (np->rx_skbuff[nr] == NULL) {
- skb = dev_alloc_skb(RX_ALLOC_BUFSIZE);
+ skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD);
if (!skb)
break;
@@ -834,11 +897,18 @@ static int nv_alloc_rx(struct net_device
} else {
skb = np->rx_skbuff[nr];
}
- np->rx_dma[nr] = pci_map_single(np->pci_dev, skb->data, skb->len,
- PCI_DMA_FROMDEVICE);
- np->rx_ring[nr].PacketBuffer = cpu_to_le32(np->rx_dma[nr]);
- wmb();
- np->rx_ring[nr].FlagLen = cpu_to_le32(RX_NIC_BUFSIZE | NV_RX_AVAIL);
+ np->rx_dma[nr] = pci_map_single(np->pci_dev, skb->data,
+ skb->end-skb->data, PCI_DMA_FROMDEVICE);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ np->rx_ring.orig[nr].PacketBuffer = cpu_to_le32(np->rx_dma[nr]);
+ wmb();
+ np->rx_ring.orig[nr].FlagLen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL);
+ } else {
+ np->rx_ring.ex[nr].PacketBufferHigh = cpu_to_le64(np->rx_dma[nr]) >> 32;
+ np->rx_ring.ex[nr].PacketBufferLow = cpu_to_le64(np->rx_dma[nr]) & 0x0FFFFFFFF;
+ wmb();
+ np->rx_ring.ex[nr].FlagLen = cpu_to_le32(np->rx_buf_sz | NV_RX2_AVAIL);
+ }
dprintk(KERN_DEBUG "%s: nv_alloc_rx: Packet %d marked as Available\n",
dev->name, refill_rx);
refill_rx++;
@@ -852,7 +922,7 @@ static int nv_alloc_rx(struct net_device
static void nv_do_rx_refill(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
disable_irq(dev->irq);
if (nv_alloc_rx(dev)) {
@@ -864,49 +934,94 @@ static void nv_do_rx_refill(unsigned lon
enable_irq(dev->irq);
}
-static int nv_init_ring(struct net_device *dev)
+static void nv_init_rx(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
int i;
- np->next_tx = np->nic_tx = 0;
- for (i = 0; i < TX_RING; i++)
- np->tx_ring[i].FlagLen = 0;
-
np->cur_rx = RX_RING;
np->refill_rx = 0;
for (i = 0; i < RX_RING; i++)
- np->rx_ring[i].FlagLen = 0;
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ np->rx_ring.orig[i].FlagLen = 0;
+ else
+ np->rx_ring.ex[i].FlagLen = 0;
+}
+
+static void nv_init_tx(struct net_device *dev)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ int i;
+
+ np->next_tx = np->nic_tx = 0;
+ for (i = 0; i < TX_RING; i++) {
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ np->tx_ring.orig[i].FlagLen = 0;
+ else
+ np->tx_ring.ex[i].FlagLen = 0;
+ np->tx_skbuff[i] = NULL;
+ np->tx_dma[i] = 0;
+ }
+}
+
+static int nv_init_ring(struct net_device *dev)
+{
+ nv_init_tx(dev);
+ nv_init_rx(dev);
return nv_alloc_rx(dev);
}
+static int nv_release_txskb(struct net_device *dev, unsigned int skbnr)
+{
+ struct fe_priv *np = netdev_priv(dev);
+
+ dprintk(KERN_INFO "%s: nv_release_txskb for skbnr %d\n",
+ dev->name, skbnr);
+
+ if (np->tx_dma[skbnr]) {
+ pci_unmap_page(np->pci_dev, np->tx_dma[skbnr],
+ np->tx_dma_len[skbnr],
+ PCI_DMA_TODEVICE);
+ np->tx_dma[skbnr] = 0;
+ }
+
+ if (np->tx_skbuff[skbnr]) {
+ dev_kfree_skb_irq(np->tx_skbuff[skbnr]);
+ np->tx_skbuff[skbnr] = NULL;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
static void nv_drain_tx(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- int i;
+ struct fe_priv *np = netdev_priv(dev);
+ unsigned int i;
+
for (i = 0; i < TX_RING; i++) {
- np->tx_ring[i].FlagLen = 0;
- if (np->tx_skbuff[i]) {
- pci_unmap_single(np->pci_dev, np->tx_dma[i],
- np->tx_skbuff[i]->len,
- PCI_DMA_TODEVICE);
- dev_kfree_skb(np->tx_skbuff[i]);
- np->tx_skbuff[i] = NULL;
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ np->tx_ring.orig[i].FlagLen = 0;
+ else
+ np->tx_ring.ex[i].FlagLen = 0;
+ if (nv_release_txskb(dev, i))
np->stats.tx_dropped++;
- }
}
}
static void nv_drain_rx(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
int i;
for (i = 0; i < RX_RING; i++) {
- np->rx_ring[i].FlagLen = 0;
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ np->rx_ring.orig[i].FlagLen = 0;
+ else
+ np->rx_ring.ex[i].FlagLen = 0;
wmb();
if (np->rx_skbuff[i]) {
pci_unmap_single(np->pci_dev, np->rx_dma[i],
- np->rx_skbuff[i]->len,
+ np->rx_skbuff[i]->end-np->rx_skbuff[i]->data,
PCI_DMA_FROMDEVICE);
dev_kfree_skb(np->rx_skbuff[i]);
np->rx_skbuff[i] = NULL;
@@ -926,20 +1041,113 @@ static void drain_ring(struct net_device
*/
static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- int nr = np->next_tx % TX_RING;
+ struct fe_priv *np = netdev_priv(dev);
+ u32 tx_flags = 0;
+ u32 tx_flags_extra = (np->desc_ver == DESC_VER_1 ? NV_TX_LASTPACKET : NV_TX2_LASTPACKET);
+ unsigned int fragments = skb_shinfo(skb)->nr_frags;
+ unsigned int nr = (np->next_tx - 1) % TX_RING;
+ unsigned int start_nr = np->next_tx % TX_RING;
+ unsigned int i;
+ u32 offset = 0;
+ u32 bcnt;
+ u32 size = skb->len-skb->data_len;
+ u32 entries = (size >> NV_TX2_TSO_MAX_SHIFT) + ((size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
+ u32 tx_flags_vlan = 0;
+
+ /* add fragments to entries count */
+ for (i = 0; i < fragments; i++) {
+ entries += (skb_shinfo(skb)->frags[i].size >> NV_TX2_TSO_MAX_SHIFT) +
+ ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
+ }
+
+ spin_lock_irq(&np->lock);
+
+ if ((np->next_tx - np->nic_tx + entries - 1) > TX_LIMIT_STOP) {
+ spin_unlock_irq(&np->lock);
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* setup the header buffer */
+ do {
+ bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;
+ nr = (nr + 1) % TX_RING;
+
+ np->tx_dma[nr] = pci_map_single(np->pci_dev, skb->data + offset, bcnt,
+ PCI_DMA_TODEVICE);
+ np->tx_dma_len[nr] = bcnt;
+
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ np->tx_ring.orig[nr].PacketBuffer = cpu_to_le32(np->tx_dma[nr]);
+ np->tx_ring.orig[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags);
+ } else {
+ np->tx_ring.ex[nr].PacketBufferHigh = cpu_to_le64(np->tx_dma[nr]) >> 32;
+ np->tx_ring.ex[nr].PacketBufferLow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF;
+ np->tx_ring.ex[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags);
+ }
+ tx_flags = np->tx_flags;
+ offset += bcnt;
+ size -= bcnt;
+ } while(size);
+
+ /* setup the fragments */
+ for (i = 0; i < fragments; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ u32 size = frag->size;
+ offset = 0;
+
+ do {
+ bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;
+ nr = (nr + 1) % TX_RING;
+
+ np->tx_dma[nr] = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt,
+ PCI_DMA_TODEVICE);
+ np->tx_dma_len[nr] = bcnt;
+
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ np->tx_ring.orig[nr].PacketBuffer = cpu_to_le32(np->tx_dma[nr]);
+ np->tx_ring.orig[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags);
+ } else {
+ np->tx_ring.ex[nr].PacketBufferHigh = cpu_to_le64(np->tx_dma[nr]) >> 32;
+ np->tx_ring.ex[nr].PacketBufferLow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF;
+ np->tx_ring.ex[nr].FlagLen = cpu_to_le32((bcnt-1) | tx_flags);
+ }
+ offset += bcnt;
+ size -= bcnt;
+ } while (size);
+ }
+
+ /* set last fragment flag */
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ np->tx_ring.orig[nr].FlagLen |= cpu_to_le32(tx_flags_extra);
+ } else {
+ np->tx_ring.ex[nr].FlagLen |= cpu_to_le32(tx_flags_extra);
+ }
np->tx_skbuff[nr] = skb;
- np->tx_dma[nr] = pci_map_single(np->pci_dev, skb->data,skb->len,
- PCI_DMA_TODEVICE);
- np->tx_ring[nr].PacketBuffer = cpu_to_le32(np->tx_dma[nr]);
+#ifdef NETIF_F_TSO
+ if (skb_shinfo(skb)->tso_size)
+ tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->tso_size << NV_TX2_TSO_SHIFT);
+ else
+#endif
+ tx_flags_extra = (skb->ip_summed == CHECKSUM_HW ? (NV_TX2_CHECKSUM_L3|NV_TX2_CHECKSUM_L4) : 0);
- spin_lock_irq(&np->lock);
- wmb();
- np->tx_ring[nr].FlagLen = cpu_to_le32( (skb->len-1) | np->tx_flags );
- dprintk(KERN_DEBUG "%s: nv_start_xmit: packet packet %d queued for transmission.\n",
- dev->name, np->next_tx);
+ /* vlan tag */
+ if (np->vlangrp && vlan_tx_tag_present(skb)) {
+ tx_flags_vlan = NV_TX3_VLAN_TAG_PRESENT | vlan_tx_tag_get(skb);
+ }
+
+ /* set tx flags */
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ np->tx_ring.orig[start_nr].FlagLen |= cpu_to_le32(tx_flags | tx_flags_extra);
+ } else {
+ np->tx_ring.ex[start_nr].TxVlan = cpu_to_le32(tx_flags_vlan);
+ np->tx_ring.ex[start_nr].FlagLen |= cpu_to_le32(tx_flags | tx_flags_extra);
+ }
+
+ dprintk(KERN_DEBUG "%s: nv_start_xmit: packet %d (entries %d) queued for transmission. tx_flags_extra: %x\n",
+ dev->name, np->next_tx, entries, tx_flags_extra);
{
int j;
for (j=0; j<64; j++) {
@@ -950,15 +1158,13 @@ static int nv_start_xmit(struct sk_buff
dprintk("\n");
}
- np->next_tx++;
+ np->next_tx += entries;
dev->trans_start = jiffies;
- if (np->next_tx - np->nic_tx >= TX_LIMIT_STOP)
- netif_stop_queue(dev);
spin_unlock_irq(&np->lock);
- writel(NVREG_TXRXCTL_KICK|np->desc_ver, get_hwbase(dev) + NvRegTxRxControl);
+ writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
pci_push(get_hwbase(dev));
- return 0;
+ return NETDEV_TX_OK;
}
/*
@@ -968,49 +1174,55 @@ static int nv_start_xmit(struct sk_buff
*/
static void nv_tx_done(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
u32 Flags;
- int i;
+ unsigned int i;
+ struct sk_buff *skb;
while (np->nic_tx != np->next_tx) {
i = np->nic_tx % TX_RING;
- Flags = le32_to_cpu(np->tx_ring[i].FlagLen);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ Flags = le32_to_cpu(np->tx_ring.orig[i].FlagLen);
+ else
+ Flags = le32_to_cpu(np->tx_ring.ex[i].FlagLen);
dprintk(KERN_DEBUG "%s: nv_tx_done: looking at packet %d, Flags 0x%x.\n",
dev->name, np->nic_tx, Flags);
if (Flags & NV_TX_VALID)
break;
if (np->desc_ver == DESC_VER_1) {
- if (Flags & (NV_TX_RETRYERROR|NV_TX_CARRIERLOST|NV_TX_LATECOLLISION|
- NV_TX_UNDERFLOW|NV_TX_ERROR)) {
- if (Flags & NV_TX_UNDERFLOW)
- np->stats.tx_fifo_errors++;
- if (Flags & NV_TX_CARRIERLOST)
- np->stats.tx_carrier_errors++;
- np->stats.tx_errors++;
- } else {
- np->stats.tx_packets++;
- np->stats.tx_bytes += np->tx_skbuff[i]->len;
+ if (Flags & NV_TX_LASTPACKET) {
+ skb = np->tx_skbuff[i];
+ if (Flags & (NV_TX_RETRYERROR|NV_TX_CARRIERLOST|NV_TX_LATECOLLISION|
+ NV_TX_UNDERFLOW|NV_TX_ERROR)) {
+ if (Flags & NV_TX_UNDERFLOW)
+ np->stats.tx_fifo_errors++;
+ if (Flags & NV_TX_CARRIERLOST)
+ np->stats.tx_carrier_errors++;
+ np->stats.tx_errors++;
+ } else {
+ np->stats.tx_packets++;
+ np->stats.tx_bytes += skb->len;
+ }
}
} else {
- if (Flags & (NV_TX2_RETRYERROR|NV_TX2_CARRIERLOST|NV_TX2_LATECOLLISION|
- NV_TX2_UNDERFLOW|NV_TX2_ERROR)) {
- if (Flags & NV_TX2_UNDERFLOW)
- np->stats.tx_fifo_errors++;
- if (Flags & NV_TX2_CARRIERLOST)
- np->stats.tx_carrier_errors++;
- np->stats.tx_errors++;
- } else {
- np->stats.tx_packets++;
- np->stats.tx_bytes += np->tx_skbuff[i]->len;
+ if (Flags & NV_TX2_LASTPACKET) {
+ skb = np->tx_skbuff[i];
+ if (Flags & (NV_TX2_RETRYERROR|NV_TX2_CARRIERLOST|NV_TX2_LATECOLLISION|
+ NV_TX2_UNDERFLOW|NV_TX2_ERROR)) {
+ if (Flags & NV_TX2_UNDERFLOW)
+ np->stats.tx_fifo_errors++;
+ if (Flags & NV_TX2_CARRIERLOST)
+ np->stats.tx_carrier_errors++;
+ np->stats.tx_errors++;
+ } else {
+ np->stats.tx_packets++;
+ np->stats.tx_bytes += skb->len;
+ }
}
}
- pci_unmap_single(np->pci_dev, np->tx_dma[i],
- np->tx_skbuff[i]->len,
- PCI_DMA_TODEVICE);
- dev_kfree_skb_irq(np->tx_skbuff[i]);
- np->tx_skbuff[i] = NULL;
+ nv_release_txskb(dev, i);
np->nic_tx++;
}
if (np->next_tx - np->nic_tx < TX_LIMIT_START)
@@ -1023,12 +1235,59 @@ static void nv_tx_done(struct net_device
*/
static void nv_tx_timeout(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
- dprintk(KERN_DEBUG "%s: Got tx_timeout. irq: %08x\n", dev->name,
+ printk(KERN_INFO "%s: Got tx_timeout. irq: %08x\n", dev->name,
readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK);
+ {
+ int i;
+
+ printk(KERN_INFO "%s: Ring at %lx: next %d nic %d\n",
+ dev->name, (unsigned long)np->ring_addr,
+ np->next_tx, np->nic_tx);
+ printk(KERN_INFO "%s: Dumping tx registers\n", dev->name);
+ for (i=0;i<0x400;i+= 32) {
+ printk(KERN_INFO "%3x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ i,
+ readl(base + i + 0), readl(base + i + 4),
+ readl(base + i + 8), readl(base + i + 12),
+ readl(base + i + 16), readl(base + i + 20),
+ readl(base + i + 24), readl(base + i + 28));
+ }
+ printk(KERN_INFO "%s: Dumping tx ring\n", dev->name);
+ for (i=0;i<TX_RING;i+= 4) {
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ printk(KERN_INFO "%03x: %08x %08x // %08x %08x // %08x %08x // %08x %08x\n",
+ i,
+ le32_to_cpu(np->tx_ring.orig[i].PacketBuffer),
+ le32_to_cpu(np->tx_ring.orig[i].FlagLen),
+ le32_to_cpu(np->tx_ring.orig[i+1].PacketBuffer),
+ le32_to_cpu(np->tx_ring.orig[i+1].FlagLen),
+ le32_to_cpu(np->tx_ring.orig[i+2].PacketBuffer),
+ le32_to_cpu(np->tx_ring.orig[i+2].FlagLen),
+ le32_to_cpu(np->tx_ring.orig[i+3].PacketBuffer),
+ le32_to_cpu(np->tx_ring.orig[i+3].FlagLen));
+ } else {
+ printk(KERN_INFO "%03x: %08x %08x %08x // %08x %08x %08x // %08x %08x %08x // %08x %08x %08x\n",
+ i,
+ le32_to_cpu(np->tx_ring.ex[i].PacketBufferHigh),
+ le32_to_cpu(np->tx_ring.ex[i].PacketBufferLow),
+ le32_to_cpu(np->tx_ring.ex[i].FlagLen),
+ le32_to_cpu(np->tx_ring.ex[i+1].PacketBufferHigh),
+ le32_to_cpu(np->tx_ring.ex[i+1].PacketBufferLow),
+ le32_to_cpu(np->tx_ring.ex[i+1].FlagLen),
+ le32_to_cpu(np->tx_ring.ex[i+2].PacketBufferHigh),
+ le32_to_cpu(np->tx_ring.ex[i+2].PacketBufferLow),
+ le32_to_cpu(np->tx_ring.ex[i+2].FlagLen),
+ le32_to_cpu(np->tx_ring.ex[i+3].PacketBufferHigh),
+ le32_to_cpu(np->tx_ring.ex[i+3].PacketBufferLow),
+ le32_to_cpu(np->tx_ring.ex[i+3].FlagLen));
+ }
+ }
+ }
+
spin_lock_irq(&np->lock);
/* 1) stop tx engine */
@@ -1042,7 +1301,10 @@ static void nv_tx_timeout(struct net_dev
printk(KERN_DEBUG "%s: tx_timeout: dead entries!\n", dev->name);
nv_drain_tx(dev);
np->next_tx = np->nic_tx = 0;
- writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
+ else
+ writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc_ex)), base + NvRegTxRingPhysAddr);
netif_wake_queue(dev);
}
@@ -1051,10 +1313,65 @@ static void nv_tx_timeout(struct net_dev
spin_unlock_irq(&np->lock);
}
+/*
+ * Called when the nic notices a mismatch between the actual data len on the
+ * wire and the len indicated in the 802 header
+ */
+static int nv_getlen(struct net_device *dev, void *packet, int datalen)
+{
+ int hdrlen; /* length of the 802 header */
+ int protolen; /* length as stored in the proto field */
+
+ /* 1) calculate len according to header */
+ if ( ((struct vlan_ethhdr *)packet)->h_vlan_proto == __constant_htons(ETH_P_8021Q)) {
+ protolen = ntohs( ((struct vlan_ethhdr *)packet)->h_vlan_encapsulated_proto );
+ hdrlen = VLAN_HLEN;
+ } else {
+ protolen = ntohs( ((struct ethhdr *)packet)->h_proto);
+ hdrlen = ETH_HLEN;
+ }
+ dprintk(KERN_DEBUG "%s: nv_getlen: datalen %d, protolen %d, hdrlen %d\n",
+ dev->name, datalen, protolen, hdrlen);
+ if (protolen > ETH_DATA_LEN)
+ return datalen; /* Value in proto field not a len, no checks possible */
+
+ protolen += hdrlen;
+ /* consistency checks: */
+ if (datalen > ETH_ZLEN) {
+ if (datalen >= protolen) {
+ /* more data on wire than in 802 header, trim of
+ * additional data.
+ */
+ dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n",
+ dev->name, protolen);
+ return protolen;
+ } else {
+ /* less data on wire than mentioned in header.
+ * Discard the packet.
+ */
+ dprintk(KERN_DEBUG "%s: nv_getlen: discarding long packet.\n",
+ dev->name);
+ return -1;
+ }
+ } else {
+ /* short packet. Accept only if 802 values are also short */
+ if (protolen > ETH_ZLEN) {
+ dprintk(KERN_DEBUG "%s: nv_getlen: discarding short packet.\n",
+ dev->name);
+ return -1;
+ }
+ dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n",
+ dev->name, datalen);
+ return datalen;
+ }
+}
+
static void nv_rx_process(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
+ struct fe_priv *np = netdev_priv(dev);
u32 Flags;
+ u32 vlanflags = 0;
+
for (;;) {
struct sk_buff *skb;
@@ -1064,8 +1381,14 @@ static void nv_rx_process(struct net_dev
break; /* we scanned the whole ring - do not continue */
i = np->cur_rx % RX_RING;
- Flags = le32_to_cpu(np->rx_ring[i].FlagLen);
- len = nv_descr_getlength(&np->rx_ring[i], np->desc_ver);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ Flags = le32_to_cpu(np->rx_ring.orig[i].FlagLen);
+ len = nv_descr_getlength(&np->rx_ring.orig[i], np->desc_ver);
+ } else {
+ Flags = le32_to_cpu(np->rx_ring.ex[i].FlagLen);
+ len = nv_descr_getlength_ex(&np->rx_ring.ex[i], np->desc_ver);
+ vlanflags = le32_to_cpu(np->rx_ring.ex[i].PacketBufferLow);
+ }
dprintk(KERN_DEBUG "%s: nv_rx_process: looking at packet %d, Flags 0x%x.\n",
dev->name, np->cur_rx, Flags);
@@ -1079,7 +1402,7 @@ static void nv_rx_process(struct net_dev
* the performance.
*/
pci_unmap_single(np->pci_dev, np->rx_dma[i],
- np->rx_skbuff[i]->len,
+ np->rx_skbuff[i]->end-np->rx_skbuff[i]->data,
PCI_DMA_FROMDEVICE);
{
@@ -1097,63 +1420,71 @@ static void nv_rx_process(struct net_dev
if (!(Flags & NV_RX_DESCRIPTORVALID))
goto next_pkt;
- if (Flags & NV_RX_MISSEDFRAME) {
- np->stats.rx_missed_errors++;
- np->stats.rx_errors++;
- goto next_pkt;
- }
- if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3|NV_RX_ERROR4)) {
- np->stats.rx_errors++;
- goto next_pkt;
- }
- if (Flags & NV_RX_CRCERR) {
- np->stats.rx_crc_errors++;
- np->stats.rx_errors++;
- goto next_pkt;
- }
- if (Flags & NV_RX_OVERFLOW) {
- np->stats.rx_over_errors++;
- np->stats.rx_errors++;
- goto next_pkt;
- }
if (Flags & NV_RX_ERROR) {
- /* framing errors are soft errors, the rest is fatal. */
+ if (Flags & NV_RX_MISSEDFRAME) {
+ np->stats.rx_missed_errors++;
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) {
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ if (Flags & NV_RX_CRCERR) {
+ np->stats.rx_crc_errors++;
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ if (Flags & NV_RX_OVERFLOW) {
+ np->stats.rx_over_errors++;
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ if (Flags & NV_RX_ERROR4) {
+ len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
+ if (len < 0) {
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ }
+ /* framing errors are soft errors. */
if (Flags & NV_RX_FRAMINGERR) {
if (Flags & NV_RX_SUBSTRACT1) {
len--;
}
- } else {
- np->stats.rx_errors++;
- goto next_pkt;
}
}
} else {
if (!(Flags & NV_RX2_DESCRIPTORVALID))
goto next_pkt;
- if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3|NV_RX2_ERROR4)) {
- np->stats.rx_errors++;
- goto next_pkt;
- }
- if (Flags & NV_RX2_CRCERR) {
- np->stats.rx_crc_errors++;
- np->stats.rx_errors++;
- goto next_pkt;
- }
- if (Flags & NV_RX2_OVERFLOW) {
- np->stats.rx_over_errors++;
- np->stats.rx_errors++;
- goto next_pkt;
- }
if (Flags & NV_RX2_ERROR) {
- /* framing errors are soft errors, the rest is fatal. */
+ if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) {
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ if (Flags & NV_RX2_CRCERR) {
+ np->stats.rx_crc_errors++;
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ if (Flags & NV_RX2_OVERFLOW) {
+ np->stats.rx_over_errors++;
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ if (Flags & NV_RX2_ERROR4) {
+ len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
+ if (len < 0) {
+ np->stats.rx_errors++;
+ goto next_pkt;
+ }
+ }
+ /* framing errors are soft errors */
if (Flags & NV_RX2_FRAMINGERR) {
if (Flags & NV_RX2_SUBSTRACT1) {
len--;
}
- } else {
- np->stats.rx_errors++;
- goto next_pkt;
}
}
Flags &= NV_RX2_CHECKSUMMASK;
@@ -1174,7 +1505,11 @@ static void nv_rx_process(struct net_dev
skb->protocol = eth_type_trans(skb, dev);
dprintk(KERN_DEBUG "%s: nv_rx_process: packet %d with %d bytes, proto %d accepted.\n",
dev->name, np->cur_rx, len, skb->protocol);
- netif_rx(skb);
+ if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT)) {
+ vlan_hwaccel_rx(skb, np->vlangrp, vlanflags & NV_RX3_VLAN_TAG_MASK);
+ } else {
+ netif_rx(skb);
+ }
dev->last_rx = jiffies;
np->stats.rx_packets++;
np->stats.rx_bytes += len;
@@ -1183,15 +1518,133 @@ next_pkt:
}
}
+static void set_bufsize(struct net_device *dev)
+{
+ struct fe_priv *np = netdev_priv(dev);
+
+ if (dev->mtu <= ETH_DATA_LEN)
+ np->rx_buf_sz = ETH_DATA_LEN + NV_RX_HEADERS;
+ else
+ np->rx_buf_sz = dev->mtu + NV_RX_HEADERS;
+}
+
/*
* nv_change_mtu: dev->change_mtu function
* Called with dev_base_lock held for read.
*/
static int nv_change_mtu(struct net_device *dev, int new_mtu)
{
- if (new_mtu > ETH_DATA_LEN)
+ struct fe_priv *np = netdev_priv(dev);
+ int old_mtu;
+
+ if (new_mtu < 64 || new_mtu > np->pkt_limit)
return -EINVAL;
+
+ old_mtu = dev->mtu;
dev->mtu = new_mtu;
+
+ /* return early if the buffer sizes will not change */
+ if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN)
+ return 0;
+ if (old_mtu == new_mtu)
+ return 0;
+
+ /* synchronized against open : rtnl_lock() held by caller */
+ if (netif_running(dev)) {
+ u8 __iomem *base = get_hwbase(dev);
+ /*
+ * It seems that the nic preloads valid ring entries into an
+ * internal buffer. The procedure for flushing everything is
+ * guessed, there is probably a simpler approach.
+ * Changing the MTU is a rare event, it shouldn't matter.
+ */
+ disable_irq(dev->irq);
+ spin_lock_bh(&dev->xmit_lock);
+ spin_lock(&np->lock);
+ /* stop engines */
+ nv_stop_rx(dev);
+ nv_stop_tx(dev);
+ nv_txrx_reset(dev);
+ /* drain rx queue */
+ nv_drain_rx(dev);
+ nv_drain_tx(dev);
+ /* reinit driver view of the rx queue */
+ nv_init_rx(dev);
+ nv_init_tx(dev);
+ /* alloc new rx buffers */
+ set_bufsize(dev);
+ if (nv_alloc_rx(dev)) {
+ if (!np->in_shutdown)
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
+ }
+ /* reinit nic view of the rx queue */
+ writel(np->rx_buf_sz, base + NvRegOffloadConfig);
+ writel((u32) np->ring_addr, base + NvRegRxRingPhysAddr);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
+ else
+ writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc_ex)), base + NvRegTxRingPhysAddr);
+ writel( ((RX_RING-1) << NVREG_RINGSZ_RXSHIFT) + ((TX_RING-1) << NVREG_RINGSZ_TXSHIFT),
+ base + NvRegRingSizes);
+ pci_push(base);
+ writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
+ pci_push(base);
+
+ /* restart rx engine */
+ nv_start_rx(dev);
+ nv_start_tx(dev);
+ spin_unlock(&np->lock);
+ spin_unlock_bh(&dev->xmit_lock);
+ enable_irq(dev->irq);
+ }
+ return 0;
+}
+
+static void nv_copy_mac_to_hw(struct net_device *dev)
+{
+ u8 __iomem *base = get_hwbase(dev);
+ u32 mac[2];
+
+ mac[0] = (dev->dev_addr[0] << 0) + (dev->dev_addr[1] << 8) +
+ (dev->dev_addr[2] << 16) + (dev->dev_addr[3] << 24);
+ mac[1] = (dev->dev_addr[4] << 0) + (dev->dev_addr[5] << 8);
+
+ writel(mac[0], base + NvRegMacAddrA);
+ writel(mac[1], base + NvRegMacAddrB);
+}
+
+/*
+ * nv_set_mac_address: dev->set_mac_address function
+ * Called with rtnl_lock() held.
+ */
+static int nv_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ struct sockaddr *macaddr = (struct sockaddr*)addr;
+
+ if(!is_valid_ether_addr(macaddr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ /* synchronized against open : rtnl_lock() held by caller */
+ memcpy(dev->dev_addr, macaddr->sa_data, ETH_ALEN);
+
+ if (netif_running(dev)) {
+ spin_lock_bh(&dev->xmit_lock);
+ spin_lock_irq(&np->lock);
+
+ /* stop rx engine */
+ nv_stop_rx(dev);
+
+ /* set mac address */
+ nv_copy_mac_to_hw(dev);
+
+ /* restart rx engine */
+ nv_start_rx(dev);
+ spin_unlock_irq(&np->lock);
+ spin_unlock_bh(&dev->xmit_lock);
+ } else {
+ nv_copy_mac_to_hw(dev);
+ }
return 0;
}
@@ -1201,8 +1654,8 @@ static int nv_change_mtu(struct net_devi
*/
static void nv_set_multicast(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
u32 addr[2];
u32 mask[2];
u32 pff;
@@ -1259,10 +1712,21 @@ static void nv_set_multicast(struct net_
spin_unlock_irq(&np->lock);
}
+/**
+ * nv_update_linkspeed: Setup the MAC according to the link partner
+ * @dev: Network device to be configured
+ *
+ * The function queries the PHY and checks if there is a link partner.
+ * If yes, then it sets up the MAC accordingly. Otherwise, the MAC is
+ * set to 10 MBit HD.
+ *
+ * The function returns 0 if there is no link partner and 1 if there is
+ * a good link partner.
+ */
static int nv_update_linkspeed(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
int adv, lpa;
int newls = np->linkspeed;
int newdup = np->duplex;
@@ -1285,6 +1749,25 @@ static int nv_update_linkspeed(struct ne
goto set_speed;
}
+ if (np->autoneg == 0) {
+ dprintk(KERN_DEBUG "%s: nv_update_linkspeed: autoneg off, PHY set to 0x%04x.\n",
+ dev->name, np->fixed_mode);
+ if (np->fixed_mode & LPA_100FULL) {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
+ newdup = 1;
+ } else if (np->fixed_mode & LPA_100HALF) {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
+ newdup = 0;
+ } else if (np->fixed_mode & LPA_10FULL) {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+ newdup = 1;
+ } else {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+ newdup = 0;
+ }
+ retval = 1;
+ goto set_speed;
+ }
/* check auto negotiation is complete */
if (!(mii_status & BMSR_ANEGCOMPLETE)) {
/* still in autonegotiation - configure nic for 10 MBit HD and wait. */
@@ -1302,7 +1785,7 @@ static int nv_update_linkspeed(struct ne
if ((control_1000 & ADVERTISE_1000FULL) &&
(status_1000 & LPA_1000FULL)) {
- dprintk(KERN_DEBUG "%s: nv_update_linkspeed: GBit ethernet detected.\n",
+ dprintk(KERN_DEBUG "%s: nv_update_linkspeed: GBit ethernet detected.\n",
dev->name);
newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_1000;
newdup = 1;
@@ -1361,9 +1844,9 @@ set_speed:
phyreg &= ~(PHY_HALF|PHY_100|PHY_1000);
if (np->duplex == 0)
phyreg |= PHY_HALF;
- if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
+ if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_100)
phyreg |= PHY_100;
- else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
+ else if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_1000)
phyreg |= PHY_1000;
writel(phyreg, base + NvRegPhyInterface);
@@ -1379,13 +1862,11 @@ set_speed:
static void nv_linkchange(struct net_device *dev)
{
if (nv_update_linkspeed(dev)) {
- if (netif_carrier_ok(dev)) {
- nv_stop_rx(dev);
- } else {
+ if (!netif_carrier_ok(dev)) {
netif_carrier_on(dev);
printk(KERN_INFO "%s: link up.\n", dev->name);
+ nv_start_rx(dev);
}
- nv_start_rx(dev);
} else {
if (netif_carrier_ok(dev)) {
netif_carrier_off(dev);
@@ -1397,7 +1878,7 @@ static void nv_linkchange(struct net_dev
static void nv_link_irq(struct net_device *dev)
{
- u8 *base = get_hwbase(dev);
+ u8 __iomem *base = get_hwbase(dev);
u32 miistat;
miistat = readl(base + NvRegMIIStatus);
@@ -1412,8 +1893,8 @@ static void nv_link_irq(struct net_devic
static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *) data;
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
u32 events;
int i;
@@ -1427,22 +1908,18 @@ static irqreturn_t nv_nic_irq(int foo, v
if (!(events & np->irqmask))
break;
- if (events & (NVREG_IRQ_TX1|NVREG_IRQ_TX2|NVREG_IRQ_TX_ERR)) {
+ spin_lock(&np->lock);
+ nv_tx_done(dev);
+ spin_unlock(&np->lock);
+
+ nv_rx_process(dev);
+ if (nv_alloc_rx(dev)) {
spin_lock(&np->lock);
- nv_tx_done(dev);
+ if (!np->in_shutdown)
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
spin_unlock(&np->lock);
}
-
- if (events & (NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF)) {
- nv_rx_process(dev);
- if (nv_alloc_rx(dev)) {
- spin_lock(&np->lock);
- if (!np->in_shutdown)
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock(&np->lock);
- }
- }
-
+
if (events & NVREG_IRQ_LINK) {
spin_lock(&np->lock);
nv_link_irq(dev);
@@ -1484,8 +1961,8 @@ static irqreturn_t nv_nic_irq(int foo, v
static void nv_do_nic_poll(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
disable_irq(dev->irq);
/* FIXME: Do we need synchronize_irq(dev->irq) here? */
@@ -1499,10 +1976,313 @@ static void nv_do_nic_poll(unsigned long
enable_irq(dev->irq);
}
-static int nv_open(struct net_device *dev)
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void nv_poll_controller(struct net_device *dev)
+{
+ nv_do_nic_poll((unsigned long) dev);
+}
+#endif
+
+static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ strcpy(info->driver, "forcedeth");
+ strcpy(info->version, FORCEDETH_VERSION);
+ strcpy(info->bus_info, pci_name(np->pci_dev));
+}
+
+static void nv_get_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ wolinfo->supported = WAKE_MAGIC;
+
+ spin_lock_irq(&np->lock);
+ if (np->wolenabled)
+ wolinfo->wolopts = WAKE_MAGIC;
+ spin_unlock_irq(&np->lock);
+}
+
+static int nv_set_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
+
+ spin_lock_irq(&np->lock);
+ if (wolinfo->wolopts == 0) {
+ writel(0, base + NvRegWakeUpFlags);
+ np->wolenabled = 0;
+ }
+ if (wolinfo->wolopts & WAKE_MAGIC) {
+ writel(NVREG_WAKEUPFLAGS_ENABLE, base + NvRegWakeUpFlags);
+ np->wolenabled = 1;
+ }
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static int nv_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ int adv;
+
+ spin_lock_irq(&np->lock);
+ ecmd->port = PORT_MII;
+ if (!netif_running(dev)) {
+ /* We do not track link speed / duplex setting if the
+ * interface is disabled. Force a link check */
+ nv_update_linkspeed(dev);
+ }
+ switch(np->linkspeed & (NVREG_LINKSPEED_MASK)) {
+ case NVREG_LINKSPEED_10:
+ ecmd->speed = SPEED_10;
+ break;
+ case NVREG_LINKSPEED_100:
+ ecmd->speed = SPEED_100;
+ break;
+ case NVREG_LINKSPEED_1000:
+ ecmd->speed = SPEED_1000;
+ break;
+ }
+ ecmd->duplex = DUPLEX_HALF;
+ if (np->duplex)
+ ecmd->duplex = DUPLEX_FULL;
+
+ ecmd->autoneg = np->autoneg;
+
+ ecmd->advertising = ADVERTISED_MII;
+ if (np->autoneg) {
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+ } else {
+ adv = np->fixed_mode;
+ }
+ if (adv & ADVERTISE_10HALF)
+ ecmd->advertising |= ADVERTISED_10baseT_Half;
+ if (adv & ADVERTISE_10FULL)
+ ecmd->advertising |= ADVERTISED_10baseT_Full;
+ if (adv & ADVERTISE_100HALF)
+ ecmd->advertising |= ADVERTISED_100baseT_Half;
+ if (adv & ADVERTISE_100FULL)
+ ecmd->advertising |= ADVERTISED_100baseT_Full;
+ if (np->autoneg && np->gigabit == PHY_GIGABIT) {
+ adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
+ if (adv & ADVERTISE_1000FULL)
+ ecmd->advertising |= ADVERTISED_1000baseT_Full;
+ }
+
+ ecmd->supported = (SUPPORTED_Autoneg |
+ SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_MII);
+ if (np->gigabit == PHY_GIGABIT)
+ ecmd->supported |= SUPPORTED_1000baseT_Full;
+
+ ecmd->phy_address = np->phyaddr;
+ ecmd->transceiver = XCVR_EXTERNAL;
+
+ /* ignore maxtxpkt, maxrxpkt for now */
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct fe_priv *np = netdev_priv(dev);
+
+ if (ecmd->port != PORT_MII)
+ return -EINVAL;
+ if (ecmd->transceiver != XCVR_EXTERNAL)
+ return -EINVAL;
+ if (ecmd->phy_address != np->phyaddr) {
+ /* TODO: support switching between multiple phys. Should be
+ * trivial, but not enabled due to lack of test hardware. */
+ return -EINVAL;
+ }
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ u32 mask;
+
+ mask = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
+ if (np->gigabit == PHY_GIGABIT)
+ mask |= ADVERTISED_1000baseT_Full;
+
+ if ((ecmd->advertising & mask) == 0)
+ return -EINVAL;
+
+ } else if (ecmd->autoneg == AUTONEG_DISABLE) {
+ /* Note: autonegotiation disable, speed 1000 intentionally
+ * forbidden - noone should need that. */
+
+ if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
+ return -EINVAL;
+ if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&np->lock);
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ int adv, bmcr;
+
+ np->autoneg = 1;
+
+ /* advertise only what has been requested */
+ adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (ecmd->advertising & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (ecmd->advertising & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (ecmd->advertising & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (ecmd->advertising & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
+
+ if (np->gigabit == PHY_GIGABIT) {
+ adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
+ adv &= ~ADVERTISE_1000FULL;
+ if (ecmd->advertising & ADVERTISED_1000baseT_Full)
+ adv |= ADVERTISE_1000FULL;
+ mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
+ }
+
+ bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+ } else {
+ int adv, bmcr;
+
+ np->autoneg = 0;
+
+ adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_HALF)
+ adv |= ADVERTISE_10HALF;
+ if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_FULL)
+ adv |= ADVERTISE_10FULL;
+ if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_HALF)
+ adv |= ADVERTISE_100HALF;
+ if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_FULL)
+ adv |= ADVERTISE_100FULL;
+ mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
+ np->fixed_mode = adv;
+
+ if (np->gigabit == PHY_GIGABIT) {
+ adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
+ adv &= ~ADVERTISE_1000FULL;
+ mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
+ }
+
+ bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+ bmcr |= ~(BMCR_ANENABLE|BMCR_SPEED100|BMCR_FULLDPLX);
+ if (adv & (ADVERTISE_10FULL|ADVERTISE_100FULL))
+ bmcr |= BMCR_FULLDPLX;
+ if (adv & (ADVERTISE_100HALF|ADVERTISE_100FULL))
+ bmcr |= BMCR_SPEED100;
+ mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+ if (netif_running(dev)) {
+ /* Wait a bit and then reconfigure the nic. */
+ udelay(10);
+ nv_linkchange(dev);
+ }
+ }
+ spin_unlock_irq(&np->lock);
+
+ return 0;
+}
+
+#define FORCEDETH_REGS_VER 1
+#define FORCEDETH_REGS_SIZE 0x400 /* 256 32-bit registers */
+
+static int nv_get_regs_len(struct net_device *dev)
+{
+ return FORCEDETH_REGS_SIZE;
+}
+
+static void nv_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
+ u32 *rbuf = buf;
+ int i;
+
+ regs->version = FORCEDETH_REGS_VER;
+ spin_lock_irq(&np->lock);
+ for (i=0;i<FORCEDETH_REGS_SIZE/sizeof(u32);i++)
+ rbuf[i] = readl(base + i*sizeof(u32));
+ spin_unlock_irq(&np->lock);
+}
+
+static int nv_nway_reset(struct net_device *dev)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ int ret;
+
+ spin_lock_irq(&np->lock);
+ if (np->autoneg) {
+ int bmcr;
+
+ bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ spin_unlock_irq(&np->lock);
+
+ return ret;
+}
+
+static struct ethtool_ops ops = {
+ .get_drvinfo = nv_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_wol = nv_get_wol,
+ .set_wol = nv_set_wol,
+ .get_settings = nv_get_settings,
+ .set_settings = nv_set_settings,
+ .get_regs_len = nv_get_regs_len,
+ .get_regs = nv_get_regs,
+ .nway_reset = nv_nway_reset,
+};
+
+static void nv_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
{
struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+
+ spin_lock_irq(&np->lock);
+
+ /* save vlan group */
+ np->vlangrp = grp;
+
+ if (grp) {
+ /* enable vlan on MAC */
+ np->txrxctl_bits |= NVREG_TXRXCTL_VLANSTRIP | NVREG_TXRXCTL_VLANINS;
+ } else {
+ /* disable vlan on MAC */
+ np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANSTRIP;
+ np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANINS;
+ }
+
+ writel(np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
+
+ spin_unlock_irq(&np->lock);
+};
+
+static void nv_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+ /* nothing to do */
+};
+
+static int nv_open(struct net_device *dev)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
int ret, oom, i;
dprintk(KERN_DEBUG "nv_open: begin\n");
@@ -1521,6 +2301,7 @@ static int nv_open(struct net_device *de
writel(0, base + NvRegAdapterControl);
/* 2) initialize descriptor rings */
+ set_bufsize(dev);
oom = nv_init_ring(dev);
writel(0, base + NvRegLinkSpeed);
@@ -1531,32 +2312,24 @@ static int nv_open(struct net_device *de
np->in_shutdown = 0;
/* 3) set mac address */
- {
- u32 mac[2];
-
- mac[0] = (dev->dev_addr[0] << 0) + (dev->dev_addr[1] << 8) +
- (dev->dev_addr[2] << 16) + (dev->dev_addr[3] << 24);
- mac[1] = (dev->dev_addr[4] << 0) + (dev->dev_addr[5] << 8);
-
- writel(mac[0], base + NvRegMacAddrA);
- writel(mac[1], base + NvRegMacAddrB);
- }
+ nv_copy_mac_to_hw(dev);
/* 4) give hw rings */
writel((u32) np->ring_addr, base + NvRegRxRingPhysAddr);
- writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
+ else
+ writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc_ex)), base + NvRegTxRingPhysAddr);
writel( ((RX_RING-1) << NVREG_RINGSZ_RXSHIFT) + ((TX_RING-1) << NVREG_RINGSZ_TXSHIFT),
base + NvRegRingSizes);
/* 5) continue setup */
- np->linkspeed = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
- np->duplex = 0;
-
writel(np->linkspeed, base + NvRegLinkSpeed);
writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3);
- writel(np->desc_ver, base + NvRegTxRxControl);
+ writel(np->txrxctl_bits, base + NvRegTxRxControl);
+ writel(np->vlanctl_bits, base + NvRegVlanControl);
pci_push(base);
- writel(NVREG_TXRXCTL_BIT1|np->desc_ver, base + NvRegTxRxControl);
+ writel(NVREG_TXRXCTL_BIT1|np->txrxctl_bits, base + NvRegTxRxControl);
reg_delay(dev, NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31, NVREG_UNKSETUP5_BIT31,
NV_SETUP5_DELAY, NV_SETUP5_DELAYMAX,
KERN_INFO "open: SetupReg5, Bit 31 remained off\n");
@@ -1569,14 +2342,21 @@ static int nv_open(struct net_device *de
writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
writel(readl(base + NvRegTransmitterStatus), base + NvRegTransmitterStatus);
writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
- writel(NVREG_OFFLOAD_NORMAL, base + NvRegOffloadConfig);
+ writel(np->rx_buf_sz, base + NvRegOffloadConfig);
writel(readl(base + NvRegReceiverStatus), base + NvRegReceiverStatus);
get_random_bytes(&i, sizeof(i));
writel(NVREG_RNDSEED_FORCE | (i&NVREG_RNDSEED_MASK), base + NvRegRandomSeed);
writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
- writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
+ if (poll_interval == -1) {
+ if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT)
+ writel(NVREG_POLL_DEFAULT_THROUGHPUT, base + NvRegPollingInterval);
+ else
+ writel(NVREG_POLL_DEFAULT_CPU, base + NvRegPollingInterval);
+ }
+ else
+ writel(poll_interval & 0xFFFF, base + NvRegPollingInterval);
writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
writel((np->phyaddr << NVREG_ADAPTCTL_PHYSHIFT)|NVREG_ADAPTCTL_PHYVALID|NVREG_ADAPTCTL_RUNNING,
base + NvRegAdapterControl);
@@ -1620,6 +2400,9 @@ static int nv_open(struct net_device *de
writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
dprintk(KERN_INFO "startup: got 0x%08x.\n", miistat);
}
+ /* set linkspeed to invalid value, thus force nv_update_linkspeed
+ * to init hw */
+ np->linkspeed = 0;
ret = nv_update_linkspeed(dev);
nv_start_rx(dev);
nv_start_tx(dev);
@@ -1642,8 +2425,8 @@ out_drain:
static int nv_close(struct net_device *dev)
{
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base;
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base;
spin_lock_irq(&np->lock);
np->in_shutdown = 1;
@@ -1674,6 +2457,12 @@ static int nv_close(struct net_device *d
if (np->wolenabled)
nv_start_rx(dev);
+ /* special op: write back the misordered MAC address - otherwise
+ * the next nv_probe would see a wrong address.
+ */
+ writel(np->orig_mac[0], base + NvRegMacAddrA);
+ writel(np->orig_mac[1], base + NvRegMacAddrB);
+
/* FIXME: power down nic */
return 0;
@@ -1684,7 +2473,7 @@ static int __devinit nv_probe(struct pci
struct net_device *dev;
struct fe_priv *np;
unsigned long addr;
- u8 *base;
+ u8 __iomem *base;
int err, i;
dev = alloc_etherdev(sizeof(struct fe_priv));
@@ -1692,7 +2481,7 @@ static int __devinit nv_probe(struct pci
if (!dev)
goto out;
- np = get_nvpriv(dev);
+ np = netdev_priv(dev);
np->pci_dev = pci_dev;
spin_lock_init(&np->lock);
SET_MODULE_OWNER(dev);
@@ -1738,30 +2527,80 @@ static int __devinit nv_probe(struct pci
}
/* handle different descriptor versions */
- if (pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_1 ||
- pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_2 ||
- pci_dev->device == PCI_DEVICE_ID_NVIDIA_NVENET_3)
- np->desc_ver = DESC_VER_1;
- else
+ if (id->driver_data & DEV_HAS_HIGH_DMA) {
+ /* packet format 3: supports 40-bit addressing */
+ np->desc_ver = DESC_VER_3;
+ if (pci_set_dma_mask(pci_dev, 0x0000007fffffffffULL)) {
+ printk(KERN_INFO "forcedeth: 64-bit DMA failed, using 32-bit addressing for device %s.\n",
+ pci_name(pci_dev));
+ } else {
+ dev->features |= NETIF_F_HIGHDMA;
+ }
+ np->txrxctl_bits = NVREG_TXRXCTL_DESC_3;
+ } else if (id->driver_data & DEV_HAS_LARGEDESC) {
+ /* packet format 2: supports jumbo frames */
np->desc_ver = DESC_VER_2;
+ np->txrxctl_bits = NVREG_TXRXCTL_DESC_2;
+ } else {
+ /* original packet format */
+ np->desc_ver = DESC_VER_1;
+ np->txrxctl_bits = NVREG_TXRXCTL_DESC_1;
+ }
+
+ np->pkt_limit = NV_PKTLIMIT_1;
+ if (id->driver_data & DEV_HAS_LARGEDESC)
+ np->pkt_limit = NV_PKTLIMIT_2;
+
+ if (id->driver_data & DEV_HAS_CHECKSUM) {
+ np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK;
+ dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG;
+#ifdef NETIF_F_TSO
+ dev->features |= NETIF_F_TSO;
+#endif
+ }
+
+ np->vlanctl_bits = 0;
+ if (id->driver_data & DEV_HAS_VLAN) {
+ np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
+ dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
+ dev->vlan_rx_register = nv_vlan_rx_register;
+ dev->vlan_rx_kill_vid = nv_vlan_rx_kill_vid;
+ }
err = -ENOMEM;
- dev->base_addr = (unsigned long) ioremap(addr, NV_PCI_REGSZ);
- if (!dev->base_addr)
+ np->base = ioremap(addr, NV_PCI_REGSZ);
+ if (!np->base)
goto out_relreg;
+ dev->base_addr = (unsigned long)np->base;
+
dev->irq = pci_dev->irq;
- np->rx_ring = pci_alloc_consistent(pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING),
- &np->ring_addr);
- if (!np->rx_ring)
- goto out_unmap;
- np->tx_ring = &np->rx_ring[RX_RING];
+
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ np->rx_ring.orig = pci_alloc_consistent(pci_dev,
+ sizeof(struct ring_desc) * (RX_RING + TX_RING),
+ &np->ring_addr);
+ if (!np->rx_ring.orig)
+ goto out_unmap;
+ np->tx_ring.orig = &np->rx_ring.orig[RX_RING];
+ } else {
+ np->rx_ring.ex = pci_alloc_consistent(pci_dev,
+ sizeof(struct ring_desc_ex) * (RX_RING + TX_RING),
+ &np->ring_addr);
+ if (!np->rx_ring.ex)
+ goto out_unmap;
+ np->tx_ring.ex = &np->rx_ring.ex[RX_RING];
+ }
dev->open = nv_open;
dev->stop = nv_close;
dev->hard_start_xmit = nv_start_xmit;
dev->get_stats = nv_get_stats;
dev->change_mtu = nv_change_mtu;
+ dev->set_mac_address = nv_set_mac_address;
dev->set_multicast_list = nv_set_multicast;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = nv_poll_controller;
+#endif
SET_ETHTOOL_OPS(dev, &ops);
dev->tx_timeout = nv_tx_timeout;
dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
@@ -1805,18 +2644,15 @@ static int __devinit nv_probe(struct pci
np->wolenabled = 0;
if (np->desc_ver == DESC_VER_1) {
- np->tx_flags = NV_TX_LASTPACKET|NV_TX_VALID;
- if (id->driver_data & DEV_NEED_LASTPACKET1)
- np->tx_flags |= NV_TX_LASTPACKET1;
+ np->tx_flags = NV_TX_VALID;
} else {
- np->tx_flags = NV_TX2_LASTPACKET|NV_TX2_VALID;
- if (id->driver_data & DEV_NEED_LASTPACKET1)
- np->tx_flags |= NV_TX2_LASTPACKET1;
- }
- if (id->driver_data & DEV_IRQMASK_1)
- np->irqmask = NVREG_IRQMASK_WANTED_1;
- if (id->driver_data & DEV_IRQMASK_2)
- np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->tx_flags = NV_TX2_VALID;
+ }
+ if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT)
+ np->irqmask = NVREG_IRQMASK_THROUGHPUT;
+ else
+ np->irqmask = NVREG_IRQMASK_CPU;
+
if (id->driver_data & DEV_NEED_TIMERIRQ)
np->irqmask |= NVREG_IRQ_TIMER;
if (id->driver_data & DEV_NEED_LINKTIMER) {
@@ -1829,16 +2665,17 @@ static int __devinit nv_probe(struct pci
}
/* find a suitable phy */
- for (i = 1; i < 32; i++) {
+ for (i = 1; i <= 32; i++) {
int id1, id2;
+ int phyaddr = i & 0x1F;
spin_lock_irq(&np->lock);
- id1 = mii_rw(dev, i, MII_PHYSID1, MII_READ);
+ id1 = mii_rw(dev, phyaddr, MII_PHYSID1, MII_READ);
spin_unlock_irq(&np->lock);
if (id1 < 0 || id1 == 0xffff)
continue;
spin_lock_irq(&np->lock);
- id2 = mii_rw(dev, i, MII_PHYSID2, MII_READ);
+ id2 = mii_rw(dev, phyaddr, MII_PHYSID2, MII_READ);
spin_unlock_irq(&np->lock);
if (id2 < 0 || id2 == 0xffff)
continue;
@@ -1846,23 +2683,24 @@ static int __devinit nv_probe(struct pci
id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
dprintk(KERN_DEBUG "%s: open: Found PHY %04x:%04x at address %d.\n",
- pci_name(pci_dev), id1, id2, i);
- np->phyaddr = i;
+ pci_name(pci_dev), id1, id2, phyaddr);
+ np->phyaddr = phyaddr;
np->phy_oui = id1 | id2;
break;
}
- if (i == 32) {
- /* PHY in isolate mode? No phy attached and user wants to
- * test loopback? Very odd, but can be correct.
- */
+ if (i == 33) {
printk(KERN_INFO "%s: open: Could not find a valid PHY.\n",
- pci_name(pci_dev));
+ pci_name(pci_dev));
+ goto out_freering;
}
+
+ /* reset it */
+ phy_init(dev);
- if (i != 32) {
- /* reset it */
- phy_init(dev);
- }
+ /* set default link speed settings */
+ np->linkspeed = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+ np->duplex = 0;
+ np->autoneg = 1;
err = register_netdev(dev);
if (err) {
@@ -1876,8 +2714,12 @@ static int __devinit nv_probe(struct pci
return 0;
out_freering:
- pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING),
- np->rx_ring, np->ring_addr);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING),
+ np->rx_ring.orig, np->ring_addr);
+ else
+ pci_free_consistent(np->pci_dev, sizeof(struct ring_desc_ex) * (RX_RING + TX_RING),
+ np->rx_ring.ex, np->ring_addr);
pci_set_drvdata(pci_dev, NULL);
out_unmap:
iounmap(get_hwbase(dev));
@@ -1894,19 +2736,15 @@ out:
static void __devexit nv_remove(struct pci_dev *pci_dev)
{
struct net_device *dev = pci_get_drvdata(pci_dev);
- struct fe_priv *np = get_nvpriv(dev);
- u8 *base = get_hwbase(dev);
+ struct fe_priv *np = netdev_priv(dev);
unregister_netdev(dev);
- /* special op: write back the misordered MAC address - otherwise
- * the next nv_probe would see a wrong address.
- */
- writel(np->orig_mac[0], base + NvRegMacAddrA);
- writel(np->orig_mac[1], base + NvRegMacAddrB);
-
/* free all structures */
- pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING), np->rx_ring, np->ring_addr);
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING), np->rx_ring.orig, np->ring_addr);
+ else
+ pci_free_consistent(np->pci_dev, sizeof(struct ring_desc_ex) * (RX_RING + TX_RING), np->rx_ring.ex, np->ring_addr);
iounmap(get_hwbase(dev));
pci_release_regions(pci_dev);
pci_disable_device(pci_dev);
@@ -1916,81 +2754,64 @@ static void __devexit nv_remove(struct p
static struct pci_device_id pci_tbl[] = {
{ /* nForce Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_1,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_1),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
},
{ /* nForce2 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_2),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
},
{ /* nForce3 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_3,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_3),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
},
{ /* nForce3 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_4,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_4),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
},
{ /* nForce3 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_5,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_5),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
},
{ /* nForce3 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_6,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_6),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
},
{ /* nForce3 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_7,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_7),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
},
{ /* CK804 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_8,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_8),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA,
},
{ /* CK804 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_9,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_9),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA,
},
{ /* MCP04 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_10,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_10),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA,
},
{ /* MCP04 Ethernet Controller */
- .vendor = PCI_VENDOR_ID_NVIDIA,
- .device = PCI_DEVICE_ID_NVIDIA_NVENET_11,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_11),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA,
+ },
+ { /* MCP51 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_12),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA,
+ },
+ { /* MCP51 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_13),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA,
+ },
+ { /* MCP55 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_14),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN,
+ },
+ { /* MCP55 Ethernet Controller */
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15),
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN,
},
{0,},
};
@@ -2016,7 +2837,11 @@ static void __exit exit_nic(void)
module_param(max_interrupt_work, int, 0);
MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt");
-
+module_param(optimization_mode, int, 0);
+MODULE_PARM_DESC(optimization_mode, "In throughput mode (0), every tx & rx packet will generate an interrupt. In CPU mode (1), interrupts are controlled by a timer.");
+module_param(poll_interval, int, 0);
+MODULE_PARM_DESC(poll_interval, "Interval determines how frequent timer interrupt is generated by [(time_in_micro_secs * 100) / (2^10)]. Min is 0 and Max is 65535.");
+
MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
MODULE_LICENSE("GPL");
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 1924277..8bc6afd 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1045,7 +1045,11 @@ #define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267
+#define PCI_DEVICE_ID_NVIDIA_NVENET_12 0x0268
+#define PCI_DEVICE_ID_NVIDIA_NVENET_13 0x0269
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E
+#define PCI_DEVICE_ID_NVIDIA_NVENET_14 0x0372
+#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F
--
1.3.3
^ permalink raw reply related
* Re: [RFC PATCH 1/2] Hardware button support for Wireless cards: radiobtn
From: Francois Romieu @ 2006-05-30 21:43 UTC (permalink / raw)
To: Ivo van Doorn; +Cc: netdev
In-Reply-To: <200605291758.07930.IvDoorn@gmail.com>
Ivo van Doorn <ivdoorn@gmail.com> :
[...]
> diff --git a/drivers/input/misc/radiobtn.c b/drivers/input/misc/radiobtn.c
> new file mode 100644
> index 0000000..8d3b84a
> --- /dev/null
> +++ b/drivers/input/misc/radiobtn.c
[...]
> +void radiobtn_poll(unsigned long data)
static ?
[...]
> +int radiobtn_register_device(struct radio_button *radiobtn)
> +{
> + int status;
> +
> + /*
> + * Check if all mandatory fields have been set.
> + */
> + if (radiobtn->poll_delay == 0 || radiobtn->button_poll == NULL)
> + return -EINVAL;
> +
> + /*
> + * Allocate, initialize and register input device.
> + */
> + radiobtn->input_dev = input_allocate_device();
> + if (!radiobtn->input_dev) {
> + printk(KERN_ERR "Failed to allocate input device %s.\n",
> + radiobtn->dev_name);
> + return -ENOMEM;
> + }
> +
> + radiobtn->input_dev->name = "Radio button";
> + radiobtn->input_dev->phys = strcat("radiobtn/", radiobtn->dev_name);
The first parameter of strcat() must be big enough to contain the whole
string.
--
Ueimor
^ permalink raw reply
* OLPC (One Laptop Per Child) Developer's program.
From: Jim Gettys @ 2006-05-30 21:42 UTC (permalink / raw)
To: netdev, linux-pm, LinuxBIOS, OLPC Developer's List
As you know, we've said we were going to have a developer's program. You
can find more information, including how to apply for boards at:
http://wiki.laptop.org/index.php/Developers_Program
Note that these are bare PC prototype boards, not packaged machines.
Jim Gettys
--
Jim Gettys
One Laptop Per Child
^ permalink raw reply
* Re: Sign-off for the IP1000A driver before inclusion
From: Francois Romieu @ 2006-05-30 21:29 UTC (permalink / raw)
To: =?unknown-8bit?B?amVzc2Uo5bu66IiIKQ==?=
Cc: Pekka J Enberg, Andrew Morton, dvrabel, linux-kernel, netdev,
david
In-Reply-To: <026801c683c8$e3f63860$4964a8c0@icplus.com.tw>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=unknown-8bit, Size: 18753 bytes --]
jesse\(建興\) <jesse@icplus.com.tw> :
[...]
> --Remove Threshold comfig and RxDMAInt from ipg_io_config(). Remove relative
> define form ipg.h
Naïve question: why ?
Hardware assisted Rx irq mitigation is cool (TM).
> --Remove and Rewrite ipg_config_autoneg() function.
- Is there any hardware reason why the bits in MAC_CTRL are set one (*1*) at
a time through repeated use of iowrite32(ioread32(ioaddr+MAC_CTRL), ...) ?
I already changed it in a7302ea588bccc3df88bc8162af8b4d4acd0e380 so I
wonder if it was reverted by accident or not.
- The rewrite messed up the codingstyle (switch/case + some spaces/tabs +
see below)
[...]
struct ipg_nic_private* sp=netdev_priv(dev);
void __iomem *ioaddr = sp->ioaddr;
u8 phyctrl;
u32 asicctrl;
int fiber;
int gig;
int fullduplex;
int txflowcontrol;
int rxflowcontrol;
[...]
/* Configure full duplex, and flow control. */
if (fullduplex == 1)
{
/* Configure IPG for full duplex operation. */
printk(KERN_INFO "setting full duplex, ");
iowrite32(ioread32(ioaddr+MAC_CTRL) |IPG_MC_DUPLEX_SELECT_FD,ioaddr+MAC_CTRL);
if (txflowcontrol == 1)
{
printk("TX flow control");
iowrite32(ioread32(ioaddr+MAC_CTRL) |IPG_MC_TX_FLOW_CONTROL_ENABLE,ioaddr+MAC_CTRL);
} else
{
- At the top of ipg_config_autoneg(), I noticed that:
phyctrl = ioread8(ioaddr + PHY_CTRL);
turned into:
phyctrl = ioread32(ioaddr +PHY_CTRL);
I changed it back to ioread8() since that's what the driver uses everywhere
else when reading PHY_CTRL. Is it correct ?
Updated patch against fe0dd7dcecf6fbcc3b34099e064f7c0065caef71 below.
The patch has not been committed in the git tree so far.
diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c
index 754ddb5..fe6ea02 100644
--- a/drivers/net/ipg.c
+++ b/drivers/net/ipg.c
@@ -5,6 +5,7 @@
* IC Plus IP1000 Gigabit Ethernet Adapter Linux Driver v2.01
* by IC Plus Corp. 2003
*
+ * Initiator:
* Craig Rich
* Sundance Technology, Inc.
* 1485 Saratoga Avenue
@@ -13,6 +14,14 @@
* 408 873 4117
* www.sundanceti.com
* craig_rich@sundanceti.com
+ *
+ * Current Maintainer:
+ * Sorbica Shieh.
+ * 10F, No.47, Lane 2, Kwang-Fu RD.
+ * Sec. 2, Hsin-Chu, Taiwan, R.O.C.
+ * http://www.icplus.com.tw
+ * sorbica@icplus.com.tw
+ * Signed-off-by: Sorbica Shieh <sorbica@icplus.com.tw>
*/
#include <linux/crc32.h>
#include <linux/ethtool.h>
@@ -478,13 +487,6 @@ static int ipg_config_autoneg(struct net
void __iomem *ioaddr = sp->ioaddr;
u32 asicctrl;
u32 mac_ctl;
- int phyaddr = 0;
- u16 status = 0;
- u16 advertisement;
- u16 linkpartner_ability;
- u16 gigadvertisement;
- u16 giglinkpartner_ability;
- u16 techabilities;
int fiber;
int gig;
int fullduplex;
@@ -511,14 +513,12 @@ static int ipg_config_autoneg(struct net
*/
sp->tenmbpsmode = 0;
- printk("Link speed = ");
+ printk(KERN_INFO "Link speed = ");
/* Determine actual speed of operation. */
switch (phyctrl & IPG_PC_LINK_SPEED) {
case IPG_PC_LINK_SPEED_10MBPS:
printk("10Mbps.\n");
- printk(KERN_INFO "%s: 10Mbps operational mode enabled.\n",
- dev->name);
sp->tenmbpsmode = 1;
break;
case IPG_PC_LINK_SPEED_100MBPS:
@@ -530,282 +530,49 @@ static int ipg_config_autoneg(struct net
break;
default:
printk("undefined!\n");
- }
-
- fiber = ipg_sti_fiber_detect(dev);
-
- /* Determine if auto-negotiation resolution is necessary.
- * First check for fiber based media 10/100 media.
- */
- if ((fiber == 1) &&
- (asicctrl & (IPG_AC_PHY_SPEED10 | IPG_AC_PHY_SPEED100))) {
- printk(KERN_INFO
- "%s: Fiber based PHY, setting full duplex, no flow control.\n",
- dev->name);
-
- mac_ctl = ioread32(ioaddr + MAC_CTRL);
- mac_ctl |= IPG_MC_DUPLEX_SELECT_FD;
- mac_ctl &= ~IPG_MC_TX_FLOW_CONTROL_ENABLE;
- mac_ctl &= ~IPG_MC_RX_FLOW_CONTROL_ENABLE;
-
- iowrite32(IPG_MC_RSVD_MASK & mac_ctl, ioaddr + MAC_CTRL);
-
return 0;
}
- /* Determine if PHY is auto-negotiation capable. */
- phyaddr = ipg_find_phyaddr(dev);
-
- if (phyaddr == -1) {
- printk(KERN_INFO
- "%s: Error on read to GMII/MII Status register.\n",
- dev->name);
- return -EILSEQ;
- }
- sp->mii_if.phy_id = phyaddr;
-
- IPG_DEBUG_MSG("GMII/MII PHY address = %x\n", phyaddr);
-
- status = mdio_read(dev, phyaddr, MII_BMSR);
-
- printk("PHYStatus = %x \n", status);
- if ((status & BMSR_ANEGCAPABLE) == 0) {
- printk(KERN_INFO
- "%s: Error PHY unable to perform auto-negotiation.\n",
- dev->name);
- return -EILSEQ;
- }
-
- advertisement = mdio_read(dev, phyaddr, MII_ADVERTISE);
- linkpartner_ability = mdio_read(dev, phyaddr, MII_LPA);
-
- printk("PHYadvertisement=%x LinkPartner=%x \n", advertisement,
- linkpartner_ability);
- if ((advertisement == 0xFFFF) || (linkpartner_ability == 0xFFFF)) {
- printk(KERN_INFO
- "%s: Error on read to GMII/MII registers 4 and/or 5.\n",
- dev->name);
- return -EILSEQ;
+ if (phyctrl & IPG_PC_DUPLEX_STATUS) {
+ fullduplex = 1;
+ txflowcontrol = 1;
+ rxflowcontrol = 1;
}
- fiber = ipg_tmi_fiber_detect(dev, phyaddr);
+ mac_ctl = ioread32(ioaddr + MAC_CTRL);
- /* Resolve full/half duplex if 1000BASE-X. */
- if ((gig == 1) && (fiber == 1)) {
- int bmcr;
- /*
- * Compare the full duplex bits in the GMII registers
- * for the local device, and the link partner. If these
- * bits are logic 1 in both registers, configure the
- * IPG for full duplex operation.
- */
- bmcr = mdio_read(dev, phyaddr, MII_BMCR);
+ /* Configure full duplex, and flow control. */
+ if (fullduplex == 1) {
+ /* Configure IPG for full duplex operation. */
+ printk(KERN_INFO "setting full duplex, ");
- if ((advertisement & ADVERTISE_1000XFULL) ==
- (linkpartner_ability & ADVERTISE_1000XFULL)) {
- fullduplex = 1;
+ mac_ctl |= IPG_MC_DUPLEX_SELECT_FD;
- /* In 1000BASE-X using IPG's internal PCS
- * layer, so write to the GMII duplex bit.
- */
- bmcr |= ADVERTISE_1000HALF; // Typo ?
+ if (txflowcontrol == 1) {
+ printk("TX flow control");
+ mac_ctl |= IPG_MC_TX_FLOW_CONTROL_ENABLE;
} else {
- fullduplex = 0;
-
- /* In 1000BASE-X using IPG's internal PCS
- * layer, so write to the GMII duplex bit.
- */
- bmcr &= ~ADVERTISE_1000HALF; // Typo ?
+ printk("no TX flow control");
+ mac_ctl &= ~IPG_MC_TX_FLOW_CONTROL_ENABLE;
}
- mdio_write(dev, phyaddr, MII_BMCR, bmcr);
- }
- /* Resolve full/half duplex if 1000BASE-T. */
- if ((gig == 1) && (fiber == 0)) {
- /* Read the 1000BASE-T "Control" and "Status"
- * registers which represent the advertised and
- * link partner abilities exchanged via next page
- * transfers.
- */
- gigadvertisement = mdio_read(dev, phyaddr, MII_CTRL1000);
- giglinkpartner_ability = mdio_read(dev, phyaddr, MII_STAT1000);
-
- /* Compare the full duplex bits in the 1000BASE-T GMII
- * registers for the local device, and the link partner.
- * If these bits are logic 1 in both registers, configure
- * the IPG for full duplex operation.
- */
- if ((gigadvertisement & ADVERTISE_1000FULL) &&
- (giglinkpartner_ability & ADVERTISE_1000FULL)) {
- fullduplex = 1;
+ if (rxflowcontrol == 1) {
+ printk(", RX flow control.");
+ mac_ctl |= IPG_MC_RX_FLOW_CONTROL_ENABLE;
} else {
- fullduplex = 0;
- }
- }
-
- /* Resolve full/half duplex for 10/100BASE-T. */
- if (gig == 0) {
- /* Autonegotiation Priority Resolution algorithm, as defined in
- * IEEE 802.3 Annex 28B.
- */
- if (((advertisement & ADVERTISE_SLCT) ==
- MII_PHY_SELECTOR_IEEE8023) &&
- ((linkpartner_ability & ADVERTISE_SLCT) ==
- MII_PHY_SELECTOR_IEEE8023)) {
- techabilities = (advertisement & linkpartner_ability &
- MII_PHY_TECHABILITYFIELD);
-
- fullduplex = 0;
-
- /* 10BASE-TX half duplex is lowest priority. */
- if (techabilities & LPA_10HALF)
- fullduplex = 0;
-
- if (techabilities & LPA_10FULL)
- fullduplex = 1;
-
- if (techabilities & LPA_100HALF)
- fullduplex = 0;
-
- if (techabilities & LPA_100BASE4)
- fullduplex = 0;
-
- /* 100BASE-TX half duplex is highest priority. *///Sorbica full duplex ?
- if (techabilities & LPA_100FULL)
- fullduplex = 1;
-
- if (fullduplex == 1) {
- /* If in full duplex mode, determine if PAUSE
- * functionality is supported by the local
- * device, and the link partner.
- */
- if (techabilities & LPA_PAUSE_CAP) {
- txflowcontrol = 1;
- rxflowcontrol = 1;
- } else {
- txflowcontrol = 0;
- rxflowcontrol = 0;
- }
- }
- }
- }
-
- /* If in 1000Mbps, fiber, and full duplex mode, resolve
- * 1000BASE-X PAUSE capabilities. */
- if ((fullduplex == 1) && (fiber == 1) && (gig == 1)) {
- /* In full duplex mode, resolve PAUSE
- * functionality.
- */
- u8 flow_ctl;
-#define LPA_PAUSE_ANY (LPA_1000XPAUSE_ASYM | LPA_1000XPAUSE)
-
- flow_ctl = (advertisement & LPA_PAUSE_ANY) >> 5;
- flow_ctl |= (linkpartner_ability & LPA_PAUSE_ANY) >> 7;
- switch (flow_ctl) {
- case 0x7:
- txflowcontrol = 1;
- rxflowcontrol = 0;
- break;
-
- case 0xA:
- case 0xB:
- case 0xE:
- case 0xF:
- txflowcontrol = 1;
- rxflowcontrol = 1;
- break;
-
- case 0xD:
- txflowcontrol = 0;
- rxflowcontrol = 1;
- break;
-
- default:
- txflowcontrol = 0;
- rxflowcontrol = 0;
- }
- }
-
- /*
- * If in 1000Mbps, non-fiber, full duplex mode, resolve
- * 1000BASE-T PAUSE capabilities.
- */
- if ((fullduplex == 1) && (fiber == 0) && (gig == 1)) {
- /*
- * Make sure the PHY is advertising we are PAUSE capable.
- * Otherwise advertise PAUSE and restart auto-negotiation.
- */
-#define ADVERTISE_PAUSE_ANY (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)
-
- if (!(ADVERTISE_PAUSE_ANY & advertisement)) {
- advertisement |= ADVERTISE_PAUSE_ANY;
- mdio_write(dev, phyaddr, MII_ADVERTISE, advertisement);
- mdio_write(dev, phyaddr, MII_BMCR, BMCR_ANRESTART);
-
- return -EAGAIN;
- }
- /* In full duplex mode, resolve PAUSE functionality. */
- switch (((ADVERTISE_PAUSE_ANY & advertisement) >> 0x8) |
- ((ADVERTISE_PAUSE_ANY & linkpartner_ability) >> 0xa)) {
- case 0x7:
- txflowcontrol = 1;
- rxflowcontrol = 0;
- break;
-
- case 0xA:
- case 0xB:
- case 0xE:
- case 0xF:
- txflowcontrol = 1;
- rxflowcontrol = 1;
- break;
-
- case 0xD:
- txflowcontrol = 0;
- rxflowcontrol = 1;
- break;
-
- default:
- txflowcontrol = 0;
- rxflowcontrol = 0;
+ printk(", no RX flow control.");
+ mac_ctl &= ~IPG_MC_RX_FLOW_CONTROL_ENABLE;
}
- }
-
- /*
- * If in 10/100Mbps, non-fiber, full duplex mode, assure
- * 10/100BASE-T PAUSE capabilities are advertised.
- * Otherwise advertise PAUSE and restart auto-negotiation.
- */
- if ((fullduplex == 1) && (fiber == 0) && (gig == 0) &&
- !(advertisement & ADVERTISE_PAUSE_CAP)) {
- advertisement |= ADVERTISE_PAUSE_CAP;
- mdio_write(dev, phyaddr, MII_ADVERTISE, advertisement);
- mdio_write(dev, phyaddr, MII_BMCR, BMCR_ANRESTART);
- return -EAGAIN;
- }
-
- printk(KERN_INFO "%s: %s based PHY, ", dev->name,
- (fiber == 1) ? "fiber" : "copper");
-
- /* Configure full duplex, and flow control. */
+ printk("\n");
+ } else {
+ /* Configure IPG for half duplex operation. */
+ printk(KERN_INFO "setting half duplex, no TX flow control, no RX flow control.\n");
- mac_ctl = ioread32(ioaddr + MAC_CTRL);
-
- mac_ctl &= ~IPG_MC_DUPLEX_SELECT_FD;
- mac_ctl &= ~IPG_MC_TX_FLOW_CONTROL_ENABLE;
- mac_ctl &= ~IPG_MC_RX_FLOW_CONTROL_ENABLE;
-
- if (fullduplex == 1) {
- mac_ctl |= IPG_MC_DUPLEX_SELECT_FD;
-
- if (txflowcontrol == 1)
- mac_ctl |= IPG_MC_TX_FLOW_CONTROL_ENABLE;
-
- if (rxflowcontrol == 1)
- mac_ctl |= IPG_MC_RX_FLOW_CONTROL_ENABLE;
+ mac_ctl &= ~IPG_MC_DUPLEX_SELECT_FD;
+ mac_ctl &= ~IPG_MC_TX_FLOW_CONTROL_ENABLE;
+ mac_ctl &= ~IPG_MC_RX_FLOW_CONTROL_ENABLE;
}
-
- iowrite32(IPG_MC_RSVD_MASK & mac_ctl, ioaddr + MAC_CTRL);
+ iowrite32(mac_ctl, ioaddr + MAC_CTRL);
return 0;
}
@@ -935,15 +702,7 @@ static int ipg_io_config(struct net_devi
ipg_nic_set_multicast_list(dev);
iowrite16(IPG_MAX_RXFRAME_SIZE, ioaddr + MAX_FRAME_SIZE);
- iowrite16(IPG_RE_RSVD_MASK & (IPG_RXEARLYTHRESH_VALUE),
- ioaddr + RX_EARLY_THRESH);
- iowrite32(IPG_TT_RSVD_MASK & (IPG_TXSTARTTHRESH_VALUE),
- ioaddr + TX_START_THRESH);
- iowrite32((IPG_RI_RSVD_MASK &
- ((IPG_RI_RXFRAME_COUNT & IPG_RXFRAME_COUNT) |
- (IPG_RI_PRIORITY_THRESH & (IPG_PRIORITY_THRESH << 12)) |
- (IPG_RI_RXDMAWAIT_TIME & (IPG_RXDMAWAIT_TIME << 16)))),
- ioaddr + RX_DMA_INT_CTRL);
+
iowrite8(IPG_RP_RSVD_MASK & (IPG_RXDMAPOLLPERIOD_VALUE),
ioaddr + RX_DMA_POLL_PERIOD);
iowrite8(IPG_RU_RSVD_MASK & (IPG_RXDMAURGENTTHRESH_VALUE),
diff --git a/drivers/net/ipg.h b/drivers/net/ipg.h
index 58b1417..47065e7 100644
--- a/drivers/net/ipg.h
+++ b/drivers/net/ipg.h
@@ -80,14 +80,11 @@ enum ipg_regs {
RX_DMA_BURST_THRESH = 0x24,
RX_DMA_URGENT_THRESH = 0x25,
RX_DMA_POLL_PERIOD = 0x26,
- RX_DMA_INT_CTRL = 0x28,
DEBUG_CTRL = 0x2c,
ASIC_CTRL = 0x30,
FIFO_CTRL = 0x38, // Unused
- RX_EARLY_THRESH = 0x3a,
FLOW_OFF_THRESH = 0x3c,
FLOW_ON_THRESH = 0x3e,
- TX_START_THRESH = 0x44,
EEPROM_DATA = 0x48,
EEPROM_CTRL = 0x4a,
EXPROM_ADDR = 0x4c, // Unused
@@ -324,11 +321,7 @@ #define IPG_RU_RSVD_MASK
/* RxDMAPollPeriod */
#define IPG_RP_RSVD_MASK 0xFF
-/* TxStartThresh */
-#define IPG_TT_RSVD_MASK 0x0FFF
-/* RxEarlyThresh */
-#define IPG_RE_RSVD_MASK 0x07FF
/* ReceiveMode */
#define IPG_RM_RSVD_MASK 0x3F
@@ -496,11 +489,6 @@ #define IPG_MC_RX_DISABLE
#define IPG_MC_RX_ENABLED 0x20000000
#define IPG_MC_PAUSED 0x40000000
-/* RxDMAIntCtrl */
-#define IPG_RI_RSVD_MASK 0xFFFF1CFF
-#define IPG_RI_RXFRAME_COUNT 0x000000FF
-#define IPG_RI_PRIORITY_THRESH 0x00001C00
-#define IPG_RI_RXDMAWAIT_TIME 0xFFFF0000
/*
* Tune
@@ -662,32 +650,16 @@ #define IPG_MAXRFDPROCESS_COUNT 0x80
*/
#define IPG_MINUSEDRFDSTOFREE 0x80
-/* Specify priority threshhold for a RxDMAPriority interrupt. */
-#define IPG_PRIORITY_THRESH 0x07
-
/* Specify the number of receive frames transferred via DMA
* before a RX interrupt is issued.
*/
-#define IPG_RXFRAME_COUNT 0x08
/* specify the jumbo frame maximum size
* per unit is 0x600 (the RxBuffer size that one RFD can carry)
*/
#define MAX_JUMBOSIZE 0x8 // max is 12K
-/* Specify the maximum amount of time (in 64ns increments) to wait
- * before issuing a RX interrupt if number of frames received
- * is less than IPG_RXFRAME_COUNT.
- *
- * Value Time
- * -------------------
- * 0x3D09 ~1ms
- * 0x061A ~100us
- * 0x009C ~10us
- * 0x000F ~1us
- */
-#define IPG_RXDMAWAIT_TIME 0x009C
-
+/
/* Key register values loaded at driver start up. */
/* TXDMAPollPeriod is specified in 320ns increments.
@@ -700,7 +672,6 @@ #define IPG_RXDMAWAIT_TIME 0x009C
* 0xFF ~82us
*/
#define IPG_TXDMAPOLLPERIOD_VALUE 0x26
-#define IPG_TXSTARTTHRESH_VALUE 0x0FFF
/* TxDMAUrgentThresh specifies the minimum amount of
* data in the transmit FIFO before asserting an
@@ -737,7 +708,6 @@ #define IPG_TXDMABURSTTHRESH_VALUE 0x30
* 0xFF ~82us
*/
#define IPG_RXDMAPOLLPERIOD_VALUE 0x01
-#define IPG_RXEARLYTHRESH_VALUE 0x07FF
/* RxDMAUrgentThresh specifies the minimum amount of
* free space within the receive FIFO before asserting
@@ -905,6 +875,7 @@ struct nic_id nics_supported[] = {
//variable record -- index by leading revision/length
//Revision/Length(=N*4), Address1, Data1, Address2, Data2,...,AddressN,DataN
+//VIOLET20060530SUPPORT_BEF_42
unsigned short DefaultPhyParam[] = {
// 11/12/03 IP1000A v1-3 rev=0x40
/*--------------------------------------------------------------------------
@@ -920,58 +891,6 @@ unsigned short DefaultPhyParam[] = {
(0x4100 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
0x0000,
30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x42
- (0x4200 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x43
- (0x4300 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x44
- (0x4400 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x45
- (0x4500 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x46
- (0x4600 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x47
- (0x4700 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x48
- (0x4800 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x49
- (0x4900 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x4A
- (0x4A00 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x4B
- (0x4B00 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x4C
- (0x4C00 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x4D
- (0x4D00 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
- // 01/09/04 IP1000A v1-5 rev=0x4E
- (0x4E00 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
- 0x0000,
- 30, 0x005e, 9, 0x0700,
0x0000
};
^ permalink raw reply related
* sky2 rx error problems...
From: Daniel J Blueman @ 2006-05-30 21:08 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Netfilter Developer, netdev
To follow on from the previous 'sky2 rx error' problem reported, the
problem doesn't have anything to do with iptables or MTU settings, ie
can be reproduced out the box.
The race is hit after a couple of GBs of data. Initially, I thought
this may have something to do with broken MSI support on my Intel
ICH-7M chipset, but disabling MSI didn't help.
kernel: sky2 lan0: rx error, status 0x977d977d length 0
Is there any debug information I can get to help or such? Voluntary
preemption is enabled, UP, HZ=100 on 2.6.17-rc4 on i686.
Dan
--
Daniel J Blueman
^ permalink raw reply
* Re: drivers/connector/cn_queue.c:130: warning: value computed is not used
From: David Miller @ 2006-05-30 20:11 UTC (permalink / raw)
To: johnpol; +Cc: schwab, netdev
In-Reply-To: <20060530114838.GA20906@2ka.mipt.ru>
From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Date: Tue, 30 May 2006 15:48:38 +0400
> On Tue, May 30, 2006 at 01:34:02PM +0200, Andreas Schwab (schwab@suse.de) wrote:
> > There is no point in testing the atomic value if the result is thrown
> > away.
>
> It was created to put implicit smp barrier, but it is not needed there.
> Your patch is correct, thank you.
Right, the *_and_test() variants have SMP barriers which the normal
ones do not. So it is not just a matter of giving a return value
or not, there are real side effect differences.
> > Signed-off-by: Andreas Schwab <schwab@suse.de>
>
> Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
But since you say it doesn't matter I'll apply this.
Thanks!
^ permalink raw reply
* Re: Refactor Netlink connector?
From: Evgeniy Polyakov @ 2006-05-30 19:09 UTC (permalink / raw)
To: James Morris; +Cc: netdev, David S. Miller, tgraf, Stephen Smalley
In-Reply-To: <Pine.LNX.4.64.0605301453360.28036@d.namei>
On Tue, May 30, 2006 at 02:58:11PM -0400, James Morris (jmorris@namei.org) wrote:
> > Apache still can setup routes using ioctl or execve("ip route add/route
> > add");
>
> Depends on the policy. You can specify which types of files/sockets
> apache can perform ioctl on, and whether it can execve 'ip', and if so,
> which security context that runs in, and then whether that security
> context can add routes.
With applications like phpmmyadmin apache must be allowed to perform such
operations no matter hacked it is or not...
> Security in SELinux is not based on the name of the application, it's
> based on the security label bound to the binary being executed.
I know how selinux works.
I see your point, selinux is supposed to control each datflow even if it
sometimes is not that good idea.
> > Anyway you can easily add lsm hook into both sending/receiving pathes in
> > connector code, it fully controls the traffic before it reached socket
> > queue or user's callback.
>
> There are already LSM hooks which allow this, it's a matter of not wanting
> to have to parse arbitrarily implemented Netlink protocols to determine
> what the messages are.
I mean you can control messages based on cn_mcg->id structure, since
cn_msg is a header for all connector messages.
> - James
> --
> James Morris
> <jmorris@namei.org>
--
Evgeniy Polyakov
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox