* Re: Network performance degradation from 2.6.11.12 to 2.6.16.20
From: Harry Edmon @ 2006-06-19 13:54 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Andrew Morton, linux-kernel, netdev
In-Reply-To: <4494C592.6090601@osdl.org>
Stephen Hemminger wrote:
> Does this fix it?
> # sysctl -w net.ipv4.tcp_abc=0
That did not help. I have 1 minute outputs from tcpdump under both 2.6.11.12
and 2.6.16.20. You will see a large size difference between the files. Since
the 2.6.11.12 one is 2 MBytes, I thought I would post them via the web instead
of via attachments. Look at:
http://www.atmos.washington.edu/~harry/linux/2.6.11.12.out.1min
http://www.atmos.washington.edu/~harry/linux/2.6.16.20.out.1min
And again, thank to all of you for looking into this.
--
Dr. Harry Edmon E-MAIL: harry@atmos.washington.edu
206-543-0547 harry@u.washington.edu
Dept of Atmospheric Sciences FAX: 206-543-0308
University of Washington, Box 351640, Seattle, WA 98195-1640
^ permalink raw reply
* Re: [NET]: Prevent multiple qdisc runs
From: jamal @ 2006-06-19 14:23 UTC (permalink / raw)
To: Herbert Xu; +Cc: David S. Miller, netdev
In-Reply-To: <20060619134227.GA16662@gondor.apana.org.au>
Herbert,
On Mon, 2006-19-06 at 23:42 +1000, Herbert Xu wrote:
> Hi Jamal:
>
> On Mon, Jun 19, 2006 at 09:33:51AM -0400, jamal wrote:
[..]
>
> Actually I discovered the problem only because the generic segmentation
> offload stuff that I'm working on needs to deal with the situation where
> a super-packet is partially transmitted. Requeueing causes all sorts of
> nasty problems so I chose to keep it within the net_device structure.
>
> To do so requires qdisc_run to be serialised against each other. I then
> found out that we want this anyway because otherwise the requeued packets
> could be reordered.
>
Ok, I am trying to visualize but having a hard time:
Re-queueing is done at the front of the queue to maintain ordering
whereas queueing is done at the front (i.e it is a FIFO). i,e
even if p2 comes in and gets queued while p1 is being processed,
requeueing of p1 will put it infront of p2.
Your super-packet issue may be different though ..
> > Also: what happens to the packet that comes in from either local or is
> > being forwarded and finds the qdisc_is_running flag is set? I couldnt
> > tell if the intent was to drop it or not. The answer for TCP is probably
> > simpler than for packets being forwarded.
>
> The qdisc_is_running only prevents qdisc_run from occuring (because it's
> already running), it does not impact on the queueing of the packet.
>
I will wait for your answer on the other part before responding to this.
cheers,
jamal
^ permalink raw reply
* Re: [NET]: Prevent multiple qdisc runs
From: Herbert Xu @ 2006-06-19 14:29 UTC (permalink / raw)
To: jamal; +Cc: David S. Miller, netdev
In-Reply-To: <1150727009.5815.72.camel@jzny2>
On Mon, Jun 19, 2006 at 10:23:29AM -0400, jamal wrote:
>
> Ok, I am trying to visualize but having a hard time:
> Re-queueing is done at the front of the queue to maintain ordering
> whereas queueing is done at the front (i.e it is a FIFO). i,e
> even if p2 comes in and gets queued while p1 is being processed,
> requeueing of p1 will put it infront of p2.
Correct. When qdisc_run happens we take an skb off the head of the
queue. If it can't be transmitted right away, we try to put it back
in the same spot.
If you have two qdisc_run's happening at the same time then that spot
could be different.
> Your super-packet issue may be different though ..
The reordering issue is not related to super-packets.
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: [NET]: Prevent multiple qdisc runs
From: jamal @ 2006-06-19 14:36 UTC (permalink / raw)
To: Herbert Xu; +Cc: netdev, David S. Miller
In-Reply-To: <20060619142928.GA17191@gondor.apana.org.au>
On Tue, 2006-20-06 at 00:29 +1000, Herbert Xu wrote:
> Correct. When qdisc_run happens we take an skb off the head of the
> queue. If it can't be transmitted right away, we try to put it back
> in the same spot.
>
> If you have two qdisc_run's happening at the same time then that spot
> could be different.
>
Ok, but:
The queue lock will ensure only one of the qdisc runs (assuming
different CPUs) will be able to dequeue at any one iota in time, no?
And if you assume that the cpu that manages to get the tx lock as well
is going to be contending for the qlock in ordewr to requeue, then the
only scenario i can see the race happening is when you have one CPU
faster than the other.
Did i miss something?
cheers,
jamal
^ permalink raw reply
* Re: Network performance degradation from 2.6.11.12 to 2.6.16.20
From: Jesper Dangaard Brouer @ 2006-06-19 14:47 UTC (permalink / raw)
To: Harry Edmon; +Cc: linux-kernel, netdev
In-Reply-To: <44948EF6.1060201@atmos.washington.edu>
>> Harry Edmon <harry@atmos.washington.edu> wrote:
>>
>>> I have a system with a strange network performance degradation from
>>> 2.6.11.12 to most recent kernels including 2.6.16.20 and 2.6.17-rc6.
>>> The system is has Dual single core Xeons with hyperthreading on.
<cut>
Hi Harry
Can you check which "high-res timesource" you are using?
In the kernel log look for:
kernel: Using tsc for high-res timesource
kernel: Using pmtmr for high-res timesource
I have experinced some network performance degradation when using the
"pmtmr" timesource, on a Opteron AMD system. It seems that the default
timesource change between 2.6.15 to 2.6.16.
If you use "pmtmr" try to reboot with kernel option "clock=tsc".
On my Opteron AMD system i normally can route 400 kpps, but with
timesource "pmtmr" i could only route around 83 kpps. (I found the timer
to be the issue by using oprofile).
Cheers,
Jesper Brouer
--
-------------------------------------------------------------------
MSc. Master of Computer Science
Dept. of Computer Science, University of Copenhagen
Author of http://www.adsl-optimizer.dk
-------------------------------------------------------------------
^ permalink raw reply
* Re: [RFT] pcnet32 NAPI changes
From: Lennart Sorensen @ 2006-06-19 14:58 UTC (permalink / raw)
To: Don Fry; +Cc: netdev
In-Reply-To: <20060616191154.GA6227@us.ibm.com>
On Fri, Jun 16, 2006 at 12:11:54PM -0700, Don Fry wrote:
> This patch is a collection of changes to pcnet32 which does the
> following:
>
> - Fix section mismatch warning.
> - fix set_ringparam to correctly handle memory allocation failures
> - fix off-by-one in get_ringparam.
> - cleanup at end of loopback_test when not up.
> - Add NAPI to driver, fixing set_ringparam and loopback_test to work
> correctly with poll.
> - for multicast, do not reset the chip unless cannot enter suspend mode
> to avoid race with poll.
>
> The set_ringparam code is larger than I would prefer, but it will not
> leave null pointers around for the code to stumble over when memory
> allocation fails. If anyone has a better idea, please let me know.
>
> Some complexity could be avoided by allocating memory for the maximum
> number of tx and rx buffers at probe time. Requiring 14k for the tx
> ring and arrays, and another 14k for rx; instead of about 10k total for
> the default sizes.
So 28k vs 10k? Why are these adjustable if it makes that little
difference? Is there any advantage to making them smaller?
> It is NAPI only, unlike Len Sorensen's version which allows for compile
> time selection. Some drivers are NAPI only, others have compile
> options. Which is preferred?
I just figured making it an option was less intrusive, although I can't
imagine a good reason for not wanting to use the NAPI version at all
times. I certainly know I intend to use it that way.
> I have tested these changes with a 79C971, 973, 976, and 978 on a ppc64
> machine, and 970A, 972, 973, 975, and 976 on an x86 machine.
>
> I have not tested these changes with VMware or Xen.
I will give it a try with our system and see how it runs.
Len Sorensen
^ permalink raw reply
* Re: [DOC]: generic netlink
From: James Morris @ 2006-06-19 15:13 UTC (permalink / raw)
To: jamal
Cc: netdev, David S. Miller, Thomas Graf, Jay Lan, Shailabh Nagar,
Per Liden
In-Reply-To: <1150724482.5815.48.camel@jzny2>
On Mon, 19 Jun 2006, jamal wrote:
> Attached is a document that should help people wishing to use generic
> netlink interface. It is a WIP so a lot more to go if i see interest.
Thanks for writing this up.
It seems that TIPC is multiplexing all of it's commands through
TIPC_GENL_CMD.
I wonder, if this is how other protocols are likely to utilize genl, then
we could possibly drop the command registration code completely and one
command op can be registered by the protocol during
genl_register_family().
This would both simplify the genl code and API, and help ensure
consistency of users.
- James
--
James Morris
<jmorris@namei.org>
^ permalink raw reply
* Re: [PATCH, RFT] bcm43xx: Busting the 1G limit
From: Daniel Gryniewicz @ 2006-06-19 15:23 UTC (permalink / raw)
To: Michael Buesch; +Cc: bcm43xx-dev, netdev
In-Reply-To: <200606171928.08155.mb@bu3sch.de>
On Sat, 2006-06-17 at 19:28 +0200, Michael Buesch wrote:
> Hi,
>
> This patch adds full 32-bit and 64-bit DMA support
> to the bcm43xx driver. Well, it _should_ do this. I can
> not test it, as I don't have a machine to trigger the 1G
> limit.
> The 1G limit should be exploitable on an AMD64 machine
> with more than 1G RAM.
>
> Please test and report, if it works or not. In the
> case of "works not", please provide full dmesg log.
>
> Note that I am not sure which cards actually support
> full 32-bit or even 64-bit mode. Older cards might still
> only support 30-bit DMA.
Hi.
I tried this on both 2.6.17-rc6 and on wireless-dev, and got pretty much
the same panic on both (modulo locking). My box is a turion with 2 GB
of ram and a BCM4318. Here's the panic from wireless-dev:
Unable to handle kernel NULL pointer dereference at 0000000000000020
RIP:
<ffffffff88104f24>{:bcm43xx:bcm43xx_dma_handle_xmitstatus+436}
PGD 0
Oops: 0000 [1] PREEMPT
CPU 0
Modules linked in: uhci_hdc ieee80211_crypt_wep cryptoloop loop
snd_atiixp_modem
snd_atiixp snd_ac97_codec snd_ac97_bus bcm43xx snd_pcm snd_timer
ieee80211softmac ehci_hcd snd ohci1394 ieee80211 ohci_hdc sdhci ieee1394
yenta_socket usbcore mmc_core soundcore rsrc_nonstatic ieee80211_crypt
8139too
snd_page_alloc pcmcia_core
Pid: 6139, comm: iwconfig Not tainted 2.6.17-rc6-dfg1-g57aab842-dirty #1
RIP: 0010:[<ffffffff88104f24>]
<ffffffff88104f24>{:bcm43xx:bcm43xx_dma_handle_xmitstatus+436}
RSP: 0018:ffffffff81445df8 EFLAGS: 00010002
RAX: 0000000000000063 RBX: 0000000000000001 RCX: 00000000ffffffff
RDX: 0000000000000000 RSI: 0000000000000082 RDI: 0000000000000001
RBP: ffffffff81445e28 R08: 000000000002e8c7 R09: 0000000000000000
R10: 0000000000000000 R11: 00000000fffffffa R12: 0000000000000000
R13: 00000000000030d1 R14: ffffffff81445eb8 R15: 00000000000000d0
FS: 00002b8b5dc68d20(0000) GS:ffffffff81445eb8(0000)
knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000000020 CR3: 0000000076314000 CR4: 00000000000006e0
Process iwconfig (pid: 6139, threadinfo ffff8100748ae000, task
ffff810075840890)
Stack: ffff81007510f050 0000000000000000 ffff81007510f050
0000000000008800
ffffffff814453b8 0000000020000800 ffffffff814453f8
ffffffff880f13cb
ffffffff81445e78 ffff81007541e740
Call Trace: <IRQ> <ffffffff880f13cb>{:bcm43xx:bcm43xx_interrupt_tasklet
+2379}
<ffffffff81092b78>{tasklet_action+72}
<ffffffff810126d0>{__do_softirq+80}
<ffffffff8106872a>{call_softirq+30} <ffffffff81075e04>{do_softirq
+52}
<ffffffff81092cf4>{irq_exit+63} <ffffffff81075e51>{do_IRQ+65}
<ffffffff81067dae>{ret_from_intr+0} <EOI>
<ffffffff810078c8>{_raw_spin_lock+296}
<ffffffff8106dc9e>{_spin_lock+30}
<ffffffff810202dd>{unlink_file_vma+61}
<ffffffff810206a8>{free_pagetables+152}
<ffffffff8103ee97>{exit_mmap+135}
<ffffffff810416b6>{mmput+54} <ffffffff81047b63>{exit_mmap+243}
<ffffffff81016a9a>{do_exit+602} <ffffffff81012e1c>{__fput+428}
<ffffffff810506e0>{debug_mutex_init+0}
<ffffffff81054862>{sys_exit_group+18}
<ffffffff81067892>{system_call+126}
Code: 45 3b 7c 24 20 7c 28 49 c7 c0 be 9e 10 88 b9 59 03 00 00 48
RIP <ffffffff88104f24>{:bcm43xx:bcm43xx_dma_handle_xmitstatus+436} RSP
<ffffffff81445df8>
CR2: 0000000000000020
<0>Kernel panic - not syncing: Aiee, killing interrupt handler!
Daniel
^ permalink raw reply
* Re: Network performance degradation from 2.6.11.12 to 2.6.16.20
From: Andi Kleen @ 2006-06-19 15:24 UTC (permalink / raw)
To: Jesper Dangaard Brouer; +Cc: Harry Edmon, linux-kernel, netdev
In-Reply-To: <Pine.LNX.4.61.0606191638550.23553@ask.diku.dk>
> If you use "pmtmr" try to reboot with kernel option "clock=tsc".
That's dangerous advice - when the system choses not to use
TSC it often has a reason.
>
> On my Opteron AMD system i normally can route 400 kpps, but with
> timesource "pmtmr" i could only route around 83 kpps. (I found the timer
> to be the issue by using oprofile).
Unless you're using packet sniffing or any other application
that requests time stamps on a socket then the timer shouldn't
make much difference. Incoming packets are only time stamped
when someone asks for the timestamps.
-Andi
^ permalink raw reply
* Re: [DOC]: generic netlink
From: jamal @ 2006-06-19 15:28 UTC (permalink / raw)
To: James Morris
Cc: Per Liden, Shailabh Nagar, Jay Lan, Thomas Graf, David S. Miller,
netdev
In-Reply-To: <Pine.LNX.4.64.0606191057420.21114@d.namei>
On Mon, 2006-19-06 at 11:13 -0400, James Morris wrote:
>
> It seems that TIPC is multiplexing all of it's commands through
> TIPC_GENL_CMD.
TIPC is a deviation; they had the 100 ioctls and therefore did a direct
one-to-one mapping.
> I wonder, if this is how other protocols are likely to utilize genl, then
> we could possibly drop the command registration code completely and one
> command op can be registered by the protocol during
> genl_register_family().
>
The intent is to have a handful of commands as in classical netlink
(eg route or qdisc etc) where you are controlling data that sits in the
kernel; i.e when you have an attribute or a vector of attributes, then
the commands will be of the semantics: ADD/DEL/GET/DUMP only.
Other that TIPC the two other users i have seen use it in this manner.
But, you are right if usage tends to lean in some other way we could get
rid of it (I think TIPC is a bad example).
> This would both simplify the genl code and API, and help ensure
> consistency of users.
>
You are talking from an SELinux perspective i take it?
My view: If you want to have ACLs against such commands
then it becomes easier to say "can only do ADD but not DEL" for example
(We need to resolve genl_rcv_msg() check on commands to be in sync with
SELinux as was pointed by Thomas)
cheers,
jamal
^ permalink raw reply
* Re: [DOC]: generic netlink
From: James Morris @ 2006-06-19 15:54 UTC (permalink / raw)
To: jamal
Cc: Per Liden, Shailabh Nagar, Jay Lan, Thomas Graf, David S. Miller,
netdev
In-Reply-To: <1150730884.5384.10.camel@jzny2>
On Mon, 19 Jun 2006, jamal wrote:
> Other that TIPC the two other users i have seen use it in this manner.
> But, you are right if usage tends to lean in some other way we could get
> rid of it (I think TIPC is a bad example).
Ok, perhaps make a note in the docs about this and keep an eye out when
new code is submitted, and encourage people not to do this.
> > This would both simplify the genl code and API, and help ensure
> > consistency of users.
> >
>
> You are talking from an SELinux perspective i take it?
Actually, what would help SELinux is the opposite, forcing everyone to use
separate commands and assigning security attributes to each one. But
because TIPC is already multiplexing, it's not feasible.
Instead, I think the way to go for SELinux is to have each nl family
provide a permission callback, so SELinux can pass the skb back to the nl
module which then returns a type of permission ('read', 'write',
'readpriv'). This way, the nl module can create and manage its own
internal table of command permissions and also know exactly where in the
message to dig for the command specifier.
> My view: If you want to have ACLs against such commands then it becomes
> easier to say "can only do ADD but not DEL" for example (We need to
> resolve genl_rcv_msg() check on commands to be in sync with SELinux as
> was pointed by Thomas)
This already exists, to some extent, but only for some protocols. You can
see examples of existing permission tables managed by SELinux in:
security/selinux/nlmsgtab.c
The hope move this out of SELinux and into each nl module, which is much
more manageable and scalable.
- James
--
James Morris
<jmorris@namei.org>
^ permalink raw reply
* Re: [DOC]: generic netlink
From: Shailabh Nagar @ 2006-06-19 15:58 UTC (permalink / raw)
To: hadi; +Cc: James Morris, Per Liden, Jay Lan, Thomas Graf, David S. Miller,
netdev
In-Reply-To: <1150730884.5384.10.camel@jzny2>
jamal wrote:
> On Mon, 2006-19-06 at 11:13 -0400, James Morris wrote:
>
>
>>It seems that TIPC is multiplexing all of it's commands through
>>TIPC_GENL_CMD.
>
>
>
> TIPC is a deviation; they had the 100 ioctls and therefore did a direct
> one-to-one mapping.
>
>
>>I wonder, if this is how other protocols are likely to utilize genl, then
>>we could possibly drop the command registration code completely and one
>>command op can be registered by the protocol during
>>genl_register_family().
>>
>
>
> The intent is to have a handful of commands as in classical netlink
> (eg route or qdisc etc) where you are controlling data that sits in the
> kernel; i.e when you have an attribute or a vector of attributes, then
> the commands will be of the semantics: ADD/DEL/GET/DUMP only.
> Other that TIPC the two other users i have seen use it in this manner.
> But, you are right if usage tends to lean in some other way we could get
> rid of it (I think TIPC is a bad example).
The taskstats interface, currently in -mm, is one user of genetlink
http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.17-rc6/2.6.17-rc6-mm2/broken-out/per-task-delay-accounting-taskstats-interface.patch
Based on Jamal's suggestions, we found it useful to have the "limited"
set of commands model and ended up with having to register just one GET
command. And in subsequent discussions, a SET command would also be handy.
But I'm not too clear about what are the advantages of trying to limit the
number of commands registered by a given exploiter of genetlink (say TIPC or taskstats),
other than the conventional usage of netlink.
e.g in the taskstats code, userspace needs to GET data on a per-pid and per-tgid basis
from the kernel and supplies the specific pid or tgid. We could either have registered
two commands (say GET_PID and GET_TGID) and then the parsing of the supplied uint32 would
be implicit in the command. But we went with the model where we have only one GET command
and the type of the parameter is specified via netlink attributes.
In our case, it didn't matter and since the type of data returned is very similar and so is
the parameter supplied (pid/tgid), one GET suffices. But I'm wondering if userspace should
consciously try and limit the commands or would it be better from a performance standpoint,
to permit a reasonably larger "fan-out" to happen at the genetlink command level (for each exploiter).
I guess this introduces more overhead for in-kernel structures (the linked list of command structures
that needs to be kept around) while saving time on doing a second level of parsing within the
exploiter-defined function that services the GET command.
The "small" set model looks like a good compromise. Reducing number of commands to one is not a good
idea IMHO....for reasons similar to why ioctl type syscalls aren't encouraged...since the genetlink
layer anyway has code for demultiplexing, might as well use it and avoid an extra level of indirection.
--Shailabh
>>This would both simplify the genl code and API, and help ensure
>>consistency of users.
>>
>
>
> You are talking from an SELinux perspective i take it?
> My view: If you want to have ACLs against such commands
> then it becomes easier to say "can only do ADD but not DEL" for example
> (We need to resolve genl_rcv_msg() check on commands to be in sync with
> SELinux as was pointed by Thomas)
>
> cheers,
> jamal
>
^ permalink raw reply
* Re: [Patch 1/1] AF_UNIX Datagram getpeersec (with minor fix)
From: Xiaolan Zhang @ 2006-06-19 16:39 UTC (permalink / raw)
To: James Morris
Cc: akpm, chrisw, Catherine Zhang, czhang.us, davem, George Wilson,
herbert, jmorris, latten, netdev, sds, Serge E Hallyn, tjaeger
In-Reply-To: <Pine.LNX.4.64.0606180313320.22401@d.namei>
James Morris <jmorris@namei.org> wrote on 06/18/2006 04:04:06 AM:
> On Sun, 18 Jun 2006, Catherine Zhang wrote:
>
> I'd also mention here that this is to complement the SO_PEERSEC option
for
> stream sockets.
>
OK.
> There's an implementation issue, which I'm sure has been mentioned
> previously. This code should not be calling SELinux API functions.
>
> > @@ -62,6 +70,12 @@ static __inline__ void scm_recv(struct s
> > if (test_bit(SOCK_PASSCRED, &sock->flags))
> > put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS,
> sizeof(scm->creds), &scm->creds);
> >
> > + if (test_bit(SOCK_PASSSEC, &sock->flags)) {
> > + err = selinux_ctxid_to_string(scm->sid, &scontext,
> &scontext_len);
>
>
I remember this issue being discussed, but no conclusion was made. The
reason that we cannot use socket_getpeersec_dgram directly is that it
passes skb as the argument, instead of socket. If we want to reuse the
same hook for UNIX, then we have to make change to the interface. I was
debating on whether I should add another hook for the UNIX domain...
Let me check whether it'll be possible to reuse socket_getpeersec_dgram
without too much disruption/complicaiton and I will repost.
thanks,
Catherine
^ permalink raw reply
* Re: Network performance degradation from 2.6.11.12 to 2.6.16.20
From: Harry Edmon @ 2006-06-19 16:40 UTC (permalink / raw)
To: Jesper Dangaard Brouer; +Cc: linux-kernel, netdev
In-Reply-To: <Pine.LNX.4.61.0606191638550.23553@ask.diku.dk>
Jesper Dangaard Brouer wrote:
>
>>> Harry Edmon <harry@atmos.washington.edu> wrote:
>>>
>>>> I have a system with a strange network performance degradation from
>>>> 2.6.11.12 to most recent kernels including 2.6.16.20 and
>>>> 2.6.17-rc6. The system is has Dual single core Xeons with
>>>> hyperthreading on.
> <cut>
>
> Hi Harry
>
> Can you check which "high-res timesource" you are using?
>
> In the kernel log look for:
> kernel: Using tsc for high-res timesource
> kernel: Using pmtmr for high-res timesource
>
> I have experinced some network performance degradation when using the
> "pmtmr" timesource, on a Opteron AMD system. It seems that the
> default timesource change between 2.6.15 to 2.6.16.
>
> If you use "pmtmr" try to reboot with kernel option "clock=tsc".
>
> On my Opteron AMD system i normally can route 400 kpps, but with
> timesource "pmtmr" i could only route around 83 kpps. (I found the
> timer to be the issue by using oprofile).
>
>
We have CONFIG_HPET_TIMER=y, so we do not see these messages.
^ permalink raw reply
* Re: Network performance degradation from 2.6.11.12 to 2.6.16.20
From: Chris Friesen @ 2006-06-19 17:34 UTC (permalink / raw)
To: Andi Kleen; +Cc: Jesper Dangaard Brouer, Harry Edmon, linux-kernel, netdev
In-Reply-To: <200606191724.31305.ak@suse.de>
Andi Kleen wrote:
> Incoming packets are only time stamped
> when someone asks for the timestamps.
Doesn't that add scheduling latency to the timestamps? Or is is a flag
that gets set to trigger timestamping at packet arrival?
Chris
^ permalink raw reply
* Re: Network performance degradation from 2.6.11.12 to 2.6.16.20
From: Jesper Dangaard Brouer @ 2006-06-19 18:24 UTC (permalink / raw)
To: Andi Kleen; +Cc: Harry Edmon, linux-kernel, netdev
In-Reply-To: <200606191724.31305.ak@suse.de>
On Mon, 19 Jun 2006, Andi Kleen wrote:
>> If you use "pmtmr" try to reboot with kernel option "clock=tsc".
>
> That's dangerous advice - when the system choses not to use
> TSC it often has a reason.
Sorry, it was not a general advice, just something to try out. It really
solved my network performance issue...
>> On my Opteron AMD system i normally can route 400 kpps, but with
>> timesource "pmtmr" i could only route around 83 kpps. (I found the timer
>> to be the issue by using oprofile).
>
> Unless you're using packet sniffing or any other application
> that requests time stamps on a socket then the timer shouldn't
> make much difference. Incoming packets are only time stamped
> when someone asks for the timestamps.
I do not know what caused the issue on my machine, but I can look into it
if you like to know?
I do have VLAN interfaces on the machine and it seems that eth1 runs in
PROMISC mode (eth1.xxx does not). Could it be caused by that?
Hilsen
Jesper Brouer
--
-------------------------------------------------------------------
MSc. Master of Computer Science
Dept. of Computer Science, University of Copenhagen
Author of http://www.adsl-optimizer.dk
-------------------------------------------------------------------
^ permalink raw reply
* [PATCH 2/2] update sunrpc to use in-kernel sockets API - ver2
From: Sridhar Samudrala @ 2006-06-19 19:15 UTC (permalink / raw)
To: davem; +Cc: netdev
This patch updates sunrpc to use in-kernel sockets API.
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -388,7 +388,7 @@ svc_sendto(struct svc_rqst *rqstp, struc
/* send head */
if (slen == xdr->head[0].iov_len)
flags = 0;
- len = sock->ops->sendpage(sock, rqstp->rq_respages[0], 0, xdr->head[0].iov_len, flags);
+ len = kernel_sendpage(sock, rqstp->rq_respages[0], 0, xdr->head[0].iov_len, flags);
if (len != xdr->head[0].iov_len)
goto out;
slen -= xdr->head[0].iov_len;
@@ -400,7 +400,7 @@ svc_sendto(struct svc_rqst *rqstp, struc
while (pglen > 0) {
if (slen == size)
flags = 0;
- result = sock->ops->sendpage(sock, *ppage, base, size, flags);
+ result = kernel_sendpage(sock, *ppage, base, size, flags);
if (result > 0)
len += result;
if (result != size)
@@ -413,7 +413,7 @@ svc_sendto(struct svc_rqst *rqstp, struc
}
/* send tail */
if (xdr->tail[0].iov_len) {
- result = sock->ops->sendpage(sock, rqstp->rq_respages[rqstp->rq_restailpage],
+ result = kernel_sendpage(sock, rqstp->rq_respages[rqstp->rq_restailpage],
((unsigned long)xdr->tail[0].iov_base)& (PAGE_SIZE-1),
xdr->tail[0].iov_len, 0);
@@ -434,13 +434,10 @@ out:
static int
svc_recv_available(struct svc_sock *svsk)
{
- mm_segment_t oldfs;
struct socket *sock = svsk->sk_sock;
int avail, err;
- oldfs = get_fs(); set_fs(KERNEL_DS);
- err = sock->ops->ioctl(sock, TIOCINQ, (unsigned long) &avail);
- set_fs(oldfs);
+ err = kernel_sock_ioctl(sock, TIOCINQ, (unsigned long) &avail);
return (err >= 0)? avail : err;
}
@@ -472,7 +469,7 @@ svc_recvfrom(struct svc_rqst *rqstp, str
* at accept time. FIXME
*/
alen = sizeof(rqstp->rq_addr);
- sock->ops->getname(sock, (struct sockaddr *)&rqstp->rq_addr, &alen, 1);
+ kernel_getpeername(sock, (struct sockaddr *)&rqstp->rq_addr, &alen);
dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n",
rqstp->rq_sock, iov[0].iov_base, iov[0].iov_len, len);
@@ -758,7 +755,6 @@ svc_tcp_accept(struct svc_sock *svsk)
struct svc_serv *serv = svsk->sk_server;
struct socket *sock = svsk->sk_sock;
struct socket *newsock;
- const struct proto_ops *ops;
struct svc_sock *newsvsk;
int err, slen;
@@ -766,29 +762,23 @@ svc_tcp_accept(struct svc_sock *svsk)
if (!sock)
return;
- err = sock_create_lite(PF_INET, SOCK_STREAM, IPPROTO_TCP, &newsock);
- if (err) {
+ clear_bit(SK_CONN, &svsk->sk_flags);
+ err = kernel_accept(sock, &newsock, O_NONBLOCK);
+ if (err < 0) {
if (err == -ENOMEM)
printk(KERN_WARNING "%s: no more sockets!\n",
serv->sv_name);
- return;
- }
-
- dprintk("svc: tcp_accept %p allocated\n", newsock);
- newsock->ops = ops = sock->ops;
-
- clear_bit(SK_CONN, &svsk->sk_flags);
- if ((err = ops->accept(sock, newsock, O_NONBLOCK)) < 0) {
- if (err != -EAGAIN && net_ratelimit())
+ else if (err != -EAGAIN && net_ratelimit())
printk(KERN_WARNING "%s: accept failed (err %d)!\n",
serv->sv_name, -err);
- goto failed; /* aborted connection or whatever */
+ return;
}
+
set_bit(SK_CONN, &svsk->sk_flags);
svc_sock_enqueue(svsk);
slen = sizeof(sin);
- err = ops->getname(newsock, (struct sockaddr *) &sin, &slen, 1);
+ err = kernel_getpeername(newsock, (struct sockaddr *) &sin, &slen);
if (err < 0) {
if (net_ratelimit())
printk(KERN_WARNING "%s: peername failed (err %d)!\n",
@@ -1407,14 +1397,14 @@ svc_create_socket(struct svc_serv *serv,
if (sin != NULL) {
if (type == SOCK_STREAM)
sock->sk->sk_reuse = 1; /* allow address reuse */
- error = sock->ops->bind(sock, (struct sockaddr *) sin,
+ error = kernel_bind(sock, (struct sockaddr *) sin,
sizeof(*sin));
if (error < 0)
goto bummer;
}
if (protocol == IPPROTO_TCP) {
- if ((error = sock->ops->listen(sock, 64)) < 0)
+ if ((error = kernel_listen(sock, 64)) < 0)
goto bummer;
}
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -207,7 +207,7 @@ static inline int xs_sendpages(struct so
base &= ~PAGE_CACHE_MASK;
}
- sendpage = sock->ops->sendpage ? : sock_no_sendpage;
+ sendpage = kernel_sendpage;
do {
int flags = XS_SENDMSG_FLAGS;
@@ -952,7 +952,7 @@ static int xs_bindresvport(struct rpc_xp
do {
myaddr.sin_port = htons(port);
- err = sock->ops->bind(sock, (struct sockaddr *) &myaddr,
+ err = kernel_bind(sock, (struct sockaddr *) &myaddr,
sizeof(myaddr));
if (err == 0) {
xprt->port = port;
@@ -1047,7 +1047,7 @@ static void xs_tcp_reuse_connection(stru
*/
memset(&any, 0, sizeof(any));
any.sa_family = AF_UNSPEC;
- result = sock->ops->connect(sock, &any, sizeof(any), 0);
+ result = kernel_connect(sock, &any, sizeof(any), 0);
if (result)
dprintk("RPC: AF_UNSPEC connect return code %d\n",
result);
@@ -1117,7 +1117,7 @@ static void xs_tcp_connect_worker(void *
/* Tell the socket layer to start connecting... */
xprt->stat.connect_count++;
xprt->stat.connect_start = jiffies;
- status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
+ status = kernel_connect(sock, (struct sockaddr *) &xprt->addr,
sizeof(xprt->addr), O_NONBLOCK);
dprintk("RPC: %p connect status %d connected %d sock state %d\n",
xprt, -status, xprt_connected(xprt), sock->sk->sk_state);
^ permalink raw reply
* [PATCH 1/2] in-kernel sockets API - ver2
From: Sridhar Samudrala @ 2006-06-19 19:15 UTC (permalink / raw)
To: davem; +Cc: netdev
This patch implements wrapper functions that provide a convenient way to
access the sockets API for in-kernel users like sunrpc, cifs & ocsf2 etc
and any future users.
The only change from the version i submitted last week is the renaming of
kernel_ioctl to kernel_sock_ioctl.
I left the exports to use EXPORT_SYMBOL() to match with the existing
interfaces sock_create_kern(), kernel_sendmsg(), kernel_recvmsg etc.
Thanks
Sridhar
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Acked-by: James Morris <jmorris@namei.org>
diff --git a/include/linux/net.h b/include/linux/net.h
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -208,6 +208,25 @@ extern int kernel_recvmsg(struct
struct kvec *vec, size_t num,
size_t len, int flags);
+extern int kernel_bind(struct socket *sock, struct sockaddr *addr,
+ int addrlen);
+extern int kernel_listen(struct socket *sock, int backlog);
+extern int kernel_accept(struct socket *sock, struct socket **newsock,
+ int flags);
+extern int kernel_connect(struct socket *sock, struct sockaddr *addr,
+ int addrlen, int flags);
+extern int kernel_getsockname(struct socket *sock, struct sockaddr *addr,
+ int *addrlen);
+extern int kernel_getpeername(struct socket *sock, struct sockaddr *addr,
+ int *addrlen);
+extern int kernel_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen);
+extern int kernel_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen);
+extern int kernel_sendpage(struct socket *sock, struct page *page, int offset,
+ size_t size, int flags);
+extern int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg);
+
#ifndef CONFIG_SMP
#define SOCKOPS_WRAPPED(name) name
#define SOCKOPS_WRAP(name, fam)
diff --git a/net/socket.c b/net/socket.c
--- a/net/socket.c
+++ b/net/socket.c
@@ -2160,6 +2160,109 @@ static long compat_sock_ioctl(struct fil
}
#endif
+int kernel_bind(struct socket *sock, struct sockaddr *addr, int addrlen)
+{
+ return sock->ops->bind(sock, addr, addrlen);
+}
+
+int kernel_listen(struct socket *sock, int backlog)
+{
+ return sock->ops->listen(sock, backlog);
+}
+
+int kernel_accept(struct socket *sock, struct socket **newsock, int flags)
+{
+ struct sock *sk = sock->sk;
+ int err;
+
+ err = sock_create_lite(sk->sk_family, sk->sk_type, sk->sk_protocol,
+ newsock);
+ if (err < 0)
+ goto done;
+
+ err = sock->ops->accept(sock, *newsock, flags);
+ if (err < 0) {
+ sock_release(*newsock);
+ goto done;
+ }
+
+ (*newsock)->ops = sock->ops;
+
+done:
+ return err;
+}
+
+int kernel_connect(struct socket *sock, struct sockaddr *addr, int addrlen,
+ int flags)
+{
+ return sock->ops->connect(sock, addr, addrlen, flags);
+}
+
+int kernel_getsockname(struct socket *sock, struct sockaddr *addr,
+ int *addrlen)
+{
+ return sock->ops->getname(sock, addr, addrlen, 0);
+}
+
+int kernel_getpeername(struct socket *sock, struct sockaddr *addr,
+ int *addrlen)
+{
+ return sock->ops->getname(sock, addr, addrlen, 1);
+}
+
+int kernel_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ mm_segment_t oldfs = get_fs();
+ int err;
+
+ set_fs(KERNEL_DS);
+ if (level == SOL_SOCKET)
+ err = sock_getsockopt(sock, level, optname, optval, optlen);
+ else
+ err = sock->ops->getsockopt(sock, level, optname, optval,
+ optlen);
+ set_fs(oldfs);
+ return err;
+}
+
+int kernel_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ mm_segment_t oldfs = get_fs();
+ int err;
+
+ set_fs(KERNEL_DS);
+ if (level == SOL_SOCKET)
+ err = sock_setsockopt(sock, level, optname, optval, optlen);
+ else
+ err = sock->ops->setsockopt(sock, level, optname, optval,
+ optlen);
+ set_fs(oldfs);
+ return err;
+}
+
+int kernel_sendpage(struct socket *sock, struct page *page, int offset,
+ size_t size, int flags)
+{
+ if (sock->ops->sendpage)
+ return sock->ops->sendpage(sock, page, offset, size, flags);
+
+ return sock_no_sendpage(sock, page, offset, size, flags);
+}
+
+int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg)
+{
+ mm_segment_t oldfs = get_fs();
+ int err;
+
+ set_fs(KERNEL_DS);
+ err = sock->ops->ioctl(sock, cmd, arg);
+ set_fs(oldfs);
+
+ return err;
+}
+
/* ABI emulation layers need these two */
EXPORT_SYMBOL(move_addr_to_kernel);
EXPORT_SYMBOL(move_addr_to_user);
@@ -2176,3 +2279,13 @@ EXPORT_SYMBOL(sock_wake_async);
EXPORT_SYMBOL(sockfd_lookup);
EXPORT_SYMBOL(kernel_sendmsg);
EXPORT_SYMBOL(kernel_recvmsg);
+EXPORT_SYMBOL(kernel_bind);
+EXPORT_SYMBOL(kernel_listen);
+EXPORT_SYMBOL(kernel_accept);
+EXPORT_SYMBOL(kernel_connect);
+EXPORT_SYMBOL(kernel_getsockname);
+EXPORT_SYMBOL(kernel_getpeername);
+EXPORT_SYMBOL(kernel_getsockopt);
+EXPORT_SYMBOL(kernel_setsockopt);
+EXPORT_SYMBOL(kernel_sendpage);
+EXPORT_SYMBOL(kernel_sock_ioctl);
^ permalink raw reply
* Re: [PATCH 2/2] NET: Accurate packet scheduling for ATM/ADSL (userspace)
From: Jesper Dangaard Brouer @ 2006-06-19 19:31 UTC (permalink / raw)
To: jamal; +Cc: Russell Stuart, Alan Cox, Stephen Hemminger, netdev, lartc
In-Reply-To: <1150376591.5116.36.camel@jzny2>
On Thu, 15 Jun 2006, jamal wrote:
> On Thu, 2006-15-06 at 10:47 +1000, Russell Stuart wrote:
>> On Wed, 2006-06-14 at 11:57 +0100, Alan Cox wrote:
>>> The other problem I see with this code is it is very tightly tied to ATM
>>> cell sizes, not to solving the generic question of packetisation.
>>
>> Others have made this point also. I can't speak for Jesper,
>> but I did consider making it generic.
I also have considered to make it generic, but choose to make my patch as
non-intrusive as possible to the kernel (and try to handle as much in
userspace as possible).
Actually I do think that the kernel patch part is very generic.
The patch simply allow us to align the rate table/array.
With the kernel patch in place, we can work on the userspace TC program to
support more and more types of exotic link layer modeling.
>> The issue was that
>> doing so would add more code, but I don't personally know
>> of any real world situation that would use the generic
>> solution. I didn't fancy the thought of arguing on these
>> lists for code that no one would actually use.
;-)
>> If someone could put up their hand and say "Hey, I need
>> this," then expanding the patch to accommodate them would
>> be a pleasure. I like generic code too.
>>
>
> It is probably doable by just looking at netdevice->type and figuring
> the link layer technology. Totally in user space and building the
> compensated for tables there before telling the kernel (advantage is no
> kernel changes and therefore it would work with older kernels as well).
I think you have got the setup all wrong.
The linux middlebox/router has two ethernet interfaces, one of the
ethernet interfaces is connected to the ADSL modem. Thus, the linux
ethernet card cannot determine that it is connected to an ADSL line.
The patch is the solution to the classical problem people
have when tryng to configure traffic control on an ADSL link?
Q: The packet scheduling does not work all the time?
A: Try to decrease to bandwidth.
The issue here is, that ATM does not have fixed overhead (due to alignment
and padding). This means that a fixed reduction of the bandwidth is not
the solution. We could reduce the bandwidth to the worst-case overhead,
which is 62%, I do not think that is a good solution...
With the patch, you can now simply configure HTB to use the rate that was
specified by the ISP.
Please read chapter 6 ("Achieving Queue Control") page 55-65, where I
demonstrate that the naive approach of reducing bandwidth does not work,
when the packet distribution change on the link.
http://www.adsl-optimizer.dk/thesis/
Cheers,
Jesper Brouer
--
-------------------------------------------------------------------
MSc. Master of Computer Science
Dept. of Computer Science, University of Copenhagen
Author of http://www.adsl-optimizer.dk
-------------------------------------------------------------------
^ permalink raw reply
* Re: [RFT] pcnet32 NAPI changes
From: Jon Mason @ 2006-06-19 20:41 UTC (permalink / raw)
To: Don Fry; +Cc: lsorense, netdev
In-Reply-To: <20060616191154.GA6227@us.ibm.com>
On Fri, Jun 16, 2006 at 12:11:54PM -0700, Don Fry wrote:
> This patch is a collection of changes to pcnet32 which does the
> following:
>
> - Fix section mismatch warning.
> - fix set_ringparam to correctly handle memory allocation failures
> - fix off-by-one in get_ringparam.
> - cleanup at end of loopback_test when not up.
> - Add NAPI to driver, fixing set_ringparam and loopback_test to work
> correctly with poll.
> - for multicast, do not reset the chip unless cannot enter suspend mode
> to avoid race with poll.
>
> The set_ringparam code is larger than I would prefer, but it will not
> leave null pointers around for the code to stumble over when memory
> allocation fails. If anyone has a better idea, please let me know.
>
> Some complexity could be avoided by allocating memory for the maximum
> number of tx and rx buffers at probe time. Requiring 14k for the tx
> ring and arrays, and another 14k for rx; instead of about 10k total for
> the default sizes.
>
> It is NAPI only, unlike Len Sorensen's version which allows for compile
> time selection. Some drivers are NAPI only, others have compile
> options. Which is preferred?
I believe it is preferred to be a compile option for non-gigabit
drivers, given that it will be eating a lot of cycles for infrequent
packets (especially for the 10Mb). I believe there was a thread about
this last year when e100 was having NAPI problems.
A general nit. There are ALOT of magic numbers in the code, most
existing prior to this patch. The driver would benefit from a little
clean-up.
Also nothing to do with this patch, but I noticed it when the code was
moved. A comment about why the following is necessary might be nice:
lp->rx_ring[i].buf_length = le16_to_cpu(2 - PKT_BUF_SZ);
Thanks,
Jon
>
> I have tested these changes with a 79C971, 973, 976, and 978 on a ppc64
> machine, and 970A, 972, 973, 975, and 976 on an x86 machine.
>
> I have not tested these changes with VMware or Xen.
>
>
>
> --- linux-2.6.17-rc6/drivers/net/orig.pcnet32.c 2006-06-15 11:49:39.000000000 -0700
> +++ linux-2.6.17-rc6/drivers/net/pcnet32.c 2006-06-16 11:30:45.000000000 -0700
> @@ -22,8 +22,8 @@
> *************************************************************************/
>
> #define DRV_NAME "pcnet32"
> -#define DRV_VERSION "1.32"
> -#define DRV_RELDATE "18.Mar.2006"
> +#define DRV_VERSION "1.33-NAPI"
> +#define DRV_RELDATE "16.Jun.2006"
> #define PFX DRV_NAME ": "
>
> static const char *const version =
> @@ -277,13 +277,12 @@ struct pcnet32_private {
> u32 phymask;
> };
>
> -static void pcnet32_probe_vlbus(void);
> static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
> static int pcnet32_probe1(unsigned long, int, struct pci_dev *);
> static int pcnet32_open(struct net_device *);
> static int pcnet32_init_ring(struct net_device *);
> static int pcnet32_start_xmit(struct sk_buff *, struct net_device *);
> -static int pcnet32_rx(struct net_device *);
> +static int pcnet32_poll(struct net_device *dev, int *budget);
> static void pcnet32_tx_timeout(struct net_device *dev);
> static irqreturn_t pcnet32_interrupt(int, void *, struct pt_regs *);
> static int pcnet32_close(struct net_device *);
> @@ -425,6 +424,215 @@ static struct pcnet32_access pcnet32_dwi
> .reset = pcnet32_dwio_reset
> };
>
> +static void pcnet32_netif_stop(struct net_device *dev)
> +{
> + dev->trans_start = jiffies;
> + netif_poll_disable(dev);
> + netif_tx_disable(dev);
> +}
> +
> +static void pcnet32_netif_start(struct net_device *dev)
> +{
> + netif_wake_queue(dev);
> + netif_poll_enable(dev);
> +}
> +
> +/*
> + * Allocate space for the new sized tx ring.
> + * Free old resources
> + * Save new resources.
> + * Any failure keeps old resources.
> + * Must be called with lp->lock held.
> + */
> +static void pcnet32_realloc_tx_ring(struct net_device *dev,
> + struct pcnet32_private *lp,
> + unsigned int size)
> +{
> + dma_addr_t new_ring_dma_addr;
> + dma_addr_t *new_dma_addr_list;
> + struct pcnet32_tx_head *new_tx_ring;
> + struct sk_buff **new_skb_list;
> +
> + pcnet32_purge_tx_ring(dev);
> +
> + new_tx_ring = pci_alloc_consistent(lp->pci_dev,
> + sizeof(struct pcnet32_tx_head) *
> + (1 << size),
> + &new_ring_dma_addr);
> + if (new_tx_ring == NULL) {
> + if (pcnet32_debug & NETIF_MSG_DRV)
> + printk("\n" KERN_ERR PFX
> + "%s: Consistent memory allocation failed.\n",
> + dev->name);
> + return;
> + }
> + memset(new_tx_ring, 0, sizeof(struct pcnet32_tx_head) * (1 << size));
> +
> + new_dma_addr_list = kcalloc(sizeof(dma_addr_t), (1 << size), GFP_ATOMIC);
> + if (!new_dma_addr_list) {
> + if (pcnet32_debug & NETIF_MSG_DRV)
> + printk("\n" KERN_ERR PFX
> + "%s: Memory allocation failed.\n", dev->name);
> + goto free_new_tx_ring;
> + }
> +
> + new_skb_list = kcalloc(sizeof(struct sk_buff *), (1 << size), GFP_ATOMIC);
> + if (!new_skb_list) {
> + if (pcnet32_debug & NETIF_MSG_DRV)
> + printk("\n" KERN_ERR PFX
> + "%s: Memory allocation failed.\n", dev->name);
> + goto free_new_lists;
> + }
> +
> + kfree(lp->tx_skbuff);
> + kfree(lp->tx_dma_addr);
> + pci_free_consistent(lp->pci_dev,
> + sizeof(struct pcnet32_tx_head) *
> + lp->tx_ring_size, lp->tx_ring,
> + lp->tx_ring_dma_addr);
> +
> + lp->tx_ring_size = (1 << size);
> + lp->tx_mod_mask = lp->tx_ring_size - 1;
> + lp->tx_len_bits = (size << 12);
> + lp->tx_ring = new_tx_ring;
> + lp->tx_ring_dma_addr = new_ring_dma_addr;
> + lp->tx_dma_addr = new_dma_addr_list;
> + lp->tx_skbuff = new_skb_list;
> + return;
> +
> + free_new_lists:
> + kfree(new_dma_addr_list);
> + free_new_tx_ring:
> + pci_free_consistent(lp->pci_dev,
> + sizeof(struct pcnet32_tx_head) *
> + (1 << size),
> + new_tx_ring,
> + new_ring_dma_addr);
> + return;
> +}
> +
> +/*
> + * Allocate space for the new sized rx ring.
> + * Re-use old receive buffers.
> + * alloc extra buffers
> + * free unneeded buffers
> + * free unneeded buffers
> + * Save new resources.
> + * Any failure keeps old resources.
> + * Must be called with lp->lock held.
> + */
> +static void pcnet32_realloc_rx_ring(struct net_device *dev,
> + struct pcnet32_private *lp,
> + unsigned int size)
> +{
> + dma_addr_t new_ring_dma_addr;
> + dma_addr_t *new_dma_addr_list;
> + struct pcnet32_rx_head *new_rx_ring;
> + struct sk_buff **new_skb_list;
> + int new, overlap;
> +
> + new_rx_ring = pci_alloc_consistent(lp->pci_dev,
> + sizeof(struct pcnet32_rx_head) *
> + (1 << size),
> + &new_ring_dma_addr);
> + if (new_rx_ring == NULL) {
> + if (pcnet32_debug & NETIF_MSG_DRV)
> + printk("\n" KERN_ERR PFX
> + "%s: Consistent memory allocation failed.\n",
> + dev->name);
> + return;
> + }
> + memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * (1 << size));
> +
> + new_dma_addr_list = kcalloc(sizeof(dma_addr_t), (1 << size), GFP_ATOMIC);
> + if (!new_dma_addr_list) {
> + if (pcnet32_debug & NETIF_MSG_DRV)
> + printk("\n" KERN_ERR PFX
> + "%s: Memory allocation failed.\n", dev->name);
> + goto free_new_rx_ring;
> + }
> +
> + new_skb_list = kcalloc(sizeof(struct sk_buff *), (1 << size), GFP_ATOMIC);
> + if (!new_skb_list) {
> + if (pcnet32_debug & NETIF_MSG_DRV)
> + printk("\n" KERN_ERR PFX
> + "%s: Memory allocation failed.\n", dev->name);
> + goto free_new_lists;
> + }
> +
> + /* first copy the current receive buffers */
> + overlap = min(size, lp->rx_ring_size);
> + for (new=0; new<overlap; new++) {
> + new_rx_ring[new] = lp->rx_ring[new];
> + new_dma_addr_list[new] = lp->rx_dma_addr[new];
> + new_skb_list[new] = lp->rx_skbuff[new];
> + }
> + /* now allocate any new buffers needed */
> + for (; new < size; new++ ) {
> + struct sk_buff *rx_skbuff;
> + new_skb_list[new] = dev_alloc_skb(PKT_BUF_SZ);
> + if (!(rx_skbuff = new_skb_list[new])) {
> + /* keep the original lists and buffers */
> + if (netif_msg_drv(lp))
> + printk(KERN_ERR
> + "%s: pcnet32_realloc_rx_ring dev_alloc_skb failed.\n",
> + dev->name);
> + goto free_all_new;
> + }
> + skb_reserve(rx_skbuff, 2);
> +
> + new_dma_addr_list[new] =
> + pci_map_single(lp->pci_dev, rx_skbuff->data,
> + PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
> + new_rx_ring[new].base = (u32) le32_to_cpu(new_dma_addr_list[new]);
> + new_rx_ring[new].buf_length = le16_to_cpu(2 - PKT_BUF_SZ);
> + new_rx_ring[new].status = le16_to_cpu(0x8000);
> + }
> + /* and free any unneeded buffers */
> + for (; new < lp->rx_ring_size; new++) {
> + if (lp->rx_skbuff[new]) {
> + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[new],
> + PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
> + dev_kfree_skb(lp->rx_skbuff[new]);
> + }
> + }
> +
> + kfree(lp->rx_skbuff);
> + kfree(lp->rx_dma_addr);
> + pci_free_consistent(lp->pci_dev,
> + sizeof(struct pcnet32_rx_head) *
> + lp->rx_ring_size, lp->rx_ring,
> + lp->rx_ring_dma_addr);
> +
> + lp->rx_ring_size = (1 << size);
> + lp->rx_mod_mask = lp->rx_ring_size - 1;
> + lp->rx_len_bits = (size << 4);
> + lp->rx_ring = new_rx_ring;
> + lp->rx_ring_dma_addr = new_ring_dma_addr;
> + lp->rx_dma_addr = new_dma_addr_list;
> + lp->rx_skbuff = new_skb_list;
> + return;
> +
> + free_all_new:
> + for (; --new >= lp->rx_ring_size; ) {
> + if (new_skb_list[new]) {
> + pci_unmap_single(lp->pci_dev, new_dma_addr_list[new],
> + PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
> + dev_kfree_skb(new_skb_list[new]);
> + }
> + }
> + kfree(new_skb_list);
> + free_new_lists:
> + kfree(new_dma_addr_list);
> + free_new_rx_ring:
> + pci_free_consistent(lp->pci_dev,
> + sizeof(struct pcnet32_rx_head) *
> + (1 << size),
> + new_rx_ring,
> + new_ring_dma_addr);
> + return;
> +}
> +
> #ifdef CONFIG_NET_POLL_CONTROLLER
> static void pcnet32_poll_controller(struct net_device *dev)
> {
> @@ -525,10 +733,10 @@ static void pcnet32_get_ringparam(struct
> {
> struct pcnet32_private *lp = dev->priv;
>
> - ering->tx_max_pending = TX_MAX_RING_SIZE - 1;
> - ering->tx_pending = lp->tx_ring_size - 1;
> - ering->rx_max_pending = RX_MAX_RING_SIZE - 1;
> - ering->rx_pending = lp->rx_ring_size - 1;
> + ering->tx_max_pending = TX_MAX_RING_SIZE;
> + ering->tx_pending = lp->tx_ring_size;
> + ering->rx_max_pending = RX_MAX_RING_SIZE;
> + ering->rx_pending = lp->rx_ring_size;
> }
>
> static int pcnet32_set_ringparam(struct net_device *dev,
> @@ -536,44 +744,44 @@ static int pcnet32_set_ringparam(struct
> {
> struct pcnet32_private *lp = dev->priv;
> unsigned long flags;
> + unsigned int size;
> + ulong ioaddr = dev->base_addr;
> int i;
>
> if (ering->rx_mini_pending || ering->rx_jumbo_pending)
> return -EINVAL;
>
> if (netif_running(dev))
> - pcnet32_close(dev);
> + pcnet32_netif_stop(dev);
>
> spin_lock_irqsave(&lp->lock, flags);
> - pcnet32_free_ring(dev);
> - lp->tx_ring_size =
> - min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);
> - lp->rx_ring_size =
> - min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE);
> + lp->a.write_csr(ioaddr, 0, 0x0004); /* stop the chip */
> +
> + size = min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);
>
> /* set the minimum ring size to 4, to allow the loopback test to work
> * unchanged.
> */
> for (i = 2; i <= PCNET32_LOG_MAX_TX_BUFFERS; i++) {
> - if (lp->tx_ring_size <= (1 << i))
> + if (size <= (1 << i))
> break;
> }
> - lp->tx_ring_size = (1 << i);
> - lp->tx_mod_mask = lp->tx_ring_size - 1;
> - lp->tx_len_bits = (i << 12);
> -
> + if ((1 << i) != lp->tx_ring_size)
> + pcnet32_realloc_tx_ring(dev, lp, i);
> +
> + size = min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE);
> for (i = 2; i <= PCNET32_LOG_MAX_RX_BUFFERS; i++) {
> - if (lp->rx_ring_size <= (1 << i))
> + if (size <= (1 << i))
> break;
> }
> - lp->rx_ring_size = (1 << i);
> - lp->rx_mod_mask = lp->rx_ring_size - 1;
> - lp->rx_len_bits = (i << 4);
> + if ((1 << i) != lp->rx_ring_size)
> + pcnet32_realloc_rx_ring(dev, lp, i);
> +
> + dev->weight = lp->rx_ring_size / 2;
>
> - if (pcnet32_alloc_ring(dev, dev->name)) {
> - pcnet32_free_ring(dev);
> - spin_unlock_irqrestore(&lp->lock, flags);
> - return -ENOMEM;
> + if (netif_running(dev)) {
> + pcnet32_netif_start(dev);
> + pcnet32_restart(dev, 0x0042);
> }
>
> spin_unlock_irqrestore(&lp->lock, flags);
> @@ -583,9 +791,6 @@ static int pcnet32_set_ringparam(struct
> "%s: Ring Param Settings: RX: %d, TX: %d\n", dev->name,
> lp->rx_ring_size, lp->tx_ring_size);
>
> - if (netif_running(dev))
> - pcnet32_open(dev);
> -
> return 0;
> }
>
> @@ -643,21 +848,20 @@ static int pcnet32_loopback_test(struct
> rc = 1; /* default to fail */
>
> if (netif_running(dev))
> - pcnet32_close(dev);
> + pcnet32_netif_stop(dev);
>
> spin_lock_irqsave(&lp->lock, flags);
> + lp->a.write_csr(ioaddr, 0, 0x0004); /* stop the chip */
> +
> + numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size));
>
> /* Reset the PCNET32 */
> lp->a.reset(ioaddr);
> + lp->a.write_csr(ioaddr, 4, 0x0915);
>
> /* switch pcnet32 to 32bit mode */
> lp->a.write_bcr(ioaddr, 20, 2);
>
> - lp->init_block.mode =
> - le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
> - lp->init_block.filter[0] = 0;
> - lp->init_block.filter[1] = 0;
> -
> /* purge & init rings but don't actually restart */
> pcnet32_restart(dev, 0x0000);
>
> @@ -704,10 +908,10 @@ static int pcnet32_loopback_test(struct
> }
>
> x = a->read_bcr(ioaddr, 32); /* set internal loopback in BSR32 */
> - x = x | 0x0002;
> - a->write_bcr(ioaddr, 32, x);
> + a->write_bcr(ioaddr, 32, x | 0x0002);
>
> - lp->a.write_csr(ioaddr, 15, 0x0044); /* set int loopback in CSR15 */
> + x = a->read_csr(ioaddr, 15); /* set int loopback in CSR15 */
> + lp->a.write_csr(ioaddr, 15, x | 0x0044);
>
> teststatus = le16_to_cpu(0x8000);
> lp->a.write_csr(ioaddr, 0, 0x0002); /* Set STRT bit */
> @@ -770,21 +974,26 @@ static int pcnet32_loopback_test(struct
>
> clean_up:
> pcnet32_purge_tx_ring(dev);
> + for (x = 0; x < numbuffs; x++) {
> + lp->rx_ring[x].buf_length = le16_to_cpu(2 - PKT_BUF_SZ);
> + wmb();
> + lp->rx_ring[x].status = le16_to_cpu(0x8000);
> + }
> x = a->read_csr(ioaddr, 15) & 0xFFFF;
> a->write_csr(ioaddr, 15, (x & ~0x0044)); /* reset bits 6 and 2 */
>
> x = a->read_bcr(ioaddr, 32); /* reset internal loopback */
> - x = x & ~0x0002;
> - a->write_bcr(ioaddr, 32, x);
> -
> - spin_unlock_irqrestore(&lp->lock, flags);
> + a->write_bcr(ioaddr, 32, (x & ~0x0002));
>
> if (netif_running(dev)) {
> - pcnet32_open(dev);
> + pcnet32_netif_start(dev);
> + pcnet32_restart(dev, 0x0042);
> } else {
> lp->a.write_bcr(ioaddr, 20, 4); /* return to 16bit mode */
> }
>
> + spin_unlock_irqrestore(&lp->lock, flags);
> +
> return (rc);
> } /* end pcnet32_loopback_test */
>
> @@ -855,6 +1064,39 @@ static int pcnet32_get_regs_len(struct n
> return ((PCNET32_NUM_REGS + j) * sizeof(u16));
> }
>
> +/*
> + * lp->lock must be held.
> + */
> +static int pcnet32_suspend(struct net_device *dev, unsigned long *flags)
> +{
> + int csr5;
> + struct pcnet32_private *lp = dev->priv;
> + struct pcnet32_access *a = &lp->a;
> + ulong ioaddr = dev->base_addr;
> + int ticks;
> +
> + /* set SUSPEND (SPND) - CSR5 bit 0 */
> + csr5 = a->read_csr(ioaddr, 5);
> + a->write_csr(ioaddr, 5, csr5 | 0x0001);
> +
> + /* poll waiting for bit to be set */
> + ticks = 0;
> + while (!(a->read_csr(ioaddr, 5) & 0x0001)) {
> + spin_unlock_irqrestore(&lp->lock, *flags);
> + mdelay(1);
> + spin_lock_irqsave(&lp->lock, *flags);
> + ticks++;
> + if (ticks > 200) {
> + if (netif_msg_hw(lp))
> + printk(KERN_DEBUG
> + "%s: Error getting into suspend!\n",
> + dev->name);
> + return 0;
> + }
> + }
> + return 1;
> +}
> +
> static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
> void *ptr)
> {
> @@ -863,31 +1105,17 @@ static void pcnet32_get_regs(struct net_
> struct pcnet32_private *lp = dev->priv;
> struct pcnet32_access *a = &lp->a;
> ulong ioaddr = dev->base_addr;
> - int ticks;
> unsigned long flags;
>
> spin_lock_irqsave(&lp->lock, flags);
>
> csr0 = a->read_csr(ioaddr, 0);
> if (!(csr0 & 0x0004)) { /* If not stopped */
> - /* set SUSPEND (SPND) - CSR5 bit 0 */
> - a->write_csr(ioaddr, 5, 0x0001);
> -
> - /* poll waiting for bit to be set */
> - ticks = 0;
> - while (!(a->read_csr(ioaddr, 5) & 0x0001)) {
> - spin_unlock_irqrestore(&lp->lock, flags);
> - mdelay(1);
> - spin_lock_irqsave(&lp->lock, flags);
> - ticks++;
> - if (ticks > 200) {
> - if (netif_msg_hw(lp))
> - printk(KERN_DEBUG
> - "%s: Error getting into suspend!\n",
> - dev->name);
> - break;
> - }
> - }
> + if (!pcnet32_suspend(dev, &flags))
> + if (netif_msg_hw(lp))
> + printk(KERN_DEBUG
> + "%s: Error getting into suspend!\n",
> + dev->name);
> }
>
> /* read address PROM */
> @@ -926,8 +1154,11 @@ static void pcnet32_get_regs(struct net_
> }
>
> if (!(csr0 & 0x0004)) { /* If not stopped */
> + int csr5;
> +
> /* clear SUSPEND (SPND) - CSR5 bit 0 */
> - a->write_csr(ioaddr, 5, 0x0000);
> + csr5 = a->read_csr(ioaddr, 5);
> + a->write_csr(ioaddr, 5, csr5 & (~0x0001));
> }
>
> spin_unlock_irqrestore(&lp->lock, flags);
> @@ -958,7 +1189,7 @@ static struct ethtool_ops pcnet32_ethtoo
> /* only probes for non-PCI devices, the rest are handled by
> * pci_register_driver via pcnet32_probe_pci */
>
> -static void __devinit pcnet32_probe_vlbus(void)
> +static void __devinit pcnet32_probe_vlbus(unsigned int *pcnet32_portlist)
> {
> unsigned int *port, ioaddr;
>
> @@ -1396,6 +1627,8 @@ pcnet32_probe1(unsigned long ioaddr, int
> dev->ethtool_ops = &pcnet32_ethtool_ops;
> dev->tx_timeout = pcnet32_tx_timeout;
> dev->watchdog_timeo = (5 * HZ);
> + dev->poll = pcnet32_poll;
> + dev->weight = lp->rx_ring_size / 2;
>
> #ifdef CONFIG_NET_POLL_CONTROLLER
> dev->poll_controller = pcnet32_poll_controller;
> @@ -2004,6 +2237,279 @@ static int pcnet32_start_xmit(struct sk_
> return 0;
> }
>
> +static int pcnet32_rx_entry(struct net_device *dev,
> + struct pcnet32_private *lp,
> + struct pcnet32_rx_head *rxp,
> + int entry)
> +{
> + int status = (short)le16_to_cpu(rxp->status) >> 8;
> + int rx_in_place = 0;
> + struct sk_buff *skb;
> + short pkt_len;
> +
> + if (status != 0x03) { /* There was an error. */
> + /*
> + * There is a tricky error noted by John Murphy,
> + * <murf@perftech.com> to Russ Nelson: Even with full-sized
> + * buffers it's possible for a jabber packet to use two
> + * buffers, with only the last correctly noting the error.
> + */
> + if (status & 0x01) /* Only count a general error at the */
> + lp->stats.rx_errors++; /* end of a packet. */
> + if (status & 0x20)
> + lp->stats.rx_frame_errors++;
> + if (status & 0x10)
> + lp->stats.rx_over_errors++;
> + if (status & 0x08)
> + lp->stats.rx_crc_errors++;
> + if (status & 0x04)
> + lp->stats.rx_fifo_errors++;
> + return 1;
> + }
> +
> + pkt_len = (le32_to_cpu(rxp->msg_length) & 0xfff) - 4;
> +
> + /* Discard oversize frames. */
> + if (unlikely(pkt_len > PKT_BUF_SZ - 2)) {
> + if (netif_msg_drv(lp))
> + printk(KERN_ERR "%s: Impossible packet size %d!\n",
> + dev->name, pkt_len);
> + lp->stats.rx_errors++;
> + return 1;
> + }
> + if (pkt_len < 60) {
> + if (netif_msg_rx_err(lp))
> + printk(KERN_ERR "%s: Runt packet!\n", dev->name);
> + lp->stats.rx_errors++;
> + return 1;
> + }
> +
> + if (pkt_len > rx_copybreak) {
> + struct sk_buff *newskb;
> +
> + if ((newskb = dev_alloc_skb(PKT_BUF_SZ))) {
> + skb_reserve(newskb, 2);
> + skb = lp->rx_skbuff[entry];
> + pci_unmap_single(lp->pci_dev,
> + lp->rx_dma_addr[entry],
> + PKT_BUF_SZ - 2,
> + PCI_DMA_FROMDEVICE);
> + skb_put(skb, pkt_len);
> + lp->rx_skbuff[entry] = newskb;
> + newskb->dev = dev;
> + lp->rx_dma_addr[entry] =
> + pci_map_single(lp->pci_dev,
> + newskb->data,
> + PKT_BUF_SZ - 2,
> + PCI_DMA_FROMDEVICE);
> + rxp->base = le32_to_cpu(lp->rx_dma_addr[entry]);
> + rx_in_place = 1;
> + } else
> + skb = NULL;
> + } else {
> + skb = dev_alloc_skb(pkt_len + 2);
> + }
> +
> + if (skb == NULL) {
> + if (netif_msg_drv(lp))
> + printk(KERN_ERR
> + "%s: Memory squeeze, dropping packet.\n",
> + dev->name);
> + lp->stats.rx_dropped++;
> + return 1;
> + }
> + skb->dev = dev;
> + if (!rx_in_place) {
> + skb_reserve(skb, 2); /* 16 byte align */
> + skb_put(skb, pkt_len); /* Make room */
> + pci_dma_sync_single_for_cpu(lp->pci_dev,
> + lp->rx_dma_addr[entry],
> + PKT_BUF_SZ - 2,
> + PCI_DMA_FROMDEVICE);
> + eth_copy_and_sum(skb,
> + (unsigned char *)(lp->rx_skbuff[entry]->data),
> + pkt_len, 0);
> + pci_dma_sync_single_for_device(lp->pci_dev,
> + lp->rx_dma_addr[entry],
> + PKT_BUF_SZ - 2,
> + PCI_DMA_FROMDEVICE);
> + }
> + lp->stats.rx_bytes += skb->len;
> + lp->stats.rx_packets++;
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + dev->last_rx = jiffies;
> + return 1;
> +}
> +
> +static int pcnet32_rx(struct net_device *dev, int quota)
> +{
> + struct pcnet32_private *lp = dev->priv;
> + int entry = lp->cur_rx & lp->rx_mod_mask;
> + struct pcnet32_rx_head *rxp = &lp->rx_ring[entry];
> + int npackets = 0;
> +
> + /* If we own the next entry, it's a new packet. Send it up. */
> + while (quota > npackets && (short)le16_to_cpu(rxp->status) >= 0) {
> + npackets += pcnet32_rx_entry(dev, lp, rxp, entry);
> + /*
> + * The docs say that the buffer length isn't touched, but Andrew
> + * Boyd of QNX reports that some revs of the 79C965 clear it.
> + */
> + rxp->buf_length = le16_to_cpu(2 - PKT_BUF_SZ);
> + wmb(); /* Make sure owner changes after others are visible */
> + rxp->status = le16_to_cpu(0x8000);
> + entry = (++lp->cur_rx) & lp->rx_mod_mask;
> + rxp = &lp->rx_ring[entry];
> + }
> +
> + return npackets;
> +}
> +
> +static int pcnet32_tx(struct net_device *dev)
> +{
> + struct pcnet32_private *lp = dev->priv;
> + unsigned int dirty_tx = lp->dirty_tx;
> + int delta;
> + int must_restart = 0;
> +
> + while (dirty_tx != lp->cur_tx) {
> + int entry = dirty_tx & lp->tx_mod_mask;
> + int status = (short)le16_to_cpu(lp->tx_ring[entry].status);
> +
> + if (status < 0)
> + break; /* It still hasn't been Txed */
> +
> + lp->tx_ring[entry].base = 0;
> +
> + if (status & 0x4000) {
> + /* There was an major error, log it. */
> + int err_status =
> + le32_to_cpu(lp->tx_ring[entry].
> + misc);
> + lp->stats.tx_errors++;
> + if (netif_msg_tx_err(lp))
> + printk(KERN_ERR
> + "%s: Tx error status=%04x err_status=%08x\n",
> + dev->name, status,
> + err_status);
> + if (err_status & 0x04000000)
> + lp->stats.tx_aborted_errors++;
> + if (err_status & 0x08000000)
> + lp->stats.tx_carrier_errors++;
> + if (err_status & 0x10000000)
> + lp->stats.tx_window_errors++;
> +#ifndef DO_DXSUFLO
> + if (err_status & 0x40000000) {
> + lp->stats.tx_fifo_errors++;
> + /* Ackk! On FIFO errors the Tx unit is turned off! */
> + /* Remove this verbosity later! */
> + if (netif_msg_tx_err(lp))
> + printk(KERN_ERR
> + "%s: Tx FIFO error!\n",
> + dev->name);
> + must_restart = 1;
> + }
> +#else
> + if (err_status & 0x40000000) {
> + lp->stats.tx_fifo_errors++;
> + if (!lp->dxsuflo) { /* If controller doesn't recover ... */
> + /* Ackk! On FIFO errors the Tx unit is turned off! */
> + /* Remove this verbosity later! */
> + if (netif_msg_tx_err(lp))
> + printk(KERN_ERR
> + "%s: Tx FIFO error!\n",
> + dev->name);
> + must_restart = 1;
> + }
> + }
> +#endif
> + } else {
> + if (status & 0x1800)
> + lp->stats.collisions++;
> + lp->stats.tx_packets++;
> + }
> +
> + /* We must free the original skb */
> + if (lp->tx_skbuff[entry]) {
> + pci_unmap_single(lp->pci_dev,
> + lp->tx_dma_addr[entry],
> + lp->tx_skbuff[entry]->
> + len, PCI_DMA_TODEVICE);
> + dev_kfree_skb_any(lp->tx_skbuff[entry]);
> + lp->tx_skbuff[entry] = NULL;
> + lp->tx_dma_addr[entry] = 0;
> + }
> + dirty_tx++;
> + }
> +
> + delta = (lp->cur_tx - dirty_tx) & (lp->tx_mod_mask + lp->tx_ring_size);
> + if (delta > lp->tx_ring_size) {
> + if (netif_msg_drv(lp))
> + printk(KERN_ERR
> + "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
> + dev->name, dirty_tx, lp->cur_tx,
> + lp->tx_full);
> + dirty_tx += lp->tx_ring_size;
> + delta -= lp->tx_ring_size;
> + }
> +
> + if (lp->tx_full &&
> + netif_queue_stopped(dev) &&
> + delta < lp->tx_ring_size - 2) {
> + /* The ring is no longer full, clear tbusy. */
> + lp->tx_full = 0;
> + netif_wake_queue(dev);
> + }
> + lp->dirty_tx = dirty_tx;
> +
> + return must_restart;
> +}
> +
> +static int pcnet32_poll(struct net_device *dev, int *budget)
> +{
> + struct pcnet32_private *lp = dev->priv;
> + int quota = min(dev->quota, *budget);
> + unsigned long ioaddr = dev->base_addr;
> + u16 val;
> + unsigned long flags;
> +
> + quota = pcnet32_rx(dev, quota);
> +
> + spin_lock_irqsave(&lp->lock, flags);
> + if (pcnet32_tx(dev)) {
> + /* reset the chip to clear the error condition, then restart */
> + lp->a.reset(ioaddr);
> + lp->a.write_csr(ioaddr, 4, 0x0915);
> + pcnet32_restart(dev, 0x0002);
> + netif_wake_queue(dev);
> + }
> + spin_unlock_irqrestore(&lp->lock, flags);
> +
> + *budget -= quota;
> + dev->quota -= quota;
> +
> + if (dev->quota == 0) {
> + return 1;
> + }
> +
> + netif_rx_complete(dev);
> +
> + spin_lock_irqsave(&lp->lock, flags);
> +
> + /* clear interrupt masks */
> + val = lp->a.read_csr(ioaddr, 3);
> + val &= 0x00ff;
> + lp->a.write_csr(ioaddr, 3, val);
> +
> + /* Set interrupt enable. */
> + lp->a.write_csr(ioaddr, 0, 0x0040);
> +
> + spin_unlock_irqrestore(&lp->lock, flags);
> +
> + return 0;
> +}
> +
> /* The PCNET32 interrupt handler. */
> static irqreturn_t
> pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs)
> @@ -2011,9 +2517,8 @@ pcnet32_interrupt(int irq, void *dev_id,
> struct net_device *dev = dev_id;
> struct pcnet32_private *lp;
> unsigned long ioaddr;
> - u16 csr0, rap;
> - int boguscnt = max_interrupt_work;
> - int must_restart;
> + u16 csr0;
> + irqreturn_t rc = IRQ_HANDLED;
>
> if (!dev) {
> if (pcnet32_debug & NETIF_MSG_INTR)
> @@ -2027,124 +2532,27 @@ pcnet32_interrupt(int irq, void *dev_id,
>
> spin_lock(&lp->lock);
>
> - rap = lp->a.read_rap(ioaddr);
> - while ((csr0 = lp->a.read_csr(ioaddr, 0)) & 0x8f00 && --boguscnt >= 0) {
> - if (csr0 == 0xffff) {
> - break; /* PCMCIA remove happened */
> - }
> + csr0 = lp->a.read_csr(ioaddr, 0);
> + if (csr0 == 0xffff) {
> + rc = IRQ_NONE;
> + } else if (csr0 & 0x8f00) {
> /* Acknowledge all of the current interrupt sources ASAP. */
> lp->a.write_csr(ioaddr, 0, csr0 & ~0x004f);
>
> - must_restart = 0;
> -
> if (netif_msg_intr(lp))
> printk(KERN_DEBUG
> "%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
> dev->name, csr0, lp->a.read_csr(ioaddr, 0));
>
> - if (csr0 & 0x0400) /* Rx interrupt */
> - pcnet32_rx(dev);
> -
> - if (csr0 & 0x0200) { /* Tx-done interrupt */
> - unsigned int dirty_tx = lp->dirty_tx;
> - int delta;
> -
> - while (dirty_tx != lp->cur_tx) {
> - int entry = dirty_tx & lp->tx_mod_mask;
> - int status =
> - (short)le16_to_cpu(lp->tx_ring[entry].
> - status);
> -
> - if (status < 0)
> - break; /* It still hasn't been Txed */
> -
> - lp->tx_ring[entry].base = 0;
> -
> - if (status & 0x4000) {
> - /* There was an major error, log it. */
> - int err_status =
> - le32_to_cpu(lp->tx_ring[entry].
> - misc);
> - lp->stats.tx_errors++;
> - if (netif_msg_tx_err(lp))
> - printk(KERN_ERR
> - "%s: Tx error status=%04x err_status=%08x\n",
> - dev->name, status,
> - err_status);
> - if (err_status & 0x04000000)
> - lp->stats.tx_aborted_errors++;
> - if (err_status & 0x08000000)
> - lp->stats.tx_carrier_errors++;
> - if (err_status & 0x10000000)
> - lp->stats.tx_window_errors++;
> -#ifndef DO_DXSUFLO
> - if (err_status & 0x40000000) {
> - lp->stats.tx_fifo_errors++;
> - /* Ackk! On FIFO errors the Tx unit is turned off! */
> - /* Remove this verbosity later! */
> - if (netif_msg_tx_err(lp))
> - printk(KERN_ERR
> - "%s: Tx FIFO error! CSR0=%4.4x\n",
> - dev->name, csr0);
> - must_restart = 1;
> - }
> -#else
> - if (err_status & 0x40000000) {
> - lp->stats.tx_fifo_errors++;
> - if (!lp->dxsuflo) { /* If controller doesn't recover ... */
> - /* Ackk! On FIFO errors the Tx unit is turned off! */
> - /* Remove this verbosity later! */
> - if (netif_msg_tx_err
> - (lp))
> - printk(KERN_ERR
> - "%s: Tx FIFO error! CSR0=%4.4x\n",
> - dev->
> - name,
> - csr0);
> - must_restart = 1;
> - }
> - }
> -#endif
> - } else {
> - if (status & 0x1800)
> - lp->stats.collisions++;
> - lp->stats.tx_packets++;
> - }
> -
> - /* We must free the original skb */
> - if (lp->tx_skbuff[entry]) {
> - pci_unmap_single(lp->pci_dev,
> - lp->tx_dma_addr[entry],
> - lp->tx_skbuff[entry]->
> - len, PCI_DMA_TODEVICE);
> - dev_kfree_skb_irq(lp->tx_skbuff[entry]);
> - lp->tx_skbuff[entry] = NULL;
> - lp->tx_dma_addr[entry] = 0;
> - }
> - dirty_tx++;
> - }
> -
> - delta =
> - (lp->cur_tx - dirty_tx) & (lp->tx_mod_mask +
> - lp->tx_ring_size);
> - if (delta > lp->tx_ring_size) {
> - if (netif_msg_drv(lp))
> - printk(KERN_ERR
> - "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
> - dev->name, dirty_tx, lp->cur_tx,
> - lp->tx_full);
> - dirty_tx += lp->tx_ring_size;
> - delta -= lp->tx_ring_size;
> - }
> -
> - if (lp->tx_full &&
> - netif_queue_stopped(dev) &&
> - delta < lp->tx_ring_size - 2) {
> - /* The ring is no longer full, clear tbusy. */
> - lp->tx_full = 0;
> - netif_wake_queue(dev);
> - }
> - lp->dirty_tx = dirty_tx;
> + if (netif_rx_schedule_prep(dev)) {
> + u16 val;
> + /* set interrupt masks */
> + val = lp->a.read_csr(ioaddr, 3);
> + val |= 0x5f00;
> + lp->a.write_csr(ioaddr, 3, val);
> + __netif_rx_schedule(dev);
> + } else {
> + printk(KERN_DEBUG "%s: interrupt while in polling mode.\n", dev->name);
> }
>
> /* Log misc errors. */
> @@ -2152,16 +2560,16 @@ pcnet32_interrupt(int irq, void *dev_id,
> lp->stats.tx_errors++; /* Tx babble. */
> if (csr0 & 0x1000) {
> /*
> - * this happens when our receive ring is full. This shouldn't
> - * be a problem as we will see normal rx interrupts for the frames
> - * in the receive ring. But there are some PCI chipsets (I can
> - * reproduce this on SP3G with Intel saturn chipset) which have
> - * sometimes problems and will fill up the receive ring with
> - * error descriptors. In this situation we don't get a rx
> - * interrupt, but a missed frame interrupt sooner or later.
> - * So we try to clean up our receive ring here.
> + * This happens when our receive ring is full. This
> + * shouldn't be a problem as we will see normal rx
> + * interrupts for the frames in the receive ring. But
> + * there are some PCI chipsets (I can reproduce this
> + * on SP3G with Intel saturn chipset) which have
> + * sometimes problems and will fill up the receive
> + * ring with error descriptors. In this situation we
> + * don't get a rx interrupt, but a missed frame
> + * interrupt sooner or later.
> */
> - pcnet32_rx(dev);
> lp->stats.rx_errors++; /* Missed a Rx frame. */
> }
> if (csr0 & 0x0800) {
> @@ -2171,183 +2579,15 @@ pcnet32_interrupt(int irq, void *dev_id,
> dev->name, csr0);
> /* unlike for the lance, there is no restart needed */
> }
> -
> - if (must_restart) {
> - /* reset the chip to clear the error condition, then restart */
> - lp->a.reset(ioaddr);
> - lp->a.write_csr(ioaddr, 4, 0x0915);
> - pcnet32_restart(dev, 0x0002);
> - netif_wake_queue(dev);
> - }
> }
>
> - /* Set interrupt enable. */
> - lp->a.write_csr(ioaddr, 0, 0x0040);
> - lp->a.write_rap(ioaddr, rap);
> -
> if (netif_msg_intr(lp))
> printk(KERN_DEBUG "%s: exiting interrupt, csr0=%#4.4x.\n",
> dev->name, lp->a.read_csr(ioaddr, 0));
>
> spin_unlock(&lp->lock);
>
> - return IRQ_HANDLED;
> -}
> -
> -static int pcnet32_rx(struct net_device *dev)
> -{
> - struct pcnet32_private *lp = dev->priv;
> - int entry = lp->cur_rx & lp->rx_mod_mask;
> - int boguscnt = lp->rx_ring_size / 2;
> -
> - /* If we own the next entry, it's a new packet. Send it up. */
> - while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
> - int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
> -
> - if (status != 0x03) { /* There was an error. */
> - /*
> - * There is a tricky error noted by John Murphy,
> - * <murf@perftech.com> to Russ Nelson: Even with full-sized
> - * buffers it's possible for a jabber packet to use two
> - * buffers, with only the last correctly noting the error.
> - */
> - if (status & 0x01) /* Only count a general error at the */
> - lp->stats.rx_errors++; /* end of a packet. */
> - if (status & 0x20)
> - lp->stats.rx_frame_errors++;
> - if (status & 0x10)
> - lp->stats.rx_over_errors++;
> - if (status & 0x08)
> - lp->stats.rx_crc_errors++;
> - if (status & 0x04)
> - lp->stats.rx_fifo_errors++;
> - lp->rx_ring[entry].status &= le16_to_cpu(0x03ff);
> - } else {
> - /* Malloc up new buffer, compatible with net-2e. */
> - short pkt_len =
> - (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)
> - - 4;
> - struct sk_buff *skb;
> -
> - /* Discard oversize frames. */
> - if (unlikely(pkt_len > PKT_BUF_SZ - 2)) {
> - if (netif_msg_drv(lp))
> - printk(KERN_ERR
> - "%s: Impossible packet size %d!\n",
> - dev->name, pkt_len);
> - lp->stats.rx_errors++;
> - } else if (pkt_len < 60) {
> - if (netif_msg_rx_err(lp))
> - printk(KERN_ERR "%s: Runt packet!\n",
> - dev->name);
> - lp->stats.rx_errors++;
> - } else {
> - int rx_in_place = 0;
> -
> - if (pkt_len > rx_copybreak) {
> - struct sk_buff *newskb;
> -
> - if ((newskb =
> - dev_alloc_skb(PKT_BUF_SZ))) {
> - skb_reserve(newskb, 2);
> - skb = lp->rx_skbuff[entry];
> - pci_unmap_single(lp->pci_dev,
> - lp->
> - rx_dma_addr
> - [entry],
> - PKT_BUF_SZ - 2,
> - PCI_DMA_FROMDEVICE);
> - skb_put(skb, pkt_len);
> - lp->rx_skbuff[entry] = newskb;
> - newskb->dev = dev;
> - lp->rx_dma_addr[entry] =
> - pci_map_single(lp->pci_dev,
> - newskb->data,
> - PKT_BUF_SZ -
> - 2,
> - PCI_DMA_FROMDEVICE);
> - lp->rx_ring[entry].base =
> - le32_to_cpu(lp->
> - rx_dma_addr
> - [entry]);
> - rx_in_place = 1;
> - } else
> - skb = NULL;
> - } else {
> - skb = dev_alloc_skb(pkt_len + 2);
> - }
> -
> - if (skb == NULL) {
> - int i;
> - if (netif_msg_drv(lp))
> - printk(KERN_ERR
> - "%s: Memory squeeze, deferring packet.\n",
> - dev->name);
> - for (i = 0; i < lp->rx_ring_size; i++)
> - if ((short)
> - le16_to_cpu(lp->
> - rx_ring[(entry +
> - i)
> - & lp->
> - rx_mod_mask].
> - status) < 0)
> - break;
> -
> - if (i > lp->rx_ring_size - 2) {
> - lp->stats.rx_dropped++;
> - lp->rx_ring[entry].status |=
> - le16_to_cpu(0x8000);
> - wmb(); /* Make sure adapter sees owner change */
> - lp->cur_rx++;
> - }
> - break;
> - }
> - skb->dev = dev;
> - if (!rx_in_place) {
> - skb_reserve(skb, 2); /* 16 byte align */
> - skb_put(skb, pkt_len); /* Make room */
> - pci_dma_sync_single_for_cpu(lp->pci_dev,
> - lp->
> - rx_dma_addr
> - [entry],
> - PKT_BUF_SZ -
> - 2,
> - PCI_DMA_FROMDEVICE);
> - eth_copy_and_sum(skb,
> - (unsigned char *)(lp->
> - rx_skbuff
> - [entry]->
> - data),
> - pkt_len, 0);
> - pci_dma_sync_single_for_device(lp->
> - pci_dev,
> - lp->
> - rx_dma_addr
> - [entry],
> - PKT_BUF_SZ
> - - 2,
> - PCI_DMA_FROMDEVICE);
> - }
> - lp->stats.rx_bytes += skb->len;
> - skb->protocol = eth_type_trans(skb, dev);
> - netif_rx(skb);
> - dev->last_rx = jiffies;
> - lp->stats.rx_packets++;
> - }
> - }
> - /*
> - * The docs say that the buffer length isn't touched, but Andrew Boyd
> - * of QNX reports that some revs of the 79C965 clear it.
> - */
> - lp->rx_ring[entry].buf_length = le16_to_cpu(2 - PKT_BUF_SZ);
> - wmb(); /* Make sure owner changes after all others are visible */
> - lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
> - entry = (++lp->cur_rx) & lp->rx_mod_mask;
> - if (--boguscnt <= 0)
> - break; /* don't stay in loop forever */
> - }
> -
> - return 0;
> + return rc;
> }
>
> static int pcnet32_close(struct net_device *dev)
> @@ -2420,13 +2660,10 @@ static struct net_device_stats *pcnet32_
> {
> struct pcnet32_private *lp = dev->priv;
> unsigned long ioaddr = dev->base_addr;
> - u16 saved_addr;
> unsigned long flags;
>
> spin_lock_irqsave(&lp->lock, flags);
> - saved_addr = lp->a.read_rap(ioaddr);
> lp->stats.rx_missed_errors = lp->a.read_csr(ioaddr, 112);
> - lp->a.write_rap(ioaddr, saved_addr);
> spin_unlock_irqrestore(&lp->lock, flags);
>
> return &lp->stats;
> @@ -2439,6 +2676,7 @@ static void pcnet32_load_multicast(struc
> volatile struct pcnet32_init_block *ib = &lp->init_block;
> volatile u16 *mcast_table = (u16 *) & ib->filter;
> struct dev_mc_list *dmi = dev->mc_list;
> + unsigned long ioaddr = dev->base_addr;
> char *addrs;
> int i;
> u32 crc;
> @@ -2447,6 +2685,10 @@ static void pcnet32_load_multicast(struc
> if (dev->flags & IFF_ALLMULTI) {
> ib->filter[0] = 0xffffffff;
> ib->filter[1] = 0xffffffff;
> + lp->a.write_csr(ioaddr, 8, 0xffff);
> + lp->a.write_csr(ioaddr, 9, 0xffff);
> + lp->a.write_csr(ioaddr, 10, 0xffff);
> + lp->a.write_csr(ioaddr, 11, 0xffff);
> return;
> }
> /* clear the multicast filter */
> @@ -2468,6 +2710,8 @@ static void pcnet32_load_multicast(struc
> le16_to_cpu(le16_to_cpu(mcast_table[crc >> 4]) |
> (1 << (crc & 0xf)));
> }
> + for (i=0; i<4; i++)
Nit, inconstient style
> + lp->a.write_csr(ioaddr, 8+i, le16_to_cpu(mcast_table[i]));
> return;
> }
>
> @@ -2478,8 +2722,11 @@ static void pcnet32_set_multicast_list(s
> {
> unsigned long ioaddr = dev->base_addr, flags;
> struct pcnet32_private *lp = dev->priv;
> + int csr15, suspended;
>
> spin_lock_irqsave(&lp->lock, flags);
> + suspended = pcnet32_suspend(dev, &flags);
> + csr15 = lp->a.read_csr(ioaddr, 15);
> if (dev->flags & IFF_PROMISC) {
> /* Log any net taps. */
> if (netif_msg_hw(lp))
> @@ -2488,15 +2735,24 @@ static void pcnet32_set_multicast_list(s
> lp->init_block.mode =
> le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) <<
> 7);
> + lp->a.write_csr(ioaddr, 15, csr15 | 0x8000);
> } else {
> lp->init_block.mode =
> le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
> + lp->a.write_csr(ioaddr, 15, csr15 & 0x7fff);
> pcnet32_load_multicast(dev);
> }
>
> - lp->a.write_csr(ioaddr, 0, 0x0004); /* Temporarily stop the lance. */
> - pcnet32_restart(dev, 0x0042); /* Resume normal operation */
> - netif_wake_queue(dev);
> + if (suspended) {
> + int csr5;
> + /* clear SUSPEND (SPND) - CSR5 bit 0 */
> + csr5 = lp->a.read_csr(ioaddr, 5);
> + lp->a.write_csr(ioaddr, 5, csr5 & (~0x0001));
> + } else {
> + lp->a.write_csr(ioaddr, 0, 0x0004); /* stop the lance. */
> + pcnet32_restart(dev, 0x0042); /* Resume normal operation */
> + netif_wake_queue(dev);
> + }
>
> spin_unlock_irqrestore(&lp->lock, flags);
> }
> @@ -2736,7 +2992,7 @@ static int __init pcnet32_init_module(vo
>
> /* should we find any remaining VLbus devices ? */
> if (pcnet32vlb)
> - pcnet32_probe_vlbus();
> + pcnet32_probe_vlbus(pcnet32_portlist);
>
> if (cards_found && (pcnet32_debug & NETIF_MSG_PROBE))
> printk(KERN_INFO PFX "%d cards_found.\n", cards_found);
>
> --
> Don Fry
> brazilnut@us.ibm.com
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH, RFT] bcm43xx: Busting the 1G limit
From: Michael Buesch @ 2006-06-19 20:43 UTC (permalink / raw)
To: Daniel Gryniewicz; +Cc: bcm43xx-dev, netdev
In-Reply-To: <1150730630.9048.2.camel@athena.fprintf.net>
On Monday 19 June 2006 17:23, Daniel Gryniewicz wrote:
> On Sat, 2006-06-17 at 19:28 +0200, Michael Buesch wrote:
> > Hi,
> >
> > This patch adds full 32-bit and 64-bit DMA support
> > to the bcm43xx driver. Well, it _should_ do this. I can
> > not test it, as I don't have a machine to trigger the 1G
> > limit.
> > The 1G limit should be exploitable on an AMD64 machine
> > with more than 1G RAM.
> >
> > Please test and report, if it works or not. In the
> > case of "works not", please provide full dmesg log.
> >
> > Note that I am not sure which cards actually support
> > full 32-bit or even 64-bit mode. Older cards might still
> > only support 30-bit DMA.
>
> Hi.
>
> I tried this on both 2.6.17-rc6 and on wireless-dev, and got pretty much
> the same panic on both (modulo locking). My box is a turion with 2 GB
> of ram and a BCM4318. Here's the panic from wireless-dev:
>
> Unable to handle kernel NULL pointer dereference at 0000000000000020
> RIP:
> <ffffffff88104f24>{:bcm43xx:bcm43xx_dma_handle_xmitstatus+436}
I am still not absolutely sure where this oops comes from.
Could you remove at least 1G of your RAM and retry?
--
Greetings Michael.
^ permalink raw reply
* Re: [RFT] pcnet32 NAPI changes
From: Lennart Sorensen @ 2006-06-19 20:49 UTC (permalink / raw)
To: Don Fry, netdev
In-Reply-To: <20060619204140.GC10036@us.ibm.com>
On Mon, Jun 19, 2006 at 03:41:40PM -0500, Jon Mason wrote:
> I believe it is preferred to be a compile option for non-gigabit
> drivers, given that it will be eating a lot of cycles for infrequent
> packets (especially for the 10Mb). I believe there was a thread about
> this last year when e100 was having NAPI problems.
How does NAPI eat cycles? It goes back to interrupt mode when the queue
is empty, and only on RX interrupt does it turn on polling again.
It is certainly possible that there are bugs in a NAPI conversion, which
I guess could be a reason to have the option to stick with the old
method, although then again not having the option ensures the bugs get
found sooner.
> A general nit. There are ALOT of magic numbers in the code, most
> existing prior to this patch. The driver would benefit from a little
> clean-up.
>
> Also nothing to do with this patch, but I noticed it when the code was
> moved. A comment about why the following is necessary might be nice:
> lp->rx_ring[i].buf_length = le16_to_cpu(2 - PKT_BUF_SZ);
I suspect many drivers are in need of some cleanup.
Len Sorensen
^ permalink raw reply
* Re: Network performance degradation from 2.6.11.12 to 2.6.16.20
From: Andi Kleen @ 2006-06-19 20:39 UTC (permalink / raw)
To: Chris Friesen; +Cc: Jesper Dangaard Brouer, Harry Edmon, linux-kernel, netdev
In-Reply-To: <4496E041.5070501@nortel.com>
On Monday 19 June 2006 19:34, Chris Friesen wrote:
> Andi Kleen wrote:
> > Incoming packets are only time stamped
> > when someone asks for the timestamps.
>
> Doesn't that add scheduling latency to the timestamps? Or is is a flag
> that gets set to trigger timestamping at packet arrival?
It's a flag (or more precise a global counter)
-Andi
^ permalink raw reply
* [PATCH] bcm43xx-d80211: AccessPoint mode related fixes
From: Michael Buesch @ 2006-06-19 21:59 UTC (permalink / raw)
To: linville; +Cc: netdev, bcm43xx-dev
Hi John,
Please apply this to wireless-dev.
There is no real reason to delay it, even _if_ there might
be still bugs in it. It's a development tree. That's what it is for. ;)
--
Get AccessPoint mode working in bcm43xx-d80211.
This patch is derived from Alexander Tsvyashchenko's original
patch. I (mb) extended it by endianess fixes and other bugfixes.
From: "Alexander Tsvyashchenko" <lists@ndl.kiev.ua>
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c 2006-06-17 21:26:10.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c 2006-06-19 11:25:02.000000000 +0200
@@ -151,8 +151,10 @@
{
u32 status;
+ assert(offset % 4 == 0);
+
status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
- if (!(status & BCM43xx_SBF_XFER_REG_BYTESWAP))
+ if (status & BCM43xx_SBF_XFER_REG_BYTESWAP)
val = swab32(val);
bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_CONTROL, offset);
@@ -312,7 +314,7 @@
}
}
-void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf)
+static void bcm43xx_time_lock(struct bcm43xx_private *bcm)
{
u32 status;
@@ -320,7 +322,19 @@
status |= BCM43xx_SBF_TIME_UPDATE;
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
mmiowb();
+}
+
+static void bcm43xx_time_unlock(struct bcm43xx_private *bcm)
+{
+ u32 status;
+
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ status &= ~BCM43xx_SBF_TIME_UPDATE;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+}
+static void bcm43xx_tsf_write_locked(struct bcm43xx_private *bcm, u64 tsf)
+{
/* Be careful with the in-progress timer.
* First zero out the low register, so we have a full
* register-overflow duration to complete the operation.
@@ -350,10 +364,13 @@
mmiowb();
bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, v0);
}
+}
- status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
- status &= ~BCM43xx_SBF_TIME_UPDATE;
- bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf)
+{
+ bcm43xx_time_lock(bcm);
+ bcm43xx_tsf_write_locked(bcm, tsf);
+ bcm43xx_time_unlock(bcm);
}
static void bcm43xx_measure_channel_change_time(struct bcm43xx_private *bcm)
@@ -415,10 +432,11 @@
static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_private *bcm)
{
static const u8 zero_addr[ETH_ALEN] = { 0 };
- const u8 *mac = NULL;
- const u8 *bssid = NULL;
+ const u8 *mac;
+ const u8 *bssid;
u8 mac_bssid[ETH_ALEN * 2];
int i;
+ u32 tmp;
bssid = bcm->interface.bssid;
if (!bssid)
@@ -431,12 +449,13 @@
memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN);
/* Write our MAC address and BSSID to template ram */
- for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))
- bcm43xx_ram_write(bcm, 0x20 + i, *((u32 *)(mac_bssid + i)));
- for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))
- bcm43xx_ram_write(bcm, 0x78 + i, *((u32 *)(mac_bssid + i)));
- for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))
- bcm43xx_ram_write(bcm, 0x478 + i, *((u32 *)(mac_bssid + i)));
+ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) {
+ tmp = (u32)(mac_bssid[i + 0]);
+ tmp |= (u32)(mac_bssid[i + 1]) << 8;
+ tmp |= (u32)(mac_bssid[i + 2]) << 16;
+ tmp |= (u32)(mac_bssid[i + 3]) << 24;
+ bcm43xx_ram_write(bcm, 0x20 + i, tmp);
+ }
}
static void bcm43xx_set_slot_time(struct bcm43xx_private *bcm, u16 slot_time)
@@ -460,49 +479,6 @@
bcm->short_slot = 0;
}
-/* FIXME: To get the MAC-filter working, we need to implement the
- * following functions (and rename them :)
- */
-#if 0
-static void bcm43xx_disassociate(struct bcm43xx_private *bcm)
-{
- bcm43xx_mac_suspend(bcm);
- bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
-
- bcm43xx_ram_write(bcm, 0x0026, 0x0000);
- bcm43xx_ram_write(bcm, 0x0028, 0x0000);
- bcm43xx_ram_write(bcm, 0x007E, 0x0000);
- bcm43xx_ram_write(bcm, 0x0080, 0x0000);
- bcm43xx_ram_write(bcm, 0x047E, 0x0000);
- bcm43xx_ram_write(bcm, 0x0480, 0x0000);
-
- if (bcm->current_core->rev < 3) {
- bcm43xx_write16(bcm, 0x0610, 0x8000);
- bcm43xx_write16(bcm, 0x060E, 0x0000);
- } else
- bcm43xx_write32(bcm, 0x0188, 0x80000000);
-
- bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);
-
-#if 0
- if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G &&
- ieee80211_is_ofdm_rate(bcm->softmac->txrates.default_rate))
- bcm43xx_short_slot_timing_enable(bcm);
-#endif
-
- bcm43xx_mac_enable(bcm);
-}
-
-static void bcm43xx_associate(struct bcm43xx_private *bcm,
- const u8 *mac)
-{
- bcm43xx_mac_suspend(bcm);
- bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_ASSOC, mac);
- bcm43xx_write_mac_bssid_templates(bcm);
- bcm43xx_mac_enable(bcm);
-}
-#endif
-
/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable.
* Returns the _previously_ enabled IRQ mask.
*/
@@ -758,12 +734,29 @@
return -ENODEV;
}
+#ifdef CONFIG_BCM947XX
+static void bcm43xx_aton(const char *str, char *dest)
+{
+ int i = 0;
+ u16 *d = (u16 *)dest;
+
+ for (;;) {
+ dest[i++] = (char)simple_strtoul(str, NULL, 16);
+ str += 2;
+ if (!*str++ || i == 6)
+ break;
+ }
+ for (i = 0; i < 3; i++)
+ d[i] = be16_to_cpu(d[i]);
+}
+#endif
+
static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm)
{
u16 value;
u16 *sprom;
#ifdef CONFIG_BCM947XX
- char *c;
+ const char *c;
#endif
sprom = kzalloc(BCM43xx_SPROM_SIZE * sizeof(u16),
@@ -772,27 +765,46 @@
printk(KERN_ERR PFX "sprom_extract OOM\n");
return -ENOMEM;
}
-#ifdef CONFIG_BCM947XX
- sprom[BCM43xx_SPROM_BOARDFLAGS2] = atoi(nvram_get("boardflags2"));
- sprom[BCM43xx_SPROM_BOARDFLAGS] = atoi(nvram_get("boardflags"));
-
- if ((c = nvram_get("il0macaddr")) != NULL)
- e_aton(c, (char *) &(sprom[BCM43xx_SPROM_IL0MACADDR]));
-
- if ((c = nvram_get("et1macaddr")) != NULL)
- e_aton(c, (char *) &(sprom[BCM43xx_SPROM_ET1MACADDR]));
-
- sprom[BCM43xx_SPROM_PA0B0] = atoi(nvram_get("pa0b0"));
- sprom[BCM43xx_SPROM_PA0B1] = atoi(nvram_get("pa0b1"));
- sprom[BCM43xx_SPROM_PA0B2] = atoi(nvram_get("pa0b2"));
- sprom[BCM43xx_SPROM_PA1B0] = atoi(nvram_get("pa1b0"));
- sprom[BCM43xx_SPROM_PA1B1] = atoi(nvram_get("pa1b1"));
- sprom[BCM43xx_SPROM_PA1B2] = atoi(nvram_get("pa1b2"));
-
- sprom[BCM43xx_SPROM_BOARDREV] = atoi(nvram_get("boardrev"));
-#else
bcm43xx_sprom_read(bcm, sprom);
+
+#ifdef CONFIG_BCM947XX
+ /* In the case some settings are found in nvram, use them
+ * to override those read from sprom.
+ */
+ c = nvram_get("boardflags2");
+ if (c)
+ sprom[BCM43xx_SPROM_BOARDFLAGS2] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("boardflags");
+ if (c)
+ sprom[BCM43xx_SPROM_BOARDFLAGS] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("il0macaddr");
+ if (c)
+ bcm43xx_aton(c, (char *)&(sprom[BCM43xx_SPROM_IL0MACADDR]));
+ c = nvram_get("et1macaddr");
+ if (c)
+ bcm43xx_aton(c, (char *)&(sprom[BCM43xx_SPROM_ET1MACADDR]));
+ c = nvram_get("pa0b0");
+ if (c)
+ sprom[BCM43xx_SPROM_PA0B0] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("pa0b1");
+ if (c)
+ sprom[BCM43xx_SPROM_PA0B1] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("pa0b2");
+ if (c)
+ sprom[BCM43xx_SPROM_PA0B2] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("pa1b0");
+ if (c)
+ sprom[BCM43xx_SPROM_PA1B0] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("pa1b1");
+ if (c)
+ sprom[BCM43xx_SPROM_PA1B1] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("pa1b2");
+ if (c)
+ sprom[BCM43xx_SPROM_PA1B2] = simple_strtoul(c, NULL, 0);
+ c = nvram_get("boardrev");
+ if (c)
+ sprom[BCM43xx_SPROM_BOARDREV] = simple_strtoul(c, NULL, 0);
#endif
/* boardflags2 */
@@ -914,21 +926,21 @@
u16 value = 0;
u32 buffer[5] = {
0x00000000,
- 0x0000D400,
+ 0x00D40000,
0x00000000,
- 0x00000001,
+ 0x01000000,
0x00000000,
};
switch (phy->type) {
case BCM43xx_PHYTYPE_A:
max_loop = 0x1E;
- buffer[0] = 0xCC010200;
+ buffer[0] = 0x000201CC;
break;
case BCM43xx_PHYTYPE_B:
case BCM43xx_PHYTYPE_G:
max_loop = 0xFA;
- buffer[0] = 0x6E840B00;
+ buffer[0] = 0x000B846E;
break;
default:
assert(0);
@@ -1521,48 +1533,274 @@
bcm43xx_write16(bcm, BCM43xx_MMIO_PS_STATUS, 0x0002);
}
+static void bcm43xx_write_template_common(struct bcm43xx_private *bcm,
+ const u8* data, u16 size,
+ u16 ram_offset,
+ u16 shm_size_offset, u8 rate)
+{
+ u32 i, tmp;
+ struct bcm43xx_plcp_hdr4 plcp;
+
+ plcp.data = 0;
+ bcm43xx_generate_plcp_hdr(&plcp, size + FCS_LEN, rate);
+ bcm43xx_ram_write(bcm, ram_offset, le32_to_cpu(plcp.data));
+ ram_offset += sizeof(u32);
+ /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet.
+ * So leave the first two bytes of the next write blank.
+ */
+ tmp = (u32)(data[0]) << 16;
+ tmp |= (u32)(data[1]) << 24;
+ bcm43xx_ram_write(bcm, ram_offset, tmp);
+ ram_offset += sizeof(u32);
+ for (i = 2; i < size; i += sizeof(u32)) {
+ tmp = (u32)(data[i + 0]);
+ if (i + 1 < size)
+ tmp |= (u32)(data[i + 1]) << 8;
+ if (i + 2 < size)
+ tmp |= (u32)(data[i + 2]) << 16;
+ if (i + 3 < size)
+ tmp |= (u32)(data[i + 3]) << 24;
+ bcm43xx_ram_write(bcm, ram_offset + i - 2, tmp);
+ }
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_size_offset,
+ size + sizeof(struct bcm43xx_plcp_hdr6));
+}
+
static void bcm43xx_write_beacon_template(struct bcm43xx_private *bcm,
u16 ram_offset,
- u16 shm_size_offset)
+ u16 shm_size_offset, u8 rate)
{
- u32 tmp;
- u16 i, size;
+ int len;
const u8 *data;
+ assert(bcm->cached_beacon);
+ len = min((size_t)bcm->cached_beacon->len,
+ 0x200 - sizeof(struct bcm43xx_plcp_hdr6));
data = (const u8 *)(bcm->cached_beacon->data);
- size = min(bcm->cached_beacon->len, (unsigned int)17);
+ bcm43xx_write_template_common(bcm, data,
+ len, ram_offset,
+ shm_size_offset, rate);
+}
- for (i = 0; i < size; i += sizeof(u32)) {
- tmp = (u32)((data + i)[0]);
- tmp |= (u32)((data + i)[1]) << 8;
- tmp |= (u32)((data + i)[2]) << 16;
- tmp |= (u32)((data + i)[3]) << 24;
- bcm43xx_ram_write(bcm, ram_offset + i, tmp);
+static void bcm43xx_write_probe_resp_plcp(struct bcm43xx_private *bcm,
+ u16 shm_offset, u16 size, u8 rate)
+{
+ struct bcm43xx_plcp_hdr4 plcp;
+ u32 tmp;
+ u16 packet_time;
+
+ plcp.data = 0;
+ bcm43xx_generate_plcp_hdr(&plcp, size + FCS_LEN, rate);
+ /*
+ * 144 + 48 + 10 = preamble + PLCP + SIFS,
+ * taken from d80211 timings calculation.
+ *
+ * FIXME: long preamble assumed!
+ *
+ */
+ packet_time = 202 + (size + FCS_LEN) * 16 / rate;
+ if ((size + FCS_LEN) * 16 % rate >= rate / 2)
+ ++packet_time;
+
+ /* Write PLCP in two parts and timing for packet transfer */
+ tmp = le32_to_cpu(plcp.data);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_offset,
+ tmp & 0xFFFF);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_offset + 2,
+ tmp >> 16);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_offset + 6,
+ packet_time);
+}
+
+/* Instead of using custom probe response template, this function
+ * just patches custom beacon template by:
+ * 1) Changing packet type
+ * 2) Patching duration field
+ * 3) Stripping TIM
+ */
+static u8 * bcm43xx_generate_probe_resp(struct bcm43xx_private *bcm,
+ u16* dest_size, u8 rate)
+{
+ const u8 *src_data;
+ u8 *dest_data;
+ u16 src_size, elem_size, src_pos, dest_pos, tmp;
+
+ assert(bcm->cached_beacon);
+ src_size = bcm->cached_beacon->len;
+ src_data = (const u8*)bcm->cached_beacon->data;
+
+ if (unlikely(src_size < 0x24)) {
+ dprintk(KERN_ERR PFX "bcm43xx_generate_probe_resp: "
+ "invalid beacon\n");
+ return NULL;
+ }
+
+ dest_data = kmalloc(src_size, GFP_ATOMIC);
+ if (unlikely(!dest_data))
+ return NULL;
+
+ /* 0x24 is offset of first variable-len Information-Element
+ * in beacon frame.
+ */
+ memcpy(dest_data, src_data, 0x24);
+ src_pos = dest_pos = 0x24;
+ for ( ; src_pos < src_size - 2; src_pos += elem_size) {
+ elem_size = src_data[src_pos + 1] + 2;
+ if (src_data[src_pos] != 0x05) { /* TIM */
+ memcpy(dest_data + dest_pos, src_data + src_pos,
+ elem_size);
+ dest_pos += elem_size;
+ }
+ }
+ *dest_size = dest_pos;
+
+ /* Set the frame control. */
+ dest_data[0] = ((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_RESP << 4));
+ dest_data[1] = 0;
+
+ /* Set the duration field.
+ *
+ * 144 + 48 + 10 = preamble + PLCP + SIFS,
+ * taken from d80211 timings calculation.
+ *
+ * FIXME: long preamble assumed!
+ *
+ */
+ tmp = 202 + (14 + FCS_LEN) * 16 / rate;
+ if ((14 + FCS_LEN) * 16 % rate >= rate / 2)
+ ++tmp;
+
+ dest_data[2] = tmp & 0xFF;
+ dest_data[3] = (tmp >> 8) & 0xFF;
+
+ return dest_data;
+}
+
+static void bcm43xx_write_probe_resp_template(struct bcm43xx_private *bcm,
+ u16 ram_offset,
+ u16 shm_size_offset, u8 rate)
+{
+ u8* probe_resp_data;
+ u16 size;
+
+ assert(bcm->cached_beacon);
+ size = bcm->cached_beacon->len;
+ probe_resp_data = bcm43xx_generate_probe_resp(bcm, &size, rate);
+ if (unlikely(!probe_resp_data))
+ return;
+
+ /* Looks like PLCP headers plus packet timings are stored for
+ * all possible basic rates
+ */
+ bcm43xx_write_probe_resp_plcp(bcm, 0x31A, size,
+ BCM43xx_CCK_RATE_1MB);
+ bcm43xx_write_probe_resp_plcp(bcm, 0x32C, size,
+ BCM43xx_CCK_RATE_2MB);
+ bcm43xx_write_probe_resp_plcp(bcm, 0x33E, size,
+ BCM43xx_CCK_RATE_5MB);
+ bcm43xx_write_probe_resp_plcp(bcm, 0x350, size,
+ BCM43xx_CCK_RATE_11MB);
+
+ size = min((size_t)size,
+ 0x200 - sizeof(struct bcm43xx_plcp_hdr6));
+ bcm43xx_write_template_common(bcm, probe_resp_data,
+ size, ram_offset,
+ shm_size_offset, rate);
+ kfree(probe_resp_data);
+}
+
+static int bcm43xx_refresh_cached_beacon(struct bcm43xx_private *bcm)
+{
+ struct ieee80211_tx_control control;
+
+ if (bcm->cached_beacon)
+ kfree_skb(bcm->cached_beacon);
+ bcm->cached_beacon = ieee80211_beacon_get(bcm->net_dev,
+ bcm->interface.if_id,
+ &control);
+ if (unlikely(!bcm->cached_beacon))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void bcm43xx_update_templates(struct bcm43xx_private *bcm)
+{
+ u32 status;
+
+ assert(bcm->cached_beacon);
+
+ bcm43xx_write_beacon_template(bcm, 0x68, 0x18,
+ BCM43xx_CCK_RATE_1MB);
+ bcm43xx_write_beacon_template(bcm, 0x468, 0x1A,
+ BCM43xx_CCK_RATE_1MB);
+ bcm43xx_write_probe_resp_template(bcm, 0x268, 0x4A,
+ BCM43xx_CCK_RATE_11MB);
+
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD);
+ status |= 0x03;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status);
+}
+
+static void bcm43xx_refresh_templates(struct bcm43xx_private *bcm)
+{
+ int err;
+
+ err = bcm43xx_refresh_cached_beacon(bcm);
+ if (unlikely(err))
+ return;
+ bcm43xx_update_templates(bcm);
+ kfree_skb(bcm->cached_beacon);
+ bcm->cached_beacon = NULL;
+}
+
+static void bcm43xx_set_ssid(struct bcm43xx_private *bcm,
+ const u8 *ssid, u8 ssid_len)
+{
+ u32 tmp;
+ u16 i, len;
+
+ len = min((u16)ssid_len, (u16)0x100);
+ for (i = 0; i < len; i += sizeof(u32)) {
+ tmp = (u32)(ssid[i + 0]);
+ if (i + 1 < len)
+ tmp |= (u32)(ssid[i + 1]) << 8;
+ if (i + 2 < len)
+ tmp |= (u32)(ssid[i + 2]) << 16;
+ if (i + 3 < len)
+ tmp |= (u32)(ssid[i + 3]) << 24;
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ 0x380 + i, tmp);
+ }
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,
+ 0x48, len);
+}
+
+static void bcm43xx_set_beacon_int(struct bcm43xx_private *bcm, u16 beacon_int)
+{
+ bcm43xx_time_lock(bcm);
+ if (bcm->current_core->rev >= 3) {
+ bcm43xx_write32(bcm, 0x188, (beacon_int << 16));
+ } else {
+ bcm43xx_write16(bcm, 0x606, (beacon_int >> 6));
+ bcm43xx_write16(bcm, 0x610, beacon_int);
}
- bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_size_offset, size);
+ bcm43xx_time_unlock(bcm);
}
static void handle_irq_beacon(struct bcm43xx_private *bcm)
{
u32 status;
+ int err;
bcm->irq_savedstate &= ~BCM43xx_IRQ_BEACON;
status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD);
if (!bcm->cached_beacon) {
- struct ieee80211_tx_control control;
-
- /* No cached template available, yet.
- * Request the 80211 subsystem to generate a new beacon
- * frame and use it as template.
- */
- bcm->cached_beacon = ieee80211_beacon_get(bcm->net_dev,
- bcm->interface.if_id,
- &control);
- if (unlikely(!bcm->cached_beacon)) {
- dprintkl(KERN_WARNING PFX "Could not generate beacon template.\n");
+ err = bcm43xx_refresh_cached_beacon(bcm);
+ if (unlikely(err))
goto ack;
- }
}
if ((status & 0x1) && (status & 0x2)) {
@@ -1571,18 +1809,20 @@
bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON,
BCM43xx_IRQ_BEACON);
bcm->irq_savedstate |= BCM43xx_IRQ_BEACON;
- if (likely(bcm->cached_beacon))
+ if (bcm->cached_beacon)
kfree_skb(bcm->cached_beacon);
bcm->cached_beacon = NULL;
return;
}
if (!(status & 0x1)) {
- bcm43xx_write_beacon_template(bcm, 0x68, 0x18);
+ bcm43xx_write_beacon_template(bcm, 0x68, 0x18,
+ BCM43xx_CCK_RATE_1MB);
status |= 0x1;
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status);
}
if (!(status & 0x2)) {
- bcm43xx_write_beacon_template(bcm, 0x468, 0x1A);
+ bcm43xx_write_beacon_template(bcm, 0x468, 0x1A,
+ BCM43xx_CCK_RATE_1MB);
status |= 0x2;
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status);
}
@@ -4114,6 +4354,17 @@
radio->power_level = conf->power_level;
bcm43xx_phy_xmitpower(bcm);
}
+
+ if (conf->ssid_hidden) {
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
+ | BCM43xx_SBF_NO_SSID_BCAST);
+ } else {
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
+ & ~BCM43xx_SBF_NO_SSID_BCAST);
+ }
+
//FIXME: This does not seem to wake up:
#if 0
if (conf->power_level == 0) {
@@ -4128,6 +4379,11 @@
//TODO: phymode
//TODO: antennas
+ if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP)) {
+ bcm43xx_set_beacon_int(bcm, conf->beacon_int);
+ bcm43xx_refresh_templates(bcm);
+ }
+
bcm43xx_unlock_irqonly(bcm, flags);
return 0;
@@ -4293,8 +4549,11 @@
bcm->interface.mac_addr = conf->mac_addr;
bcm->interface.type = conf->type;
}
- if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
+ if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
bcm43xx_select_opmode(bcm);
+ if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP))
+ bcm43xx_refresh_templates(bcm);
+ }
err = 0;
dprintk(KERN_INFO PFX "Virtual interface added "
@@ -4343,6 +4602,11 @@
if (conf->type != IEEE80211_IF_TYPE_MNTR) {
assert(bcm->interface.if_id == if_id);
bcm->interface.bssid = conf->bssid;
+ if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP)) {
+ assert(conf->type == IEEE80211_IF_TYPE_AP);
+ bcm43xx_set_ssid(bcm, conf->ssid, conf->ssid_len);
+ bcm43xx_refresh_templates(bcm);
+ }
}
bcm43xx_unlock_irqsafe(bcm, flags);
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_xmit.c
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_xmit.c 2006-06-13 21:06:01.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_xmit.c 2006-06-18 18:43:12.000000000 +0200
@@ -112,14 +112,13 @@
return 0;
}
-static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp,
- const u16 octets, const u8 bitrate,
- const int ofdm_modulation)
+void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp,
+ const u16 octets, const u8 bitrate)
{
__le32 *data = &(plcp->data);
__u8 *raw = plcp->raw;
- if (ofdm_modulation) {
+ if (bcm43xx_is_ofdm_rate(bitrate)) {
*data = bcm43xx_plcp_get_ratecode_ofdm(bitrate);
assert(!(octets & 0xF000));
*data |= (octets << 5);
@@ -224,11 +223,9 @@
flen = sizeof(u16) + sizeof(u16) + ETH_ALEN + ETH_ALEN + FCS_LEN,
bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_plcp),
- flen, bitrate,
- !bcm43xx_is_cck_rate(bitrate));
+ flen, bitrate);
bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_fallback_plcp),
- flen, fallback_bitrate,
- !bcm43xx_is_cck_rate(fallback_bitrate));
+ flen, fallback_bitrate);
fctl = WLAN_FC_TYPE_CTRL << 2;
fctl |= WLAN_FC_STYPE_RTS << 4;
dur = le16_to_cpu(wlhdr->duration_id);
@@ -332,10 +329,9 @@
}
/* Generate the PLCP header and the fallback PLCP header. */
bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp),
- plcp_fragment_len,
- bitrate, ofdm_modulation);
+ plcp_fragment_len, bitrate);
bcm43xx_generate_plcp_hdr(&txhdr->fallback_plcp, plcp_fragment_len,
- fallback_bitrate, fallback_ofdm_modulation);
+ fallback_bitrate);
/* Set the CONTROL field */
if (ofdm_modulation)
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_xmit.h
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_xmit.h 2006-06-13 14:43:08.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_xmit.h 2006-06-18 18:41:27.000000000 +0200
@@ -154,4 +154,7 @@
struct sk_buff *skb,
struct bcm43xx_rxhdr *rxhdr);
+void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp,
+ const u16 octets, const u8 bitrate);
+
#endif /* BCM43xx_XMIT_H_ */
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.h
===================================================================
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.h 2006-06-13 14:43:08.000000000 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.h 2006-06-18 18:41:03.000000000 +0200
@@ -33,24 +33,6 @@
#include "bcm43xx.h"
-#ifdef CONFIG_BCM947XX
-#define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0)
-
-static inline void e_aton(char *str, char *dest)
-{
- int i = 0;
- u16 *d = (u16 *) dest;
-
- for (;;) {
- dest[i++] = (char) simple_strtoul(str, NULL, 16);
- str += 2;
- if (!*str++ || i == 6)
- break;
- }
- for (i = 0; i < 3; i++)
- d[i] = cpu_to_be16(d[i]);
-}
-#endif
#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes]
#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes)
@@ -143,6 +125,12 @@
rate == BCM43xx_CCK_RATE_11MB);
}
+static inline
+int bcm43xx_is_ofdm_rate(int rate)
+{
+ return !bcm43xx_is_cck_rate(rate);
+}
+
void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf);
void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf);
--
Greetings Michael.
^ permalink raw reply
* Re: [Bugme-new] [Bug 6682] New: BUG: soft lockup detected on CPU#0! / ksoftirqd takse 100% CPU
From: Andrew Morton @ 2006-06-19 22:20 UTC (permalink / raw)
To: netdev
Cc: Dipankar Sarma, Paul E. McKenney,
bugme-daemon@kernel-bugs.osdl.org, alchemyx
In-Reply-To: <200606122311.k5CNBMEx007518@fire-2.osdl.org>
bugme-daemon@bugzilla.kernel.org wrote:
>
> http://bugzilla.kernel.org/show_bug.cgi?id=6682
>
> Summary: BUG: soft lockup detected on CPU#0! / ksoftirqd takse
> 100% CPU
> Kernel Version: 2.6.15.6
> Status: NEW
> Severity: normal
> Owner: acme@conectiva.com.br
> Submitter: alchemyx@uznam.net.pl
>
>
> Most recent kernel where this bug did not occur: (unknown)
> Distribution: Gentoo
> Hardware Environment: 2x Xeon 2.66, 1 GB RAM, NICS: 2 x e1000, and one double
> port e100. Based on Intel E7501 architecture (2U rack Intel chassis).
> Software Environment: quagga 0.98.6
> Problem Description: ksoftirqd/0 takes 100% of CPU. further investigation shows
> no sing of network flood or something (and also 2 of 3 NICs are e1000 with
> NAPI). Ocassionaly there are "BUG: soft lockup detected on CPU#0!".
>
>
> Steps to reproduce:
>
> There is no simple way to reproduce. I think that everythint started when we
> attached second provider with BGP support. We are using quagga which injects
> about 186 000 routes into kernel. When running for a while (at least few hours,
> sometimes a day) we get 100% usage on ksoftirqd/0 and following messages in logs:
>
> BUG: soft lockup detected on CPU#0!
>
> Pid: 6506, comm: zebra
> EIP: 0060:[<c027f6fd>] CPU: 0
> EIP is at _spin_lock+0x7/0xf
> EFLAGS: 00000286 Not tainted (2.6.15.6)
> EAX: f6203180 EBX: e6fbf000 ECX: 00000000 EDX: f6bec000
> ESI: f6203000 EDI: eddb4b80 EBP: fffffff4 DS: 007b ES: 007b
> CR0: 8005003b CR2: aca6dff0 CR3: 361ad000 CR4: 000006d0
> [<c02396f9>] dev_queue_xmit+0xe0/0x203
> [<c0250de8>] ip_output+0x1e1/0x237
> [<c024f3f5>] ip_forward+0x181/0x1df
> [<c024e21a>] ip_rcv+0x40c/0x485
> [<c0239bd0>] netif_receive_skb+0x12f/0x165
> [<f885aa4c>] e1000_clean_rx_irq+0x389/0x410 [e1000]
> [<f885a1ca>] e1000_clean+0x94/0x12f [e1000]
> [<c0239d5a>] net_rx_action+0x69/0xf0
> [<c011a305>] __do_softirq+0x55/0xbd
> [<c011a39a>] do_softirq+0x2d/0x31
> [<c011a3f8>] local_bh_enable+0x5a/0x65
> [<c024a0a1>] rt_run_flush+0x5f/0x80
> [<c027623f>] fn_hash_insert+0x352/0x39f
> [<c027364c>] inet_rtm_newroute+0x57/0x62
> [<c02413ed>] rtnetlink_rcv_msg+0x1a8/0x1cb
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c0247c1e>] netlink_rcv_skb+0x3a/0x8b
> [<c0247cb1>] netlink_run_queue+0x42/0xc3
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c0241227>] rtnetlink_rcv+0x22/0x40
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c024764c>] netlink_data_ready+0x17/0x54
> [<c0246a99>] netlink_sendskb+0x1f/0x39
> [<c0247449>] netlink_sendmsg+0x27b/0x28c
> [<c0231467>] sock_sendmsg+0xce/0xe9
> [<c0112b36>] __wake_up+0x27/0x3b
> [<c01a6216>] copy_to_user+0x38/0x42
> [<c01a625a>] copy_from_user+0x3a/0x60
> [<c01a625a>] copy_from_user+0x3a/0x60
> [<c0126be2>] autoremove_wake_function+0x0/0x3a
> [<c0236bcd>] verify_iovec+0x49/0x7f
> [<c02327f2>] sys_sendmsg+0x152/0x1a8
> [<c0147a62>] do_sync_read+0xb8/0xeb
> [<c01a6216>] copy_to_user+0x38/0x42
> [<c0126be2>] autoremove_wake_function+0x0/0x3a
> [<c0122b7a>] getrusage+0x34/0x43
> [<c0168504>] inotify_dentry_parent_queue_event+0x29/0x7c
> [<c01a625a>] copy_from_user+0x3a/0x60
> [<c0232b6b>] sys_socketcall+0x167/0x180
> [<c0102433>] sysenter_past_esp+0x54/0x75
>
> BUG: soft lockup detected on CPU#0!
>
> Pid: 6506, comm: zebra
> EIP: 0060:[<f8952052>] CPU: 0
> EIP is at u32_classify+0x52/0x170 [cls_u32]
> EFLAGS: 00000206 Not tainted (2.6.15.6)
> EAX: e2fbd020 EBX: f48649c0 ECX: 00000010 EDX: 29b09d5a
> ESI: f48649ec EDI: 00000001 EBP: e2fbd020 DS: 007b ES: 007b
> CR0: 8005003b CR2: 08154004 CR3: 361ad000 CR4: 000006d0
> [<f88462fa>] ipt_do_table+0x2de/0x2fd [ip_tables]
> [<f883b523>] ip_nat_fn+0x177/0x185 [iptable_nat]
> [<f88e159f>] ip_refrag+0x23/0x5f [ip_conntrack]
> [<c0244d82>] tc_classify+0x2c/0x3f
> [<f895514b>] htb_classify+0x14b/0x1dd [sch_htb]
> [<f8955638>] htb_enqueue+0x1d/0x13a [sch_htb]
> [<c02396fd>] dev_queue_xmit+0xe4/0x203
> [<c0250de8>] ip_output+0x1e1/0x237
> [<c024f3f5>] ip_forward+0x181/0x1df
> [<c024e21a>] ip_rcv+0x40c/0x485
> [<c0239bd0>] netif_receive_skb+0x12f/0x165
> [<f885aa4c>] e1000_clean_rx_irq+0x389/0x410 [e1000]
> [<f885a1ca>] e1000_clean+0x94/0x12f [e1000]
> [<c0239d5a>] net_rx_action+0x69/0xf0
> [<c011a305>] __do_softirq+0x55/0xbd
> [<c011a39a>] do_softirq+0x2d/0x31
> [<c011a3f8>] local_bh_enable+0x5a/0x65
> [<c024a0a1>] rt_run_flush+0x5f/0x80
> [<c027623f>] fn_hash_insert+0x352/0x39f
> [<c027364c>] inet_rtm_newroute+0x57/0x62
> [<c02413ed>] rtnetlink_rcv_msg+0x1a8/0x1cb
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c0247c1e>] netlink_rcv_skb+0x3a/0x8b
> [<c0247cb1>] netlink_run_queue+0x42/0xc3
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c0241227>] rtnetlink_rcv+0x22/0x40
> [<c0241245>] rtnetlink_rcv_msg+0x0/0x1cb
> [<c024764c>] netlink_data_ready+0x17/0x54
> [<c0246a99>] netlink_sendskb+0x1f/0x39
> [<c0247449>] netlink_sendmsg+0x27b/0x28c
> [<c0231467>] sock_sendmsg+0xce/0xe9
> [<c0112b36>] __wake_up+0x27/0x3b
> [<c01a625a>] copy_from_user+0x3a/0x60
> [<c01a625a>] copy_from_user+0x3a/0x60
> [<c0126be2>] autoremove_wake_function+0x0/0x3a
> [<c0236bcd>] verify_iovec+0x49/0x7f
> [<c02327f2>] sys_sendmsg+0x152/0x1a8
> [<c0147a62>] do_sync_read+0xb8/0xeb
> [<c0122b7a>] getrusage+0x34/0x43
> [<c011d72f>] update_wall_time+0xa/0x32
> [<c011d9e5>] do_timer+0x33/0xa9
> [<c01a625a>] copy_from_user+0x3a/0x60
> [<c0232b6b>] sys_socketcall+0x167/0x180
> [<c0102433>] sysenter_past_esp+0x54/0x75
>
> It happens on 2.6.15.6. Tonight I will try 2.6.16.16 with FIB_TRIE instead of
> FIB_HASH. I am submitting that bug under network but I am not completly sure if
> it belongs here.
This also happens on 2.6.16.6.
^ 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