* Re: [2/3] 2.6.23-rc2: known regressions
From: Michal Piotrowski @ 2007-08-05 16:26 UTC (permalink / raw)
To: Linus Torvalds
Cc: Andrew Morton, LKML, linux-fsdevel, nfs, Andrew Clayton, Netdev,
Stephen Hemminger, Thomas Meyer, Florian Lohoff, Uwe Bugla,
linux-pm, Rafael J. Wysocki, Pavel Machek, Meelis Roos
In-Reply-To: <46B5F74D.7010708@googlemail.com>
Hi all,
Here is a list of some known regressions in 2.6.23-rc2.
Feel free to add new regressions/remove fixed etc.
http://kernelnewbies.org/known_regressions
List of Aces
Name Regressions fixed since 21-Jun-2007
Adrian Bunk 6
Andi Kleen 4
Andrew Morton 4
Linus Torvalds 4
Al Viro 3
Cornelia Huck 3
Jens Axboe 3
Tejun Heo 3
David Woodhouse 2
Hugh Dickins 2
Trent Piepho 2
FS
Subject : [NFSD OOPS] 2.6.23-rc1-git10
References : http://lkml.org/lkml/2007/8/2/462
Last known good : ?
Submitter : Andrew Clayton <andrew@digital-domain.net>
Caused-By : ?
Handled-By : ?
Status : unknown
Networking
Subject : IP v4 routing is broken
References : http://www.stardust.webpages.pl/files/tbf/bugs/bug_report01.txt
Last known good : 2.6.22-git2
Submitter : Uwe Bugla <uwe.bugla@gmx.de>
Caused-By : ?
Handled-By : ?
Status : unknown
Subject : sky2 boot crash in sky2_mac_intr
References : http://lkml.org/lkml/2007/7/24/91
Last known good : ?
Submitter : Florian Lohoff <flo@rfc822.org>
Caused-By :
Handled-By : Stephen Hemminger <shemminger@osdl.org>
Status : unknown
Subject : New wake ups from sky2
References : http://lkml.org/lkml/2007/7/20/386
Last known good : ?
Submitter : Thomas Meyer <thomas@m3y3r.de>
Caused-By : Stephen Hemminger <shemminger@osdl.org>
commit eb35cf60e462491249166182e3e755d3d5d91a28
Handled-By : Stephen Hemminger <shemminger@osdl.org>
Status : unknown
Power management
Subject : Kconfig: 'SUSPEND_SMP' refers to undefined symbol 'HOTPLUG_CPU'
References : http://lkml.org/lkml/2007/8/4/39
Last known good : ?
Submitter : Meelis Roos <mroos@linux.ee>
Caused-By : ?
Handled-By : Rafael J. Wysocki <rjw@sisk.pl>
Status : unknown
Regards,
Michal
--
LOG
http://www.stardust.webpages.pl/log/
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-05 15:08 UTC (permalink / raw)
To: Daniel Phillips; +Cc: netdev, linux-kernel, linux-fsdevel, Peter Zijlstra
In-Reply-To: <200708050104.19596.phillips@phunq.net>
Hi Daniel.
On Sun, Aug 05, 2007 at 01:04:19AM -0700, Daniel Phillips (phillips@phunq.net) wrote:
> > we can wait in it for memory in mempool. Although that means we
> > already in trouble.
>
> Not at all. This whole block writeout path needs to be written to run
> efficiently even when normal system memory is completely gone. All it
> means when we wait on a mempool is that the block device queue is as
> full as we are ever going to let it become, and that means the block
> device is working as hard as it can (subject to a small caveat: for
> some loads a device can work more efficiently if it can queue up larger
> numbers of requests down at the physical elevators).
If we are sleeping in memory pool, then we already do not have memory to
complete previous requests, so we are in trouble. This can work for
devices which do not require additional allocations (like usual local
storage), but not for network connected ones.
> > I agree, any kind of high-boundary leveling must be implemented in
> > device itself, since block layer does not know what device is at the
> > end and what it will need to process given block request.
>
> I did not say the throttling has to be implemented in the device, only
> that we did it there because it was easiest to code that up and try it
> out (it worked). This throttling really wants to live at a higher
> level, possibly submit_bio()...bio->endio(). Someone at OLS (James
> Bottomley?) suggested it would be better done at the request queue
> layer, but I do not immediately see why that should be. I guess this
> is going to come down to somebody throwing out a patch for interested
> folks to poke at. But this detail is a fine point. The big point is
> to have _some_ throttling mechanism in place on the block IO path,
> always.
If not in device, then at least it should say to block layer about its
limits. What about new function to register queue which will get maximum
number of bios in flight and sleep in generic_make_request() when new
bio is going to be submitted and it is about to exceed the limit?
By default things will be like they are now, except additional
non-atomic increment and branch in generic_make_request() and decrement
and wake in bio_end_io()?
I can cook up such a patch if idea worth efforts.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-05 15:01 UTC (permalink / raw)
To: Daniel Phillips; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <200708050106.58383.phillips@phunq.net>
On Sun, Aug 05, 2007 at 01:06:58AM -0700, Daniel Phillips (phillips@phunq.net) wrote:
> > DST original code worked as device mapper plugin too, but its two
> > additional allocations (io and clone) per block request ended up for
> > me as a show stopper.
>
> Ah, sorry, I misread. A show stopper in terms of efficiency, or in
> terms of deadlock?
At least as in terms of efficiency. Device mapper lives in happy world
where memory does not end and allocations are fast.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: b44 compile error on !PCI
From: Meelis Roos @ 2007-08-05 10:03 UTC (permalink / raw)
To: Michael Buesch; +Cc: netdev
In-Reply-To: <200708021917.45876.mb@bu3sch.de>
> > CC [M] drivers/net/b44.o
> > drivers/net/b44.c: In function 'b44_sync_dma_desc_for_device':
> > drivers/net/b44.c:134: error: implicit declaration of function 'dma_sync_single_range_for_device'
> > drivers/net/b44.c: In function 'b44_sync_dma_desc_for_cpu':
> > drivers/net/b44.c:144: error: implicit declaration of function 'dma_sync_single_range_for_cpu'
>
> Are you sure this is related to !PCI ?
> If I grep include/asm-sparc64/dma-mapping.h I don't
> find dma_sync_single_range_for_*
> So I'd rather call this a bug in the arch code of sparc64.
Yes, now that Dave Miller added these functions to sparc64, b44 compiles
fine.
--
Meelis Roos (mroos@linux.ee)
^ permalink raw reply
* e1000 doesn't resume properly from standby (2.6.23-rc2)
From: Simon Arlott @ 2007-08-05 8:52 UTC (permalink / raw)
To: linux-pm; +Cc: Linux Kernel Mailing List, netdev
00:0a.0 Ethernet controller: Intel Corp. 82546EB Gigabit Ethernet Controller (Copper) (rev 01)
Subsystem: Intel Corp.: Unknown device 1012
Flags: bus master, 66Mhz, medium devsel, latency 32, IRQ 5
Memory at e3020000 (64-bit, non-prefetchable) [size=128K]
I/O ports at b000 [size=64]
Capabilities: [dc] Power Management version 2
Capabilities: [e4] PCI-X non-bridge device.
Capabilities: [f0] Message Signalled Interrupts: 64bit+ Queue=0/0 Enable-
00:0a.1 Ethernet controller: Intel Corp. 82546EB Gigabit Ethernet Controller (Copper) (rev 01)
Subsystem: Intel Corp.: Unknown device 1012
Flags: bus master, 66Mhz, medium devsel, latency 32, IRQ 12
Memory at e3000000 (64-bit, non-prefetchable) [size=128K]
I/O ports at b400 [size=64]
Capabilities: [dc] Power Management version 2
Capabilities: [e4] PCI-X non-bridge device.
Capabilities: [f0] Message Signalled Interrupts: 64bit+ Queue=0/0 Enable-
[ 950.132046] Stopping tasks ... done.
[ 950.459794] Suspending console(s)
[ 951.776277] pnp: Device 00:0c disabled.
[ 951.776673] pnp: Device 00:0a disabled.
[ 951.776984] pnp: Device 00:09 disabled.
[ 951.777306] pnp: Device 00:08 disabled.
[ 951.777786] ACPI: PCI interrupt for device 0000:00:11.5 disabled
[ 951.995359] ACPI: PCI interrupt for device 0000:00:11.3 disabled
[ 952.006094] ACPI: PCI interrupt for device 0000:00:11.2 disabled
[ 952.022243] ACPI handle has no context!
[ 952.033068] ACPI: PCI interrupt for device 0000:00:0c.2 disabled
[ 952.044086] ACPI: PCI interrupt for device 0000:00:0c.1 disabled
[ 952.055083] ACPI: PCI interrupt for device 0000:00:0c.0 disabled
[ 952.282211] ACPI: PCI interrupt for device 0000:00:0a.1 disabled
[ 952.282221] ACPI handle has no context!
[ 952.537474] ACPI: PCI interrupt for device 0000:00:0a.0 disabled
[ 952.537495] ACPI handle has no context!
[ 956.857085] Back to C!
[ 957.295035] ACPI: Unable to turn cooling device [b18d0e00] 'off'
[ 957.521400] PCI: Setting latency timer of device 0000:00:01.0 to 64
[ 957.521478] ACPI: PCI Interrupt 0000:00:09.0[A] -> Link [LNKB] -> GSI 11 (level, low) -> IRQ 11
[ 957.532256] PM: Writing back config space on device 0000:00:0a.0 at offset f (was ff0100, writing ff0105)
[ 957.532277] PM: Writing back config space on device 0000:00:0a.0 at offset 8 (was 1, writing b001)
[ 957.532291] PM: Writing back config space on device 0000:00:0a.0 at offset 4 (was 4, writing e3020004)
[ 957.532299] PM: Writing back config space on device 0000:00:0a.0 at offset 3 (was 800000, writing 802008)
[ 957.532309] PM: Writing back config space on device 0000:00:0a.0 at offset 1 (was 2300000, writing 2300007)
[ 957.532339] ACPI: PCI Interrupt 0000:00:0a.0[A] -> Link [LNKC] -> GSI 5 (level, low) -> IRQ 5
[ 957.567251] PM: Writing back config space on device 0000:00:0a.1 at offset f (was ff0200, writing ff020c)
[ 957.567275] PM: Writing back config space on device 0000:00:0a.1 at offset 8 (was 1, writing b401)
[ 957.567290] PM: Writing back config space on device 0000:00:0a.1 at offset 4 (was 4, writing e3000004)
[ 957.567298] PM: Writing back config space on device 0000:00:0a.1 at offset 3 (was 800000, writing 802008)
[ 957.567308] PM: Writing back config space on device 0000:00:0a.1 at offset 1 (was 2300000, writing 2300007)
[ 957.567346] ACPI: PCI Interrupt 0000:00:0a.1[B] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.589975] ACPI: PCI Interrupt 0000:00:0b.0[A] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.600217] ACPI: PCI Interrupt 0000:00:0c.0[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 957.611230] ACPI: PCI Interrupt 0000:00:0c.1[B] -> Link [LNKB] -> GSI 11 (level, low) -> IRQ 11
[ 957.838282] ACPI: PCI Interrupt 0000:00:0c.2[C] -> Link [LNKC] -> GSI 5 (level, low) -> IRQ 5
[ 957.902166] ohci1394: fw-host0: OHCI-1394 1.0 (PCI): IRQ=[11] MMIO=[e3046000-e30467ff] Max Packet=[512] IR/IT contexts=[4/8]
[ 957.911656] ACPI: PCI Interrupt 0000:00:11.1[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 957.911666] PCI: VIA VLink IRQ fixup for 0000:00:11.1, from 255 to 11
[ 957.922034] ACPI: PCI Interrupt 0000:00:11.2[D] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.933028] ACPI: PCI Interrupt 0000:00:11.3[D] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.944076] ACPI: PCI Interrupt 0000:00:11.5[C] -> Link [LNKC] -> GSI 5 (level, low) -> IRQ 5
[ 957.944091] PCI: Setting latency timer of device 0000:00:11.5 to 64
[ 957.946061] ACPI: PCI Interrupt 0000:01:00.0[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 957.947464] pnp: Device 00:08 activated.
[ 957.948724] pnp: Device 00:09 activated.
[ 957.950635] pnp: Device 00:0a activated.
[ 957.950664] pnp: Failed to activate device 00:0b.
[ 957.951942] pnp: Device 00:0c activated.
[ 959.883939] e1000: eth0: e1000_watchdog: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX/TX
[ 961.866287] hde: selected mode 0x45
[ 965.310493] hdg: selected mode 0x45
[ 965.316152] hdh: selected mode 0x42
[ 969.135523] hda: selected mode 0x46
[ 973.454631] hdb: selected mode 0x45
[ 973.457179] hdc: selected mode 0x44
[ 973.459550] hdd: selected mode 0x42
[ 973.950474] Restarting tasks ... done.
[ 974.261357] agpgart: Found an AGP 2.0 compliant device at 0000:00:00.0.
[ 974.262099] agpgart: Putting AGP V2 device at 0000:00:00.0 into 4x mode
[ 974.262620] agpgart: Putting AGP V2 device at 0000:01:00.0 into 4x mode
[ 974.262894] [drm] Loading R200 Microcode
eth0 now doesn't receive anything - it's transmitting ok because
I can receive its packets on another host. It's also still getting
interrupts.
If I then ifconfig eth0 down and up, or change the MTU (since
that resets the link on e1000), it starts working again:
[ 993.926603] e1000: eth0: e1000_watchdog: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX/TX
There's a link up at 959.883939 too...
--
Simon Arlott
^ permalink raw reply
* Re: Distributed storage.
From: Daniel Phillips @ 2007-08-05 8:06 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <20070804164438.GC14175@2ka.mipt.ru>
On Saturday 04 August 2007 09:44, Evgeniy Polyakov wrote:
> > On Tuesday 31 July 2007 10:13, Evgeniy Polyakov wrote:
> > > * storage can be formed on top of remote nodes and be
> > > exported simultaneously (iSCSI is peer-to-peer only, NBD requires
> > > device mapper and is synchronous)
> >
> > In fact, NBD has nothing to do with device mapper. I use it as a
> > physical target underneath ddraid (a device mapper plugin) just
> > like I would use your DST if it proves out.
>
> I meant to create a storage on top of several nodes one needs to have
> device mapper or something like that on top of NBD itself. To further
> export resulted device one needs another userspace NDB application
> and so on. DST simplifies that greatly.
>
> DST original code worked as device mapper plugin too, but its two
> additional allocations (io and clone) per block request ended up for
> me as a show stopper.
Ah, sorry, I misread. A show stopper in terms of efficiency, or in
terms of deadlock?
Regards,
Daniel
^ permalink raw reply
* Re: Distributed storage.
From: Daniel Phillips @ 2007-08-05 8:04 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: netdev, linux-kernel, linux-fsdevel, Peter Zijlstra
In-Reply-To: <20070804163740.GB14175@2ka.mipt.ru>
On Saturday 04 August 2007 09:37, Evgeniy Polyakov wrote:
> On Fri, Aug 03, 2007 at 06:19:16PM -0700, I wrote:
> > To be sure, I am not very proud of this throttling mechanism for
> > various reasons, but the thing is, _any_ throttling mechanism no
> > matter how sucky solves the deadlock problem. Over time I want to
> > move the
>
> make_request_fn is always called in process context,
Yes, as is submit_bio which calls it. The decision re where it is best
to throttle, in submit_bio or in make_request_fn, has more to do with
system factoring, that is, is throttling something that _every_ block
device should have (yes I think) or is it a delicate, optional thing
that needs a tweakable algorithm per block device type (no I think).
The big worry I had was that by blocking on congestion in the
submit_bio/make_request_fn I might stuff up system-wide mm writeout.
But a while ago that part of the mm was tweaked (by Andrew if I recall
correctly) to use a pool of writeout threads and understand the concept
of one of them blocking on some block device, and not submit more
writeout to the same block device until the first thread finishes its
submission. Meanwhile, other mm writeout threads carry on with other
block devices.
> we can wait in it for memory in mempool. Although that means we
> already in trouble.
Not at all. This whole block writeout path needs to be written to run
efficiently even when normal system memory is completely gone. All it
means when we wait on a mempool is that the block device queue is as
full as we are ever going to let it become, and that means the block
device is working as hard as it can (subject to a small caveat: for
some loads a device can work more efficiently if it can queue up larger
numbers of requests down at the physical elevators).
By the way, ddsnap waits on a counting semaphore, not a mempool. That
is because we draw our reserve memory from the global memalloc reserve,
not from a mempool. And that is not only because it takes less code to
do so, but mainly because global pools as opposed to lots of little
special purpose pools seem like a good idea to me. Though I will admit
that with our current scheme we need to allow for the total of the
maximum reserve requirements for all memalloc users in the memalloc
pool, so it does not actually save any memory vs dedicated pools. We
could improve that if we wanted to, by having hard and soft reserve
requirements: the global reserve actually only needs to be as big as
the total of the hard requirements. With this idea, if by some unlucky
accident every single pool user got itself maxed out at the same time,
we would still not exceed our share of the global reserve.
Under "normal" low memory situations, a block device would typically be
free to grab reserve memory up to its soft limit, allowing it to
optimize over a wider range of queued transactions. My little idea
here is: allocating specific pages to a pool is kind of dumb, all we
really want to do is account precisely for the number of pages we are
allowed to draw from the global reserve.
OK, I kind of digressed, but this all counts as explaining the details
of what Peter and I have been up to for the last year (longer for me).
At this point, we don't need to do the reserve accounting in the most
absolutely perfect way possible, we just need to get something minimal
in place to fix the current deadlock problems, then we can iteratively
improve it.
> I agree, any kind of high-boundary leveling must be implemented in
> device itself, since block layer does not know what device is at the
> end and what it will need to process given block request.
I did not say the throttling has to be implemented in the device, only
that we did it there because it was easiest to code that up and try it
out (it worked). This throttling really wants to live at a higher
level, possibly submit_bio()...bio->endio(). Someone at OLS (James
Bottomley?) suggested it would be better done at the request queue
layer, but I do not immediately see why that should be. I guess this
is going to come down to somebody throwing out a patch for interested
folks to poke at. But this detail is a fine point. The big point is
to have _some_ throttling mechanism in place on the block IO path,
always.
Device mapper in particular does not have any throttling itself: calling
submit_bio on a device mapper device directly calls the device mapper
bio dispatcher. Default initialized block device queue do provide a
crude form of throttling based on limiting the number of requests.
This is insufficiently precise to do a good job in the long run, but it
works for now because the current gaggle of low level block drivers do
not have a lot of resource requirements and tend to behave fairly
predictably (except for some irritating issues re very slow devices
working in parallel with very fast devices, but... worry about that
later). Network block drivers - for example your driver - do have
nontrivial resource requirements and they do not, as far as I can see,
have any form of throttling on the upstream side. So unless you can
demonstrate I'm wrong (I would be more than happy about that) then we
are going to need to add some.
Anyway, I digressed again. _Every_ layer in a block IO stack needs to
have a reserve, if it consumes memory. So we can't escape the question
of how big to make those reserves by trying to push it all down to the
lowest level, hoping that the low level device knows more about how
many requests it will have in flight. For the time being, we will just
plug in some seat of the pants numbers in classic Linux fashion and
that will serve us until somebody gets around to inventing the one true
path discovery mechanism that can sniff around in the block IO stack
and figure out the optimal amount of system resources to reserve at
each level, which ought to be worth at least a master's thesis for
somebody.
Regards,
Daniel
^ permalink raw reply
* Re: [patch 2.6.23-rc1] add xt_statistic.h to the header list for usermode programs
From: David Miller @ 2007-08-05 4:18 UTC (permalink / raw)
To: cebbert; +Cc: netdev
In-Reply-To: <46B365A9.8080507@redhat.com>
From: Chuck Ebbert <cebbert@redhat.com>
Date: Fri, 03 Aug 2007 13:28:09 -0400
> Add xt_statistic.h to the list of headers to install.
>
> Apparently needed to build newer versions of iptables.
>
> Signed-off-by: Chuck Ebbert <cebbert@redhat.com>
Applied, thanks.
^ permalink raw reply
* Re: strange tcp behavior
From: David Miller @ 2007-08-05 3:21 UTC (permalink / raw)
To: johnpol; +Cc: simon, john, netdev
In-Reply-To: <20070804165151.GE14175@2ka.mipt.ru>
From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Date: Sat, 4 Aug 2007 20:51:51 +0400
> On Fri, Aug 03, 2007 at 02:17:17PM -0700, David Miller (davem@davemloft.net) wrote:
> > From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> > Date: Fri, 3 Aug 2007 12:22:42 +0400
> >
> > > Maybe recvmsg should be changed too for symmetry?
> >
> > I took a look at this, and it's not %100 trivial.
> >
> > Let's do this later, and only sendmsg for now in order to
> > fix the bug in the stable branches.
>
> I've tested your patch, besides there was an offset in one of hooks,
> it works perfectly ok.
>
> Feel free to add my ack, tested-by or whatever is needed for this :)
> Your patch fixes the problem.
It is already merged to Linus's tree long before you found a chance to
test it :-) So it would be difficult for me to do so.
^ permalink raw reply
* Re: [RFC Resend 3/3][BNX2]: Add iSCSI support to BNX2 devices.
From: Arnaldo Carvalho de Melo @ 2007-08-05 2:28 UTC (permalink / raw)
To: Michael Chan
Cc: Jeff Garzik, David Miller, mchristi, netdev, open-iscsi, anilgv,
talm, lusinsky, uri
In-Reply-To: <1186273141.4806.4.camel@dell>
There is at least one bug, please check it and the other suggestions,
Thanks,
- Arnaldo
Em Sat, Aug 04, 2007 at 05:19:01PM -0700, Michael Chan escreveu:
> diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
> new file mode 100644
> index 0000000..0576e1b
> --- /dev/null
> +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
> @@ -0,0 +1,3718 @@
> +/* bnx2i_iscsi.c: Broadcom NetXtreme II iSCSI driver.
> +
> + *
> + * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.
> + *
> + * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
> + */
> +
> +#include "bnx2i.h"
> +
> +struct scsi_host_template bnx2i_host_template;
> +struct iscsi_transport bnx2i_iscsi_transport;
> +
> +/*
> + * Global endpoint resource info
> + */
> +void *bnx2i_ep_pages[MAX_PAGES_PER_CTRL_STRUCT_POOL];
> +struct list_head bnx2i_free_ep_list;
> +struct list_head bnx2i_unbound_ep;
> +u32 bnx2i_num_free_ep;
> +u32 bnx2i_max_free_ep;
> +spinlock_t bnx2i_resc_lock;
static DEFINE_SPINLOCK(bnx2i_resc_lock);
I guess the other variables can also be marked static
> +struct tcp_port_mngt bnx2i_tcp_port_tbl;
> +static u16 bnx2i_local_tcp_port = 63000;
> +
> +
> +static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
> + struct bnx2i_cmd *);
> +static void bnx2i_free_tcp_port(u16 port);
> +static u16 bnx2i_alloc_tcp_port(void);
> +
> +
> +static int bnx2i_adapter_ready(struct bnx2i_hba *hba)
> +{
> + int retval = 0;
> +
> + if (!hba || !test_bit(ADAPTER_STATE_UP, &hba->adapter_state) ||
> + test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state))
> + retval = -EPERM;
> + return retval;
> +}
> +
> +/*
> + * identifies & marks various bd info for imm data, unsolicited data
> + * and the first solicited data seq.
> + */
> +static void bnx2i_get_write_cmd_bd_idx(struct bnx2i_cmd *cmd, u32 buf_off,
> + u32 *start_bd_off, u32 *start_bd_idx)
> +{
> + u32 cur_offset = 0;
> + u32 cur_bd_idx = 0;
> + struct iscsi_bd *bd_tbl = cmd->bd_tbl->bd_tbl;
> +
> + if (buf_off) {
> + while (buf_off >= (cur_offset + bd_tbl->buffer_length)) {
> + cur_offset += bd_tbl->buffer_length;
> + cur_bd_idx++;
> + bd_tbl++;
> + }
> + }
> +
> + *start_bd_off = buf_off - cur_offset;
> + *start_bd_idx = cur_bd_idx;
> +}
> +
> +/*
> + * identifies & marks various bd info for immediate data,
> + * unsolicited data and first solicited data seq.
> + */
> +static void bnx2i_setup_write_cmd_bd_info(struct bnx2i_cmd *cmd)
> +{
> + struct bnx2i_conn *conn = NULL;
> + struct bnx2i_sess *sess = NULL;
> + u32 start_bd_offset = 0;
> + u32 start_bd_idx = 0;
> + u32 buffer_offset = 0;
> + u32 seq_len = 0;
> + u32 fbl = 0, mrdsl = 0;
> + u32 cmd_len = cmd->req.total_data_transfer_length;
> +
> + if (cmd)
> + conn = cmd->conn;
> + if (conn->sess)
> + sess = conn->sess;
> +
> + /* if ImmediateData is turned off & IntialR2T is turned on,
> + * there will be no immediate or unsolicited data, just return.
> + */
> + if (sess->initial_r2t && !sess->imm_data) {
> + return;
> + }
> + fbl = sess->first_burst_len;
> + mrdsl = conn->max_data_seg_len_xmit;
> +
> + /* Immediate data */
> + if (sess->imm_data) {
> + seq_len = min(mrdsl, fbl);
> + seq_len = min(cmd_len, seq_len);
> + buffer_offset += seq_len;
> + }
> + if (seq_len == cmd_len) {
> + return;
> + }
> +
> + if (!sess->initial_r2t) {
> + if (seq_len >= fbl)
> + goto r2t_data;
> + seq_len = min(fbl, cmd_len) - seq_len;
> + bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
> + &start_bd_offset, &start_bd_idx);
> + cmd->req.ud_buffer_offset = start_bd_offset;
> + cmd->req.ud_start_bd_index = start_bd_idx;
> + buffer_offset += seq_len;
> + }
> +r2t_data:
> + if (buffer_offset != cmd_len) {
> + bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
> + &start_bd_offset, &start_bd_idx);
> + if ((start_bd_offset > fbl) ||
> + (start_bd_idx > cmd->scsi_cmd->use_sg)) {
> + int i = 0;
> +
> + printk(KERN_ALERT "bnx2i- error, buf offset 0x%x "
> + "bd_valid %d use_sg %d\n",
> + buffer_offset, cmd->bd_tbl->bd_valid,
> + cmd->scsi_cmd->use_sg);
> + for (i = 0; i < cmd->bd_tbl->bd_valid; i++)
> + printk(KERN_ALERT "bnx2i err, bd[%d]: len %x\n",
> + i, cmd->bd_tbl->bd_tbl[i].\
> + buffer_length);
> + }
> + cmd->req.sd_buffer_offset = start_bd_offset;
> + cmd->req.sd_start_bd_index = start_bd_idx;
> + }
> +}
> +
> +
> +/*
> + */
> +static int bnx2i_split_bd(struct bnx2i_cmd *cmd, u64 addr, int sg_len,
> + int bd_index)
> +{
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + int frag_size = 0, sg_frags = 0;
> +
> + while (sg_len) {
> + if (sg_len >= BD_SPLIT_SIZE)
> + frag_size = BD_SPLIT_SIZE;
> + else
> + frag_size = sg_len;
> + bd[bd_index + sg_frags].buffer_addr_lo = (u32) addr;
> + bd[bd_index + sg_frags].buffer_addr_hi = addr >> 32;
> + bd[bd_index + sg_frags].buffer_length = frag_size;
> + bd[bd_index + sg_frags].flags = 0;
> + if ((bd_index + sg_frags) == 0)
> + bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN;
> + addr += (u64) frag_size;
> + sg_frags++;
> + sg_len -= frag_size;
> + }
> + return sg_frags;
> +}
> +
> +
> +/*
> + * map single buffer
> + */
> +static int bnx2i_map_single_buf(struct bnx2i_hba *hba,
> + struct bnx2i_cmd *cmd)
> +{
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + int byte_count = 0;
Why set byte_count to 0...
> + int bd_count = 0;
> + u64 addr;
> +
> + byte_count = sc->request_bufflen;
... when you imediately set it to something else?
> + sc->SCp.dma_handle =
> + pci_map_single(hba->pci_dev, sc->request_buffer,
> + sc->request_bufflen, sc->sc_data_direction);
> + addr = sc->SCp.dma_handle;
> +
> + if (byte_count > MAX_BD_LENGTH) {
> + bd_count = bnx2i_split_bd(cmd, addr, byte_count, 0);
> + } else {
> + bd_count = 1;
Ditto for bd_count
> + bd[0].buffer_addr_lo = addr & 0xffffffff;
> + bd[0].buffer_addr_hi = addr >> 32;
> + bd[0].buffer_length = sc->request_bufflen;
> + bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN |
> + ISCSI_BD_LAST_IN_BD_CHAIN;
> + }
> + bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
> +
> + return bd_count;
> +}
> +
> +
> +/*
> + * map SG list
> + */
> +static int bnx2i_map_sg(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
> +{
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + struct scatterlist *sg;
> + int byte_count = 0;
> + int sg_frags = 0;
> + int bd_count = 0;
> + int sg_count = 0;
> + int sg_len;
> + u64 addr;
> + int i;
> +
> + sg = (struct scatterlist *) sc->request_buffer;
Needless cast, scsi_cmnd->request_buffer is a void pointer.
> +
> + sg_count = pci_map_sg(hba->pci_dev, sg, sc->use_sg,
> + sc->sc_data_direction);
> +
> + for (i = 0; i < sg_count; i++) {
> + sg_len = sg_dma_len(sg);
> + addr = sg_dma_address(sg);
> + if (sg_len > MAX_BD_LENGTH) {
> + sg_frags = bnx2i_split_bd(cmd, addr, sg_len,
> + bd_count);
> + } else {
> + sg_frags = 1;
> + bd[bd_count].buffer_addr_lo = addr & 0xffffffff;
> + bd[bd_count].buffer_addr_hi = addr >> 32;
> + bd[bd_count].buffer_length = sg_len;
> + bd[bd_count].flags = 0;
> + if (bd_count == 0)
> + bd[bd_count].flags =
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> + }
> + byte_count += sg_len;
> + sg++;
> + bd_count += sg_frags;
> + }
> + bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
> +
> + BUG_ON(byte_count != sc->request_bufflen);
> + return bd_count;
> +}
> +
> +/*
> + * creates BD list table for the command
> + */
> +static int bnx2i_iscsi_map_sg_list(struct bnx2i_cmd *cmd)
> +{
> + struct bnx2i_hba *hba = cmd->conn->sess->hba;
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + int bd_count = 0;
> +
> + if (sc->use_sg)
> + bd_count = bnx2i_map_sg(hba, cmd);
> + else if (sc->request_bufflen)
> + bd_count = bnx2i_map_single_buf(hba, cmd);
> + else {
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + bd_count = 0;
No need to set bd_count to zero, you already did it when declaring,
remove this one or the one at declaration time, above.
> + bd[0].buffer_addr_lo = bd[0].buffer_addr_hi = 0;
> + bd[0].buffer_length = bd[0].flags = 0;
> + }
> + cmd->bd_tbl->bd_valid = bd_count;
> + return 0;
> +}
> +
> +
> +/*
> + * create BD list table for the command
> + */
> +int bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd)
> +{
> + struct bnx2i_hba *hba = cmd->conn->sess->hba;
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct pci_dev *pdev = hba->pci_dev;
Why do you declare pdev, and initialize it, here? It is only needed
if...
> + struct scatterlist *sg;
> +
> + if (cmd->bd_tbl->bd_valid && sc) {
... this condition is true, so it should be declared/initialized here
> + if (sc->use_sg) {
and the sg variable should be declared here, where it is needed.
> + sg = (struct scatterlist *) sc->request_buffer;
No need for the cast
> + pci_unmap_sg(pdev, sg, sc->use_sg,
> + sc->sc_data_direction);
> + } else {
> + pci_unmap_single(pdev, sc->SCp.dma_handle,
> + sc->request_bufflen,
> + sc->sc_data_direction);
> + }
> + cmd->bd_tbl->bd_valid = 0;
> + }
> + return 0;
> +}
> +
> +
> +
> +static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd)
> +{
> + memset(&cmd->req, 0x00, sizeof(cmd->req));
> + cmd->req.op_code = ISCSI_OPCODE_SCSI_CMD;
> + cmd->req.bd_list_addr_lo = (u32) cmd->bd_tbl->bd_tbl_dma;
> + cmd->req.bd_list_addr_hi =
> + (u32) ((u64) cmd->bd_tbl->bd_tbl_dma >> 32);
> +
> +}
> +
> +
> +/*
> + * update iscsi cid table entry with connection pointer
> + */
> +static void bnx2i_bind_conn_to_iscsi_cid(struct bnx2i_conn *conn,
> + u32 iscsi_cid)
> +{
> + struct bnx2i_hba *hba = NULL;
needless initialization...
> +
> + if (!conn || !conn->sess)
> + return;
> +
> + hba = conn->sess->hba;
as it is initialized here
> +
> + if (hba->cid_que.conn_cid_tbl[iscsi_cid])
> + printk(KERN_ERR "bnx2i: conn bind - entry #%d not free\n",
> + iscsi_cid);
Strange, what to do with the old value? is it OK just to print this
_error_ message? Haven't checked, just feels suspicious to just use
printk, BUG_ON or WARN_ON case?
> + hba->cid_que.conn_cid_tbl[iscsi_cid] = conn;
> +}
> +
> +
> +/*
> + * maps an iscsi cid to corresponding conn ptr
> + */
> +struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
> + u16 iscsi_cid)
> +{
> + if (!hba->cid_que.conn_cid_tbl) {
> + printk(KERN_ERR "bnx2i: ERROR - missing conn<->cid table\n");
> + return NULL;
> +
> + } else if (iscsi_cid >= hba->max_active_conns) {
> + printk(KERN_ERR "bnx2i: wrong cid #%d\n", iscsi_cid);
> + return NULL;
> + }
> + return(hba->cid_que.conn_cid_tbl[iscsi_cid]);
> +}
> +
> +
> +
> +/*
> + * allocates a iscsi_cid from free pool
> + */
> +static u32 bnx2i_alloc_iscsi_cid(struct bnx2i_hba *hba)
> +{
> + int idx = 0;
Another needless initialization
> +
> + if (!hba->cid_que.cid_free_cnt)
> + return (ISCSI_RESERVED_TAG);
> +
> + idx = hba->cid_que.cid_q_cons_idx;
> + hba->cid_que.cid_q_cons_idx++;
> + if (hba->cid_que.cid_q_cons_idx == hba->cid_que.cid_q_max_idx) {
> + hba->cid_que.cid_q_cons_idx = 0;
> + }
No need for { }, there is just one line in this if
> +
> + hba->cid_que.cid_free_cnt--;
> + return hba->cid_que.cid_que[idx];
> +}
> +
> +
> +/*
> + * return iscsi_cid back to free pool
> + */
> +static void bnx2i_free_iscsi_cid(struct bnx2i_hba *hba, u16 iscsi_cid)
> +{
> + int idx = 0;
Needless initialization
> +
> + if (iscsi_cid == (u16)ISCSI_RESERVED_TAG)
> + return;
> +
> + hba->cid_que.cid_free_cnt++;
> +
> + idx = hba->cid_que.cid_q_prod_idx;
> + hba->cid_que.cid_que[idx] = iscsi_cid;
> + hba->cid_que.conn_cid_tbl[iscsi_cid] = NULL;
> + hba->cid_que.cid_q_prod_idx++;
> + if (hba->cid_que.cid_q_prod_idx == hba->cid_que.cid_q_max_idx) {
Ditto wrt {}
> + hba->cid_que.cid_q_prod_idx = 0;
> + }
> +}
> +
> +
> +
> +/*
> + * setup iscsi_cid queue, 'iscsi_cid' value ranges from 0 to (MAX_CONNS -1)
> + */
> +static int bnx2i_setup_free_cid_que(struct bnx2i_hba *hba)
> +{
> + int mem_size;
> + int i = 0;
> +
> + mem_size = hba->max_active_conns * sizeof(u16);
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> +
> + hba->cid_que.cid_que_base = kmalloc(mem_size, GFP_KERNEL);
good, the value returned by kmalloc is a void pointer, so no need to
cast it...
> + if (!hba->cid_que.cid_que_base)
> + return -ENOMEM;
> +
> + mem_size = hba->max_active_conns * sizeof(struct bnx2i_conn *);
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> + hba->cid_que.conn_cid_tbl =
> + (struct bnx2i_conn **)kmalloc(mem_size, GFP_KERNEL);
... so why do you use a cast here? :-)
> + if (!hba->cid_que.conn_cid_tbl) {
Hey, here you test if this was not allocated...
> + kfree(hba->cid_que.cid_que_base);
> + hba->cid_que.cid_que_base = NULL;
But don't return -ENOMEM here and...
> + }
> +
> + hba->cid_que.cid_que = (u32 *)hba->cid_que.cid_que_base;
> + hba->cid_que.cid_q_prod_idx = 0;
> + hba->cid_que.cid_q_cons_idx = 0;
> + hba->cid_que.cid_q_max_idx = hba->max_active_conns;
> + hba->cid_que.cid_free_cnt = hba->max_active_conns;
> +
> + for (i = 0; i < hba->max_active_conns; i++) {
> + hba->cid_que.cid_que[i] = i;
> + hba->cid_que.conn_cid_tbl[i] = NULL;
... potentially dereference NULL here? Bzzt :)
> + }
> + return 0;
> +}
> +
> +
> +/*
> + * Releases resources held by free 'iscsi_cid' queue
> + */
> +static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba)
> +{
> + if (hba->cid_que.cid_que_base) {
kfree handles receiving a NULL pointer, so no need to duplicate the test
against NULL, just call kfree and set the pointer to NULL
> + kfree(hba->cid_que.cid_que_base);
> + hba->cid_que.cid_que_base = NULL;
> + }
> +
> + if (hba->cid_que.conn_cid_tbl) {
Ditto
> + kfree(hba->cid_que.conn_cid_tbl);
> + hba->cid_que.conn_cid_tbl = NULL;
> + }
> +}
> +
> +
> +/*
> + * routine allocates a free endpoint structure from the global pool
> + */
> +struct bnx2i_endpoint *bnx2i_alloc_ep(void)
> +{
> + struct bnx2i_endpoint *endpoint = NULL;
> + struct list_head *listp;
> + u16 tcp_port;
> +
> + spin_lock_bh(&bnx2i_resc_lock);
> +
> + tcp_port = bnx2i_alloc_tcp_port();
> + if (!tcp_port) {
> + spin_unlock_bh(&bnx2i_resc_lock);
> + return NULL;
> + }
> + if (list_empty(&bnx2i_free_ep_list)) {
> + spin_unlock_bh(&bnx2i_resc_lock);
> + printk(KERN_ERR "alloc_ep: unable to alloc ep struct\n");
> + return endpoint;
Why not return NULL here as you did in the previous test?
Well, since you initialized endpoint to NULL you could, in both cases
just do a goto out_unlock that would be defined...
> + }
> + listp = (struct list_head *)bnx2i_free_ep_list.next;
> + list_del_init(listp);
> + bnx2i_num_free_ep--;
> +
> + endpoint = (struct bnx2i_endpoint *)listp;
> + endpoint->in_use = 1;
> + endpoint->tcp_port = tcp_port;
> + init_waitqueue_head(&endpoint->ofld_wait);
... here:
out_unlock:
I'll stop reviewing here, please consider checking the rest of the patch
for the kinds of things I pointed out,
Best Regards,
- Arnaldo
> +
> + spin_unlock_bh(&bnx2i_resc_lock);
> + return endpoint;
> +}
> +
> +
> +/*
> + * free endpoint structure to global free pool
> + */
> +void bnx2i_free_ep(struct bnx2i_endpoint *endpoint)
> +{
> + if (!endpoint)
> + return;
> +
> + spin_lock_bh(&bnx2i_resc_lock);
> + endpoint->state = EP_STATE_IDLE;
> + endpoint->in_use = 0;
> + bnx2i_free_iscsi_cid(endpoint->hba, endpoint->ep_iscsi_cid);
> + if (endpoint->conn) {
> + endpoint->conn->ep = NULL;
> + endpoint->conn = NULL;
> + }
> + endpoint->sess = NULL;
> +
> + if (endpoint->tcp_port) {
> + bnx2i_free_tcp_port(endpoint->tcp_port);
> + }
> + endpoint->hba = NULL;
> + list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
> + bnx2i_num_free_ep++;
> + spin_unlock_bh(&bnx2i_resc_lock);
> +}
> +
> +
> +/*
> + * allocates free pool of endpoint structurres, endpoint structures
> + * are used to store QP related control and PT info
> + */
> +int bnx2i_alloc_ep_pool(void)
> +{
> + struct bnx2i_endpoint *endpoint = NULL;
> + int index = 0, count = 0;
> + int ret_val = 1;
> + int total_endpoints = 0;
> + int page_count = 0;
> + int num_endpoints_per_page = 0;
> + void *mem_ptr = NULL;
> +
> + spin_lock_init(&bnx2i_resc_lock);
> + INIT_LIST_HEAD(&bnx2i_free_ep_list);
> + INIT_LIST_HEAD(&bnx2i_unbound_ep);
> +
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + bnx2i_ep_pages[index] = NULL;
> + }
> +
> + num_endpoints_per_page =
> + PAGE_SIZE / sizeof(struct bnx2i_endpoint);
> +
> + total_endpoints = ISCSI_MAX_CONNS_PER_HBA;
> + if (total_endpoints >
> + (num_endpoints_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
> + total_endpoints = (num_endpoints_per_page *
> + MAX_PAGES_PER_CTRL_STRUCT_POOL);
> + }
> +
> + bnx2i_num_free_ep = 0;
> + for (index = 0; index < total_endpoints;) {
> + mem_ptr = (void *)kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ERR "ep_pool: mem alloc failed\n");
> + break;
> + }
> + bnx2i_ep_pages[page_count++] = (void *)mem_ptr;
> +
> + memset(mem_ptr, 0, PAGE_SIZE);
> +
> + endpoint = (struct bnx2i_endpoint *)mem_ptr;
> + for (count = 0; count < num_endpoints_per_page; count++) {
> + endpoint->in_use = 0;
> + list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
> + endpoint++;
> + }
> +
> + bnx2i_num_free_ep += num_endpoints_per_page;
> + index += num_endpoints_per_page;
> + }
> + if (bnx2i_num_free_ep == 0)
> + ret_val = 0;
> + bnx2i_max_free_ep = bnx2i_num_free_ep;
> +
> + return(ret_val);
> +}
> +
> +
> +/*
> + * Free memory resources held by global endpoint pool
> + */
> +void bnx2i_release_ep_pool(void)
> +{
> + int index = 0;
> + void *mem_ptr = NULL;
> +
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + mem_ptr = bnx2i_ep_pages[index];
> + if (mem_ptr) {
> + kfree((void *) mem_ptr);
> + break;
> + }
> + bnx2i_ep_pages[index] = NULL;
> + }
> + bnx2i_num_free_ep = 0;
> + return;
> +}
> +
> +
> +/*
> + * iSCSI Session ITT queue management code
> + */
> +static u32 bnx2i_alloc_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
> +{
> + u32 itt_val = ITT_INVALID_SIGNATURE;
> +
> + if (sess->itt_q.itt_q_count) {
> + itt_val = sess->itt_q.itt_que[sess->itt_q.itt_q_cons_idx++];
> + sess->itt_q.itt_q_cons_idx %= sess->itt_q.itt_q_max_idx;
> + sess->itt_q.itt_cmd[itt_val] = cmd;
> + sess->itt_q.itt_q_count--;
> + }
> + return itt_val;
> +}
> +
> +
> +static void bnx2i_free_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
> +{
> + if (cmd->req.itt == ITT_INVALID_SIGNATURE) {
> + printk(KERN_ALERT "free_itt: RSVD ITT - sess 0x%p\n", sess);
> + }
> + sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] = cmd->req.itt;
> + sess->itt_q.itt_q_prod_idx %= sess->itt_q.itt_q_max_idx;
> + sess->itt_q.itt_cmd[cmd->req.itt] = NULL;
> + sess->itt_q.itt_q_count++;
> + cmd->req.itt = ITT_INVALID_SIGNATURE;
> +}
> +
> +
> +/*
> + * setup ITT queue during iSCSI session creation. ITT queue is a
> + * circular array of ITTs [range 0 - (SQ SIZE - 1)] managed by
> + * producer and consumer index
> + */
> +static int bnx2i_setup_free_itt_queue(struct bnx2i_sess *sess)
> +{
> + u16 itt_q_size = (u16)sess->sq_size;
> + u32 itt_value = 0;
> + int unit_size = sizeof(u16);
> + int mem_size = PAGE_SIZE;
> +
> + if ((itt_q_size * unit_size) > mem_size)
> + mem_size = (itt_q_size * unit_size);
> +
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> + sess->itt_q.itt_que_base = kmalloc(mem_size, GFP_KERNEL);
> + if (!sess->itt_q.itt_que_base) {
> + return -ENOMEM;
> + }
> +
> + mem_size = (itt_q_size * sizeof(struct bnx2i_cmd *));
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> + sess->itt_q.itt_cmd =
> + (struct bnx2i_cmd **) kmalloc(mem_size, GFP_KERNEL);
> + if (!sess->itt_q.itt_cmd) {
> + kfree(sess->itt_q.itt_que_base);
> + sess->itt_q.itt_que_base = NULL;
> + return -1;
> + }
> + memset(sess->itt_q.itt_cmd, 0x00, mem_size);
> +
> + sess->itt_q.itt_que = (u32 *)sess->itt_q.itt_que_base;
> + sess->itt_q.itt_q_prod_idx = 0;
> + sess->itt_q.itt_q_cons_idx = 0;
> + sess->itt_q.itt_q_max_idx = itt_q_size;
> + sess->itt_q.itt_q_count = itt_q_size;
> +
> + itt_value = 0;
> + while (itt_value < itt_q_size) {
> + sess->itt_q.itt_cmd[itt_value] = (struct bnx2i_cmd *)NULL;
> + sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] =
> + itt_value++;
> + if (sess->itt_q.itt_q_prod_idx >= sess->itt_q.itt_q_max_idx) {
> + sess->itt_q.itt_q_prod_idx = 0;
> + }
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * free resources held by free ITT queue
> + */
> +static void bnx2i_release_free_itt_queue(struct bnx2i_sess *sess)
> +{
> + sess->itt_q.itt_q_count = 0;
> + if (sess->itt_q.itt_que_base) {
> + kfree (sess->itt_q.itt_que_base);
> + sess->itt_q.itt_que_base = NULL;
> + }
> +
> + if (sess->itt_q.itt_cmd) {
> + kfree (sess->itt_q.itt_cmd);
> + sess->itt_q.itt_cmd = NULL;
> + }
> + return;
> +}
> +
> +
> +/*
> + * allocates a command structures from free poll
> + */
> +struct bnx2i_cmd *bnx2i_alloc_cmd(struct bnx2i_sess *sess)
> +{
> + struct bnx2i_cmd *cmd = NULL;
> + struct list_head *listp;
> +
> + if (unlikely(!sess || (sess->num_free_cmds == 0))) {
> + return cmd;
> + }
> +
> + listp = (struct list_head *) sess->free_cmds.next;
> + list_del_init(listp);
> + sess->num_free_cmds--;
> + cmd = (struct bnx2i_cmd *)listp;
> + cmd->in_use = 1;
> + cmd->scsi_status_rcvd = cmd->resi_len = 0;
> + cmd->scsi_uflow = cmd->scsi_oflow = 0;
> +
> + bnx2i_setup_cmd_wqe_template(cmd);
> +
> + cmd->req.itt = bnx2i_alloc_itt(sess, cmd);
> +
> + return cmd;
> +}
> +
> +
> +/*
> + * return command structure and ITT back to free pool.
> + */
> +void bnx2i_free_cmd(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
> +{
> + if (!sess || !cmd)
> + return;
> +
> + cmd->in_use = 0;
> + bnx2i_free_itt(sess, cmd);
> + list_add_tail(&cmd->link, &sess->free_cmds);
> + sess->num_free_cmds++;
> +}
> +
> +
> +/*
> + * Allocate command structure pool for a given iSCSI session
> + */
> +int bnx2i_alloc_cmd_pool(struct bnx2i_sess *sess)
> +{
> + struct bnx2i_cmd *cmdp = NULL;
> + int index = 0, count = 0;
> + int ret_val = 0;
> + int total_cmds = 0;
> + int num_cmds = 0;
> + int page_count = 0;
> + int num_cmds_per_page = 0;
> + void *mem_ptr = NULL;
> +
> + if (!sess)
> + return -EINVAL;
> +
> + INIT_LIST_HEAD(&sess->free_cmds);
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + sess->cmd_pages[index] = NULL;
> + }
> +
> + num_cmds_per_page = PAGE_SIZE / sizeof(struct bnx2i_cmd);
> + total_cmds = sess->hba->scsi_template->can_queue + 1;
> + if (total_cmds >
> + (num_cmds_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
> + total_cmds = num_cmds_per_page *
> + MAX_PAGES_PER_CTRL_STRUCT_POOL;
> + }
> +
> + for (index = 0; index < total_cmds;) {
> + mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + break;
> + }
> + sess->cmd_pages[page_count++] = (void *)mem_ptr;
> +
> + num_cmds = num_cmds_per_page;
> + if ((total_cmds - index) < num_cmds_per_page)
> + num_cmds = (total_cmds - index);
> +
> + memset(mem_ptr, 0, PAGE_SIZE);
> + cmdp = (struct bnx2i_cmd *) mem_ptr;
> + for (count = 0; count < num_cmds; count++) {
> + cmdp->in_use = 0;
> + cmdp->req.itt = ITT_INVALID_SIGNATURE;
> +
> + /* Allocate BD table */
> + cmdp->bd_tbl = bnx2i_alloc_bd_table(sess, cmdp);
> + if (!cmdp->bd_tbl) {
> + /* should never fail, as it's guaranteed to have
> + * (ISCSI_MAX_CMDS_PER_SESS + 1) BD tables
> + * allocated before calling this function.
> + */
> + printk(KERN_ERR "no BD table cmd %p\n", cmdp);
> + goto bd_table_failed;
> + }
> + list_add_tail(&cmdp->link, &sess->free_cmds);
> + cmdp++;
> + }
> +
> + sess->num_free_cmds += num_cmds;
> + index += num_cmds;
> + }
> + sess->allocated_cmds = sess->num_free_cmds;
> +
> + if (sess->num_free_cmds == 0)
> + ret_val = -ENOMEM;
> + return(ret_val);
> +
> +bd_table_failed:
> + return(-ENOMEM);
> +}
> +
> +
> +/*
> + * Release memory held by command struct pool.
> + */
> +void bnx2i_free_cmd_pool(struct bnx2i_sess *sess)
> +{
> + int index = 0;
> + void *mem_ptr = NULL;
> +
> + if (unlikely(!sess))
> + return;
> +
> + if (sess->num_free_cmds != sess->allocated_cmds) {
> + /*
> + * WARN: either there is some command struct leak or
> + * still some SCSI commands are pending.
> + * TODO: post mortem required...
> + */
> + }
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + mem_ptr = sess->cmd_pages[index];
> + if (mem_ptr) {
> + kfree((void *) mem_ptr);
> + break;
> + }
> + sess->cmd_pages[index] = NULL;
> + }
> + sess->num_free_cmds = sess->allocated_cmds = 0;
> + return;
> +}
> +
> +
> +/*
> + * Allocate a BD table
> + */
> +static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
> + struct bnx2i_cmd *cmd)
> +{
> + struct io_bdt *bd_tbl = NULL;
> +
> + if (list_empty(&sess->bd_tbl_list)) {
> + return NULL;
> + }
> + bd_tbl = (struct io_bdt *)sess->bd_tbl_list.next;
> + list_del(&bd_tbl->link);
> + list_add_tail(&bd_tbl->link, &sess->bd_tbl_active);
> + bd_tbl->bd_valid = 0;
> + if (!bd_tbl->cmdp) {
> + bd_tbl->cmdp = cmd;
> + }
> + return bd_tbl;
> +}
> +
> +
> +/*
> + * Free up memory pages allocated held by BD resources
> + */
> +static void bnx2i_free_all_bdt_resc_pages(struct bnx2i_sess *sess)
> +{
> + int i = 0;
> + struct bd_resc_page *resc_page = NULL;
> +
> + spin_lock_bh(&sess->lock);
> + while (!list_empty(&sess->bd_resc_page)) {
> + resc_page = (struct bd_resc_page *)sess->bd_resc_page.prev;
> + list_del(sess->bd_resc_page.prev);
> + for(i = 0; i < resc_page->num_valid; i++) {
> + kfree(resc_page->page[i]);
> + }
> + kfree(resc_page);
> + }
> + spin_unlock_bh(&sess->lock);
> +}
> +
> +
> +
> +/*
> + * allocated 4K page to track BD table memory
> + */
> +struct bd_resc_page *bnx2i_alloc_bdt_resc_page(struct bnx2i_sess *sess)
> +{
> + void *mem_ptr;
> + struct bd_resc_page *resc_page = NULL;
> +
> + mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!mem_ptr)
> + return NULL;
> +
> + resc_page = (struct bd_resc_page *) mem_ptr;
> + list_add_tail(&resc_page->link, &sess->bd_resc_page);
> + resc_page->max_ptrs = (PAGE_SIZE -
> + (u32)&((struct bd_resc_page *) 0)->page[0]) / sizeof(void *);
> + resc_page->num_valid = 0;
> +
> + return resc_page;
> +}
> +
> +
> +/*
> + * link newly allocated memory page to the list
> + */
> +int bnx2i_add_bdt_resc_page(struct bnx2i_sess *sess, void *bd_page)
> +{
> + struct bd_resc_page *resc_page = NULL;
> +
> +#define is_resc_page_full(_resc_pg) (_resc_pg->num_valid == _resc_pg->max_ptrs)
> +#define active_resc_page(_resc_list) \
> + (list_empty(_resc_list) ? NULL : (_resc_list)->prev)
> + if (list_empty(&sess->bd_resc_page)) {
> + resc_page = bnx2i_alloc_bdt_resc_page(sess);
> + } else {
> + resc_page = (struct bd_resc_page *)
> + active_resc_page(&sess->bd_resc_page);
> + }
> +
> + if (!resc_page)
> + return -ENOMEM;
> +
> + resc_page->page[resc_page->num_valid++] = bd_page;
> + if (is_resc_page_full(resc_page)) {
> + resc_page = bnx2i_alloc_bdt_resc_page(sess);
> + }
> + return 0;
> +}
> +
> +
> +/*
> + * Allocate BD table pool, DMA'able memory for a given session.
> + */
> +int bnx2i_alloc_bd_table_pool(struct bnx2i_sess *sess)
> +{
> + int index = 0, count = 0;
> + int ret_val = 0;
> + int num_elem_per_page;
> + struct io_bdt *bdt_info;
> + char *mem_ptr = NULL;
> + u32 bd_tbl_size = 0;
> + u32 mem_size = 0;
> + int total_bd_tbl = 0;
> +
> + INIT_LIST_HEAD(&sess->bd_resc_page);
> + INIT_LIST_HEAD(&sess->bd_tbl_list);
> + INIT_LIST_HEAD(&sess->bd_tbl_active);
> + total_bd_tbl = sess->hba->scsi_template->can_queue + 1;
> + mem_size = total_bd_tbl * sizeof(struct io_bdt);
> + num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
> + for (index = 0; index < total_bd_tbl; index += num_elem_per_page) {
> + if (((total_bd_tbl - index) * sizeof(struct io_bdt))
> + >= PAGE_SIZE) {
> + mem_size = PAGE_SIZE;
> + num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
> + } else {
> + mem_size =
> + (total_bd_tbl - index) * sizeof(struct io_bdt);
> + num_elem_per_page = (total_bd_tbl - index);
> + }
> + mem_ptr = (void *)kmalloc(mem_size, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ERR "alloc_bd_tbl: mem alloc failed\n");
> + ret_val = -ENOMEM;
> + goto resc_alloc_failed;
> + }
> + bnx2i_add_bdt_resc_page(sess, mem_ptr);
> +
> + memset(mem_ptr, 0, mem_size);
> + bdt_info = (struct io_bdt *)mem_ptr;
> + for (count = 0; count < num_elem_per_page; count++) {
> + list_add_tail(&bdt_info->link, &sess->bd_tbl_list);
> + bdt_info++;
> + }
> + }
> +
> + bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
> + bdt_info = (struct io_bdt *)sess->bd_tbl_list.next;
> + while (bdt_info && (bdt_info != (struct io_bdt *)&sess->bd_tbl_list)) {
> + mem_ptr = (char *)pci_alloc_consistent(sess->hba->pci_dev,
> + bd_tbl_size,
> + &bdt_info->bd_tbl_dma);
> + if (!mem_ptr) {
> + printk(KERN_ERR "bd_tbl: DMA mem alloc failed\n");
> + ret_val = -ENOMEM;
> + goto dma_alloc_failed;
> + }
> + bdt_info->bd_tbl = (struct iscsi_bd *)mem_ptr;
> + bdt_info->max_bd_cnt = ISCSI_MAX_BDS_PER_CMD;
> + bdt_info->bd_valid = 0;
> + bdt_info->cmdp = NULL;
> +
> + bdt_info = (struct io_bdt *)bdt_info->link.next;
> + }
> + return(ret_val);
> +
> +resc_alloc_failed:
> +dma_alloc_failed:
> + return(ret_val);
> +}
> +
> +
> +/*
> + * releases BD table pool memory
> + */
> +void bnx2i_free_bd_table_pool(struct bnx2i_sess *sess)
> +{
> + struct list_head *list;
> + struct io_bdt *bdt_info;
> + u32 bd_tbl_size = 0;
> +
> + bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
> + list_for_each(list, &sess->bd_tbl_list) {
> + bdt_info = list_entry(list, struct io_bdt, link);
> + pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
> + (void *)bdt_info->bd_tbl,
> + bdt_info->bd_tbl_dma);
> + bdt_info->bd_tbl = NULL;
> + if (bdt_info->cmdp) {
> + bdt_info->cmdp->bd_tbl = NULL;
> + bdt_info->cmdp = NULL;
> + }
> + }
> +
> + list_for_each(list, &sess->bd_tbl_active) {
> + bdt_info = list_entry(list, struct io_bdt, link);
> + pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
> + (void *)bdt_info->bd_tbl,
> + bdt_info->bd_tbl_dma);
> + bdt_info->bd_tbl = NULL;
> + if (bdt_info->cmdp) {
> + bdt_info->cmdp->bd_tbl = NULL;
> + bdt_info->cmdp = NULL;
> + }
> + }
> +}
> +
> +
> +/*
> + * allocate memory for dummy buffer and associated BD table
> + * to be used by middle path (MP) requests
> + */
> +static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
> +{
> + int rc = 0;
> + struct iscsi_bd *mp_bdt;
> + u64 addr;
> + hba->mp_bd_tbl = NULL;
> + if (hba->cnic_dev_type == CNIC_10GIG_GEN1)
> + return rc;
> +
> + hba->mp_bd_tbl = pci_alloc_consistent(hba->pci_dev,
> + PAGE_SIZE, &hba->mp_bd_dma);
> + if (!hba->mp_bd_tbl) {
> + printk(KERN_ERR "unable to allocate Middle Path BDT\n");
> + rc = -1;
> + goto out;
> + }
> +
> + hba->dummy_buffer =
> + pci_alloc_consistent(hba->pci_dev,
> + PAGE_SIZE, &hba->dummy_buf_dma);
> + if (!hba->dummy_buffer) {
> + printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n");
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + hba->mp_bd_tbl, hba->mp_bd_dma);
> + hba->mp_bd_tbl = NULL;
> + rc = -1;
> + goto out;
> + }
> +
> + mp_bdt = (struct iscsi_bd *)hba->mp_bd_tbl;
> + addr = (unsigned long)hba->dummy_buf_dma;
> + mp_bdt->buffer_addr_lo = addr & 0xffffffff;
> + mp_bdt->buffer_addr_hi = addr >> 32;
> + mp_bdt->buffer_length = PAGE_SIZE;
> + mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> +
> +out:
> + return rc;
> +}
> +
> +
> +/*
> + * free MP dummy buffer and associated BD table
> + */
> +static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
> +{
> +
> + if (hba->mp_bd_tbl) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + hba->mp_bd_tbl, hba->mp_bd_dma);
> + hba->mp_bd_tbl = NULL;
> + }
> + if (hba->dummy_buffer) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + hba->dummy_buffer, hba->dummy_buf_dma);
> + hba->dummy_buffer = NULL;
> + }
> + return;
> +}
> +
> +
> +static u16 bnx2i_alloc_tcp_port()
> +{
> + return bnx2i_local_tcp_port++;
> +}
> +
> +
> +/*
> + * Function : bnx2i_free_tcp_port
> + * Description:
> + */
> +static void bnx2i_free_tcp_port(u16 port)
> +{
> + if (!bnx2i_tcp_port_tbl.free_q)
> + return;
> +
> + bnx2i_tcp_port_tbl.free_q[bnx2i_tcp_port_tbl.prod_idx] = port;
> + bnx2i_tcp_port_tbl.prod_idx++;
> + bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
> + bnx2i_tcp_port_tbl.num_free_ports++;
> +}
> +
> +void bnx2i_tcp_port_new_entry(u16 tcp_port)
> +{
> + u32 idx = bnx2i_tcp_port_tbl.prod_idx;
> +
> + spin_lock(&bnx2i_resc_lock);
> + bnx2i_tcp_port_tbl.free_q[idx] = (u16)tcp_port;
> + bnx2i_tcp_port_tbl.prod_idx++;
> + bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
> + bnx2i_tcp_port_tbl.num_free_ports++;
> + bnx2i_tcp_port_tbl.num_required--;
> + spin_unlock(&bnx2i_resc_lock);
> +}
> +
> +/*
> + * Function : bnx2i_init_tcp_port_mngr
> + * Description:
> + */
> +void bnx2i_init_tcp_port_mngr(void)
> +{
> + int mem_size = 0;
> +
> + bnx2i_tcp_port_tbl.num_free_ports = 0;
> + bnx2i_tcp_port_tbl.prod_idx = 0;
> + bnx2i_tcp_port_tbl.cons_idx = 0;
> + bnx2i_tcp_port_tbl.max_idx = 0;
> + bnx2i_tcp_port_tbl.num_required = 0;
> +
> +#define BNX2I_MAX_TCP_PORTS 1024
> +
> + bnx2i_tcp_port_tbl.port_tbl_size = BNX2I_MAX_TCP_PORTS;
> +
> + mem_size = sizeof(u16) * bnx2i_tcp_port_tbl.port_tbl_size;
> + if (bnx2i_tcp_port_tbl.port_tbl_size) {
> + bnx2i_tcp_port_tbl.free_q =
> + (u16 *)kmalloc(mem_size, GFP_KERNEL);
> +
> + if (bnx2i_tcp_port_tbl.free_q)
> + bnx2i_tcp_port_tbl.max_idx =
> + bnx2i_tcp_port_tbl.port_tbl_size;
> + }
> +}
> +
> +
> +/*
> + * Function : bnx2i_cleanup_tcp_port_mngr
> + * Description:
> + */
> +void bnx2i_cleanup_tcp_port_mngr(void)
> +{
> + if (bnx2i_tcp_port_tbl.free_q) {
> + kfree(bnx2i_tcp_port_tbl.free_q);
> + bnx2i_tcp_port_tbl.free_q = NULL;
> + }
> + bnx2i_tcp_port_tbl.num_free_ports = 0;
> +}
> +
> +
> +
> +/*
> + * interface was brought down by the user, fail all iSCSI sessions
> + * on this adapter,
> + */
> +void bnx2i_start_iscsi_hba_shutdown(struct bnx2i_hba *hba)
> +{
> + struct list_head *list = NULL;
> + struct list_head *tmp = NULL;
> + struct bnx2i_sess *sess;
> +
> + list_for_each_safe(list, tmp, &hba->active_sess) {
> + sess = (struct bnx2i_sess *)list;
> + bnx2i_do_iscsi_sess_recovery(sess, DID_NO_CONNECT);
> + }
> +}
> +
> +
> +/*
> + * IP address change indication, fail all iSCSI sessions on this adapter
> + */
> +void bnx2i_iscsi_handle_ip_event(struct bnx2i_hba *hba)
> +{
> + struct list_head *list = NULL;
> + struct list_head *tmp = NULL;
> + struct bnx2i_sess *sess;
> +
> + spin_lock(&hba->lock);
> + list_for_each_safe(list, tmp, &hba->active_sess) {
> + sess = (struct bnx2i_sess *)list;
> + spin_unlock(&hba->lock);
> + bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
> + spin_lock(&hba->lock);
> + }
> + spin_unlock(&hba->lock);
> +}
> +
> +
> +
> +static void
> +conn_err_recovery_task(struct work_struct *work)
> +{
> + struct bnx2i_hba *hba = container_of(work, struct bnx2i_hba,
> + err_rec_task);
> + struct bnx2i_sess *sess;
> + int cons_idx = hba->sess_recov_cons_idx;
> +
> + while (hba->sess_recov_prod_idx != cons_idx) {
> + sess = hba->sess_recov_list[cons_idx];
> + bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
> + if (cons_idx == hba->sess_recov_max_idx)
> + cons_idx = 0;
> + else
> + cons_idx++;
> + }
> + hba->sess_recov_cons_idx = cons_idx;
> +}
> +
> +
> +
> +
> +/*
> + * allocate memory buffer to extract conn context
> + */
> +static void bnx2i_init_ctx_dump_mem(struct bnx2i_hba *hba)
> +{
> + if (hba->ctx_addr)
> + return;
> +
> + hba->ictx_poll_mode = 0;
> + hba->ctx_size = 0;
> + hba->ctx_read_cnt = 0xffffffff;
> + hba->ctx_addr = pci_alloc_consistent(hba->pci_dev,
> + BNX2I_CONN_CTX_BUF_SIZE,
> + &hba->ctx_dma_hndl);
> + if (!hba->ctx_addr)
> + return;
> + hba->ctx_size = BNX2I_CONN_CTX_BUF_SIZE;
> +}
> +
> +
> +/*
> + * free context memory buffer
> + */
> +static void bnx2i_free_ctx_dump_mem(struct bnx2i_hba *hba)
> +{
> + if (!hba->ctx_addr || (hba->ctx_size == 0))
> + return;
> +
> + pci_free_consistent(hba->pci_dev, hba->ctx_size,
> + hba->ctx_addr, hba->ctx_dma_hndl);
> + hba->ctx_dma_hndl = 0;
> + hba->ctx_addr = NULL;
> + hba->ctx_size = 0;
> +}
> +
> +
> +static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
> + struct bnx2i_endpoint *ep)
> +{
> + int cur_idx;
> +
> + write_lock(&hba->ep_rdwr_lock);
> + cur_idx = hba->ep_destroy_prod_idx++;
> + hba->ep_destroy_list[cur_idx] = ep;
> + hba->ep_destroy_prod_idx %= hba->ep_destroy_max_idx;
> + write_unlock(&hba->ep_rdwr_lock);
> + return 0;
> +}
> +
> +struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba)
> +{
> + int cur_idx;
> +
> + read_lock(&hba->ep_rdwr_lock);
> + if (hba->ep_destroy_prod_idx == hba->ep_destroy_cons_idx) {
> + read_unlock(&hba->ep_rdwr_lock);
> + return NULL;
> + }
> + cur_idx = hba->ep_destroy_cons_idx++;
> + hba->ep_destroy_cons_idx %= hba->ep_destroy_max_idx;
> + read_unlock(&hba->ep_rdwr_lock);
> +
> + return (hba->ep_destroy_list[cur_idx]);
> +}
> +
> +static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
> + struct bnx2i_endpoint *ep)
> +{
> + int cur_idx;
> +
> + write_lock(&hba->ep_rdwr_lock);
> + cur_idx = hba->ep_ofld_prod_idx++;
> + hba->ep_ofld_list[cur_idx] = ep;
> + hba->ep_ofld_prod_idx %= hba->ep_ofld_max_idx;
> + write_unlock(&hba->ep_rdwr_lock);
> + return 0;
> +}
> +
> +struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba)
> +{
> + int cur_idx;
> +
> + read_lock(&hba->ep_rdwr_lock);
> + if (hba->ep_ofld_prod_idx == hba->ep_ofld_cons_idx) {
> + read_unlock(&hba->ep_rdwr_lock);
> + return NULL;
> + }
> + cur_idx = hba->ep_ofld_cons_idx++;
> + hba->ep_ofld_cons_idx %= hba->ep_ofld_max_idx;
> + read_unlock(&hba->ep_rdwr_lock);
> +
> + return (hba->ep_ofld_list[cur_idx]);
> +}
> +
> +static int bnx2i_init_ep_ofld_destroy_que(struct bnx2i_hba *hba)
> +{
> + rwlock_init(&hba->ep_rdwr_lock);
> + hba->ep_ofld_list = (struct bnx2i_endpoint **)
> + kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!hba->ep_ofld_list)
> + return -ENOMEM;
> +
> + hba->ep_ofld_prod_idx = 0;
> + hba->ep_ofld_cons_idx = 0;
> + hba->ep_ofld_max_idx =
> + PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
> +
> + hba->ep_destroy_list = (struct bnx2i_endpoint **)
> + kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!hba->ep_destroy_list) {
> + kfree(hba->ep_ofld_list);
> + hba->ep_ofld_list = NULL;
> + return -ENOMEM;
> + }
> +
> + hba->ep_destroy_prod_idx = 0;
> + hba->ep_destroy_cons_idx = 0;
> + hba->ep_destroy_max_idx =
> + PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
> + return 0;
> +}
> +
> +
> +static void bnx2i_free_ep_ofld_destroy_que(struct bnx2i_hba *hba)
> +{
> + if (hba->ep_ofld_list) {
> + kfree(hba->ep_ofld_list);
> + hba->ep_ofld_list = NULL;
> + }
> + if (hba->ep_destroy_list) {
> + kfree(hba->ep_destroy_list);
> + hba->ep_destroy_list = NULL;
> + }
> +}
> +
> +/*
> + * allocate & initialize adapter structure and call other
> + * support routines to do per adapter initialization
> + */
> +struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
> +{
> + struct bnx2i_hba *hba = NULL;
> +
> + hba = kmalloc(sizeof(struct bnx2i_hba), GFP_KERNEL);
> +
> + if (hba == NULL)
> + return NULL;
> +
> + memset((void *) hba, 0, sizeof(struct bnx2i_hba));
> +
> + /* Get PCI related information and update hba struct members */
> + hba->pci_dev = cnic->pcidev;
> + if (hba->pci_dev) {
> + hba->pci_did = hba->pci_dev->device;
> + hba->pci_vid = hba->pci_dev->vendor;
> + hba->pci_sdid = hba->pci_dev->subsystem_device;
> + hba->pci_svid = hba->pci_dev->subsystem_vendor;
> + hba->pci_func = PCI_FUNC(hba->pci_dev->devfn);
> + hba->pci_devno = PCI_SLOT(hba->pci_dev->devfn);
> + hba->pci_intr_num = hba->pci_dev->irq;
> + }
> +
> + INIT_LIST_HEAD(&hba->active_sess);
> + if (bnx2i_init_ep_ofld_destroy_que(hba))
> + goto ep_ofld_que_err;
> +
> + hba->mtu_supported = BNX2I_MAX_MTU_SUPPORTED;
> +
> + /* TODO: different values for Teton/Xinan/Everest */
> + hba->max_active_conns = ISCSI_MAX_CONNS_PER_HBA;
> +
> + if (bnx2i_setup_free_cid_que(hba))
> + goto cid_que_err;
> +
> + /* SQ/RQ/CQ size can be changed via sysfx interface */
> + hba->max_sqes = BNX2I_SQ_WQES_DEFAULT;
> + hba->max_rqes = BNX2I_RQ_WQES_DEFAULT;
> + hba->max_cqes = BNX2I_CQ_WQES_DEFAULT;
> + hba->num_ccell = BNX2I_CCELLS_DEFAULT;
> +
> + if (bnx2i_setup_mp_bdt(hba)) {
> + goto mp_bdt_err;
> + }
> +
> + spin_lock_init(&hba->lock);
> + /* initialize timer and wait queue used for resource cleanup when
> + * interface is brought down */
> + init_timer(&hba->hba_timer);
> + init_waitqueue_head(&hba->eh_wait);
> +
> + INIT_WORK(&hba->err_rec_task, conn_err_recovery_task);
> + hba->sess_recov_prod_idx = 0;
> + hba->sess_recov_cons_idx = 0;
> + hba->sess_recov_max_idx = 0;
> + hba->sess_recov_list =
> + (struct bnx2i_sess **)kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!hba->sess_recov_list)
> + goto rec_que_err;
> + hba->sess_recov_max_idx = PAGE_SIZE / sizeof (struct bnx2i_sess *) - 1;
> +
> + bnx2i_init_ctx_dump_mem(hba);
> +
> + return hba;
> +
> +rec_que_err:
> + bnx2i_free_mp_bdt(hba);
> +mp_bdt_err:
> + bnx2i_release_free_cid_que(hba);
> +cid_que_err:
> + bnx2i_free_ep_ofld_destroy_que(hba);
> +ep_ofld_que_err:
> + bnx2i_free_hba(hba);
> +
> + return NULL;
> +}
> +
> +
> +/*
> + * free adapter structure and call various cleanup routines.
> + */
> +void bnx2i_free_hba(struct bnx2i_hba *hba)
> +{
> + if (hba == NULL)
> + return;
> +
> + bnx2i_free_ctx_dump_mem(hba);
> +
> + bnx2i_free_mp_bdt(hba);
> + bnx2i_release_free_cid_que(hba);
> + bnx2i_free_ep_ofld_destroy_que(hba);
> +
> + INIT_LIST_HEAD(&hba->active_sess);
> + /* Free memory held by hba structure */
> + kfree((void *)hba);
> +}
> +
> +
> +
> +
> +/*
> + * return all commands in active queue which should already have been
> + * cleaned up by the cnic device.
> + */
> +static void bnx2i_flush_active_cmd_queue(struct bnx2i_sess *sess, int err_code)
> +{
> + struct list_head *list;
> + struct list_head *tmp;
> + struct bnx2i_cmd *cmd;
> + unsigned long flags;
> + if (!sess->num_active_cmds)
> + return;
> +
> + spin_lock_irqsave(sess->host->host_lock, flags);
> + list_for_each_safe(list, tmp, &sess->active_cmds) {
> + cmd = (struct bnx2i_cmd *) list;
> + cmd->req.itt &= ISCSI_CMD_RESPONSE_INDEX;
> + bnx2i_iscsi_unmap_sg_list(cmd);
> + cmd->cmd_state = ISCSI_CMD_STATE_COMPLETED;
> + list_del_init(&cmd->link);
> + bnx2i_return_failed_command(sess, cmd, err_code);
> + bnx2i_free_cmd(sess, cmd);
> + }
> + spin_unlock_irqrestore(sess->host->host_lock, flags);
> +}
> +
> +
> +/*
> + * initiate cleanup of outstanding commands for sess recovery
> + */
> +static int bnx2i_session_recovery_start(struct bnx2i_sess *sess, int err_code)
> +{
> + if (unlikely(!sess)) {
> + printk(KERN_ALERT "sess_recov_start: sess not active\n");
> + return FAILED;
> + }
> +
> + if (!is_sess_active(sess)) {
> + wait_event_interruptible_timeout(sess->er_wait,
> + (sess->state ==
> + BNX2I_SESS_IN_FFP), HZ);
> + if (signal_pending(current))
> + flush_signals(current);
> + if (!is_sess_active(sess)) {
> + printk(KERN_ALERT "sess_reco: sess still not active\n");
> + sess->lead_conn->state = CONN_STATE_XPORT_FREEZE;
> + return FAILED;
> + }
> + }
> +
> + return SUCCESS;
> +}
> +
> +
> +/*
> + * SCSI host reset handler, which is translates to iSCSI session
> + * recovery
> + */
> +int bnx2i_do_iscsi_sess_recovery(struct bnx2i_sess *sess, int err_code)
> +{
> + struct bnx2i_hba *hba = NULL;
> + struct bnx2i_conn *conn = sess->lead_conn;
> +
> + if (bnx2i_session_recovery_start(sess, err_code) != SUCCESS) {
> + printk(KERN_INFO "bnx2i: sess rec start returned error\n");
> + return FAILED;
> + }
> + hba = sess->hba;
> +
> + sess->recovery_state = ISCSI_SESS_RECOVERY_OPEN_ISCSI;
> + iscsi_conn_error(conn->cls_conn, ISCSI_ERR_CONN_FAILED);
> +
> + /* if session teardown is because of net interface down,
> + * no need to wait for complete recovery */
> + if (err_code == DID_NO_CONNECT) {
> + wait_event_interruptible_timeout(sess->er_wait,
> + !conn->ep,
> + msecs_to_jiffies(1000));
> + } else {
> + wait_event_interruptible(sess->er_wait,
> + ((sess->recovery_state &
> + ISCSI_SESS_RECOVERY_COMPLETE) ||
> + (sess->recovery_state &
> + ISCSI_SESS_RECOVERY_FAILED)));
> + }
> +
> + if (signal_pending(current))
> + flush_signals(current);
> +
> + if (err_code == DID_NO_CONNECT)
> + return SUCCESS;
> +
> + if (sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE) {
> + printk(KERN_INFO "bnx2i: host #%d reset succeeded\n",
> + sess->host->host_no);
> + sess->state = BNX2I_SESS_IN_FFP;
> + } else {
> + return FAILED;
> + }
> + sess->recovery_state = 0;
> + return SUCCESS;
> +}
> +
> +
> +/*
> + * free up resources held by this session
> + */
> +int bnx2i_iscsi_sess_release(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
> +{
> + if (!sess)
> + return 0;
> +
> + bnx2i_release_free_itt_queue(sess);
> + bnx2i_free_cmd_pool(sess);
> + bnx2i_free_bd_table_pool(sess);
> + bnx2i_free_all_bdt_resc_pages(sess);
> +
> + list_del_init(&sess->link);
> + hba->num_active_sess--;
> +
> + return 0;
> +}
> +
> +
> +/*
> + * initialize various per session statistic counters
> + */
> +static void bnx2i_init_iscsi_sess_stats(struct bnx2i_sess *sess)
> +{
> + if (!sess)
> + return;
> +
> + sess->violation_notified = 0;
> +
> + sess->total_data_octets_sent = 0;
> + sess->total_data_octets_rcvd = 0;
> + sess->conn_login_ok = 0;
> + sess->conn_login_failed = 0;
> + sess->num_login_req_pdus = 0;
> + sess->num_login_resp_pdus = 0;
> + sess->num_scsi_cmd_pdus = 0;
> + sess->num_scsi_resp_pdus = 0;
> + sess->num_nopout_pdus = 0;
> + sess->num_nopin_pdus = 0;
> + sess->num_reject_pdus = 0;
> + sess->num_async_pdus = 0;
> + sess->num_dataout_pdus = 0;
> + sess->num_r2t_pdus = 0;
> + sess->num_datain_pdus = 0;
> + sess->num_snack_pdus = 0;
> + sess->num_text_req_pdus = 0;
> + sess->num_text_resp_pdus = 0;
> + sess->num_tmf_req_pdus = 0;
> + sess->num_tmf_resp_pdus = 0;
> + sess->num_logout_req_pdus = 0;
> + sess->num_logout_resp_pdus = 0;
> +}
> +
> +
> +/*
> + * set iSCSI parameter values to defaults, as defined in rfc3720
> + */
> +static void bnx2i_sess_set_param_defaults(struct bnx2i_sess *sess)
> +{
> + sess->initial_r2t = ISCSI_DEFAULT_INITIAL_R2T;
> + sess->max_r2t = ISCSI_DEFAULT_MAX_OUTSTANDING_R2T;
> + sess->imm_data = ISCSI_DEFAULT_IMMEDIATE_DATA;
> + sess->first_burst_len = ISCSI_DEFAULT_FIRST_BURST_LENGTH;
> + sess->max_burst_len = ISCSI_DEFAULT_MAX_BURST_LENGTH;
> + sess->time2wait = 2;
> + sess->time2retain = 20;
> +}
> +
> +
> +/*
> + * initialize session structure elements and allocate per sess resources
> + */
> +int bnx2i_iscsi_sess_new(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
> +{
> + int rc;
> +
> + spin_lock(&hba->lock);
> + list_add_tail(&sess->link, &hba->active_sess);
> + hba->num_active_sess++;
> + spin_unlock(&hba->lock);
> +
> + sess->sq_size = hba->max_sqes;
> + sess->tsih = 0;
> + sess->lead_conn = NULL;
> +
> + spin_lock_init(&sess->lock);
> +
> + /* initialize active connection list */
> + INIT_LIST_HEAD(&sess->conn_list);
> + INIT_LIST_HEAD(&sess->free_cmds);
> +
> + INIT_LIST_HEAD(&sess->active_cmds);
> + sess->num_active_cmds = 0;
> +
> + sess->num_active_conn = 0;
> + sess->max_conns = 1;
> + sess->conn_id = 0;
> + sess->target_name = NULL;
> +
> + sess->state = BNX2I_SESS_INITIAL;
> + sess->recovery_state = 0;
> +
> + if (bnx2i_alloc_bd_table_pool(sess) != 0) {
> + printk(KERN_ERR "sess_new: unable to alloc bd table pool\n");
> + rc = -ENOMEM;
> + goto err_bd_pool;
> + }
> +
> + if (bnx2i_alloc_cmd_pool(sess) != 0) {
> + printk(KERN_ERR "sess_new: alloc cmd pool failed\n");
> + rc = -ENOMEM;
> + goto err_cmd_pool;
> + }
> +
> + rc = bnx2i_setup_free_itt_queue(sess);
> + if (rc) {
> + rc = -ENOMEM;
> + goto err_itt_que;
> + }
> +
> + init_timer(&sess->abort_timer);
> + init_waitqueue_head(&sess->er_wait);
> + init_timer(&sess->poll_timer);
> +
> + bnx2i_init_iscsi_sess_stats(sess);
> + bnx2i_sess_set_param_defaults(sess);
> +
> + return 0;
> +
> +err_itt_que:
> + bnx2i_free_cmd_pool(sess);
> +err_cmd_pool:
> + bnx2i_free_bd_table_pool(sess);
> +err_bd_pool:
> + return rc;
> +}
> +
> +
> +/*
> + * Login related resources is freed in this routine.
> + */
> +void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn)
> +{
> + if (conn->gen_pdu.resp_bd_tbl) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + conn->gen_pdu.resp_bd_tbl,
> + conn->gen_pdu.resp_bd_dma);
> + conn->gen_pdu.resp_bd_tbl = NULL;
> + }
> +
> + if (conn->gen_pdu.req_bd_tbl) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + conn->gen_pdu.req_bd_tbl,
> + conn->gen_pdu.req_bd_dma);
> + conn->gen_pdu.req_bd_tbl = NULL;
> + }
> +
> + if (conn->gen_pdu.resp_buf) {
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.resp_buf,
> + conn->gen_pdu.resp_dma_addr);
> + conn->gen_pdu.resp_buf = NULL;
> + }
> +
> + if (conn->gen_pdu.req_buf) {
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.req_buf,
> + conn->gen_pdu.req_dma_addr);
> + conn->gen_pdu.req_buf = NULL;
> + }
> +}
> +
> +
> +/*
> + * Login & nop-in related resources is allocated in this routine.
> + */
> +static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn)
> +{
> + /* Allocate memory for login request/response buffers */
> + conn->gen_pdu.req_buf =
> + (char *) pci_alloc_consistent(hba->pci_dev,
> + ISCSI_CONN_LOGIN_BUF_SIZE,
> + &conn->gen_pdu.req_dma_addr);
> + if (conn->gen_pdu.req_buf == NULL)
> + goto login_req_buf_failure;
> +
> + conn->gen_pdu.req_buf_size = 0;
> + conn->gen_pdu.req_wr_ptr = conn->gen_pdu.req_buf;
> +
> + conn->gen_pdu.resp_buf =
> + (char *) pci_alloc_consistent(hba->pci_dev,
> + ISCSI_CONN_LOGIN_BUF_SIZE,
> + &conn->gen_pdu.resp_dma_addr);
> + if (conn->gen_pdu.resp_buf == NULL)
> + goto login_resp_buf_failure;
> +
> + conn->gen_pdu.resp_buf_size = ISCSI_CONN_LOGIN_BUF_SIZE;
> + conn->gen_pdu.resp_wr_ptr = conn->gen_pdu.resp_buf;
> +
> + conn->gen_pdu.req_bd_tbl =
> + (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
> + &conn->gen_pdu.req_bd_dma);
> + if (conn->gen_pdu.req_bd_tbl == NULL)
> + goto login_req_bd_tbl_failure;
> +
> + conn->gen_pdu.resp_bd_tbl =
> + (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
> + &conn->gen_pdu.resp_bd_dma);
> + if (conn->gen_pdu.resp_bd_tbl == NULL)
> + goto login_resp_bd_tbl_failure;
> +
> + return 0;
> +
> +login_resp_bd_tbl_failure:
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE, conn->gen_pdu.req_bd_tbl,
> + conn->gen_pdu.req_bd_dma);
> + conn->gen_pdu.req_bd_tbl = NULL;
> +
> +login_req_bd_tbl_failure:
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.resp_buf,
> + conn->gen_pdu.resp_dma_addr);
> + conn->gen_pdu.resp_buf = NULL;
> +login_resp_buf_failure:
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.req_buf, conn->gen_pdu.req_dma_addr);
> + conn->gen_pdu.req_buf = NULL;
> +login_req_buf_failure:
> + printk(KERN_ERR "bnx2i:a conn login resource alloc failed!!\n");
> + return -ENOMEM;
> +
> +}
> +
> +
> +/*
> + * connection structure is initialized in this routine.
> + */
> +int bnx2i_iscsi_conn_new(struct bnx2i_sess *sess, struct bnx2i_conn *conn)
> +{
> + int ret_code = 0;
> + struct bnx2i_hba *hba = sess->hba;
> +
> + if (!sess || !conn || !hba)
> + return -EINVAL;
> +
> + conn->sess = sess;
> + conn->header_digest_en = 0;
> + conn->data_digest_en = 0;
> +
> + spin_lock_init(&conn->lock);
> +
> + init_timer(&conn->poll_timer);
> + conn->gen_pdu.cmd = NULL;
> +
> + /* 'ep' ptr will be assigned in bind() call */
> + conn->ep = NULL;
> +
> + ret_code = bnx2i_conn_alloc_login_resources(hba, conn);
> + if (ret_code != 0) {
> + printk(KERN_ALERT "conn_new: login resc alloc failed!!\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * extract & update SN counters from login response
> + */
> +static int bnx2i_login_resp_update_cmdsn(struct bnx2i_conn *conn)
> +{
> + u32 max_cmdsn;
> + u32 exp_cmdsn;
> + u32 stat_sn;
> + struct bnx2i_sess *sess = conn->sess;
> + struct iscsi_nopin *hdr = NULL;
> +
> + hdr = (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
> +
> + max_cmdsn = ntohl(hdr->max_cmdsn);
> + exp_cmdsn = ntohl(hdr->exp_cmdsn);
> + stat_sn = ntohl(hdr->statsn);
> +#define SN_DELTA_ISLAND 0xffff
> + if (max_cmdsn < exp_cmdsn -1 &&
> + max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND)
> + return -EINVAL;
> +
> + if (max_cmdsn > sess->max_cmdsn ||
> + max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
> + sess->max_cmdsn = max_cmdsn;
> +
> + if (exp_cmdsn > sess->exp_cmdsn ||
> + exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
> + sess->exp_cmdsn = exp_cmdsn;
> + }
> + if (stat_sn == conn->exp_statsn)
> + conn->exp_statsn++;
> +
> + return 0;
> +}
> +
> +
> +/*
> + * update iSCSI SN counters for the given session
> + */
> +void bnx2i_update_cmd_sequence(struct bnx2i_sess *sess,
> + u32 exp_sn, u32 max_sn)
> +{
> + u32 exp_cmdsn = exp_sn;
> + u32 max_cmdsn = max_sn;
> +
> + if (max_cmdsn < exp_cmdsn -1 &&
> + max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND) {
> + printk(KERN_ALERT "cmd_sequence: error, exp 0x%x, max 0x%x\n",
> + exp_cmdsn, max_cmdsn);
> + BUG_ON(1);
> + }
> + if (max_cmdsn > sess->max_cmdsn ||
> + max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
> + sess->max_cmdsn = max_cmdsn;
> + if (exp_cmdsn > sess->exp_cmdsn ||
> + exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
> + sess->exp_cmdsn = exp_cmdsn;
> + }
> +
> + return;
> +}
> +
> +
> +/*
> + * This function propogates SCSI response to SCSI-ML by calling
> + * scsi_done() and also returns command struct back to free pool
> + */
> +int bnx2i_process_scsi_resp(struct bnx2i_cmd *cmd)
> +{
> + int ret = 0;
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct Scsi_Host *host;
> + int res_count = 0;
> +
> + if (!sc)
> + return 0;
> +
> + host = cmd->conn->sess->host;
> + sc->result = (DID_OK << 16) | cmd->scsi_status;
> +
> + if (cmd->iscsi_resp != ISCSI_STATUS_CMD_COMPLETED) {
> + sc->result = (DID_ERROR << 16);
> + goto call_scsi_done;
> + }
> +
> + if (sc->sc_data_direction == DMA_TO_DEVICE) {
> + goto call_scsi_done;
> + }
> +
> + if (cmd->scsi_uflow) {
> + res_count = cmd->resi_len;
> + if (res_count > 0 && res_count <= sc->request_bufflen)
> + sc->resid = res_count;
> + else
> + sc->result = (DID_BAD_TARGET << 16) |
> + cmd->scsi_status;
> + } else if (cmd->scsi_oflow) {
> + sc->resid = res_count;
> + }
> +
> +call_scsi_done:
> + if ((cmd->cmd_state == ISCSI_CMD_STATE_ABORT_PEND) ||
> + (cmd->cmd_state == ISCSI_CMD_STATE_CLEANUP_PEND)) {
> + printk(KERN_ALERT "scsi_resp: command is being aborted\n");
> + return -1;
> + }
> +
> + spin_lock(host->host_lock);
> + cmd->scsi_cmd = NULL;
> + cmd->conn->sess->num_active_cmds--;
> + sc->scsi_done(sc);
> + bnx2i_free_cmd(cmd->conn->sess, cmd);
> + spin_unlock(host->host_lock);
> + return ret;
> +}
> +
> +
> +
> +/*
> + * login response PDU is pushed to application daemon by
> + * calling iscsi_recv_pdu()
> + */
> +int bnx2i_indicate_login_resp(struct bnx2i_conn *conn)
> +{
> + int ret = 0;
> + int data_len = 0;
> + struct iscsi_login_rsp *login_resp =
> + (struct iscsi_login_rsp *) &conn->gen_pdu.resp_hdr;
> +
> + /* check if this is the first login response for this connection.
> + * If yes, we need to copy initial StatSN to connection structure.
> + */
> + if (conn->exp_statsn == STATSN_UPDATE_SIGNATURE) {
> + conn->exp_statsn = ntohl(login_resp->statsn) + 1;
> + }
> +
> + ret = bnx2i_login_resp_update_cmdsn(conn);
> + if (ret != 0) {
> + return -EINVAL;
> + }
> +
> + data_len = conn->gen_pdu.resp_wr_ptr - conn->gen_pdu.resp_buf;
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) login_resp,
> + (char *) conn->gen_pdu.resp_buf, data_len);
> +
> + return 0;
> +}
> +
> +
> +/*
> + * deliver logout response PDU to application daemon
> + */
> +int bnx2i_indicate_logout_resp(struct bnx2i_conn *conn)
> +{
> + struct iscsi_logout_rsp *logout_resp =
> + (struct iscsi_logout_rsp *) &conn->gen_pdu.resp_hdr;
> +
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) logout_resp,
> + (char *) NULL, 0);
> +
> + return 0;
> +}
> +
> +
> +/*
> + * deliver iSCSI async PDU to user daemon
> + */
> +int bnx2i_indicate_async_mesg(struct bnx2i_conn *conn)
> +{
> + struct iscsi_async *async_msg =
> + (struct iscsi_async *) &conn->gen_pdu.resp_hdr;
> +
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) async_msg,
> + (char *) NULL, 0);
> +
> + return 0;
> +}
> +
> +
> +
> +/*
> + * Function : bnx2i_process_nopin
> + */
> +int bnx2i_process_nopin(struct bnx2i_conn *conn, struct bnx2i_cmd *cmd,
> + char *data_buf, int data_len)
> +{
> + struct iscsi_nopin *nopin_msg =
> + (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
> +
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) nopin_msg,
> + (char *) data_buf, data_len);
> +
> + spin_lock(conn->sess->host->host_lock);
> + list_del_init(&cmd->link);
> + bnx2i_free_cmd(cmd->conn->sess, cmd);
> + spin_unlock(conn->sess->host->host_lock);
> +
> + return 0;
> +}
> +
> +
> +
> +/*
> + * Allocates buffers and BD tables before shipping requests to cnic
> + * for PDUs prepared by 'iscsid' daemon
> + */
> +static void bnx2i_iscsi_prep_generic_pdu_bd(struct bnx2i_conn *conn)
> +{
> + struct iscsi_bd *bd_tbl = NULL;
> +
> + bd_tbl = (struct iscsi_bd *) conn->gen_pdu.req_bd_tbl;
> +
> + bd_tbl->buffer_addr_hi =
> + (u32) ((u64) conn->gen_pdu.req_dma_addr >> 32);
> + bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.req_dma_addr;
> + bd_tbl->buffer_length = conn->gen_pdu.req_wr_ptr -
> + conn->gen_pdu.req_buf;
> + bd_tbl->reserved0 = 0;
> + bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> +
> + bd_tbl = (struct iscsi_bd *) conn->gen_pdu.resp_bd_tbl;
> + bd_tbl->buffer_addr_hi = (u64) conn->gen_pdu.resp_dma_addr >> 32;
> + bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.resp_dma_addr;
> + bd_tbl->buffer_length = ISCSI_CONN_LOGIN_BUF_SIZE;
> + bd_tbl->reserved0 = 0;
> + bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> +}
> +
> +
> +
> +/*
> + * called to transmit PDUs prepared by the 'iscsid' daemon. iSCSI login,
> + * Nop-out and Logout requests flow through this path.
> + */
> +static int bnx2i_iscsi_send_generic_request(struct bnx2i_cmd *cmnd)
> +{
> + int rc = 0;
> + char *buf = NULL;
> + int data_len = 0;
> + struct bnx2i_conn *conn = cmnd->conn;
> +
> + bnx2i_iscsi_prep_generic_pdu_bd(conn);
> + switch (cmnd->iscsi_opcode & ISCSI_OPCODE_MASK) {
> + case ISCSI_OP_LOGIN:
> + bnx2i_send_iscsi_login(conn, cmnd);
> + break;
> +
> + case ISCSI_OP_NOOP_OUT:
> + data_len = conn->gen_pdu.req_buf_size;
> + buf = conn->gen_pdu.req_buf;
> + if (data_len)
> + rc = bnx2i_send_iscsi_nopout(conn, cmnd,
> + ISCSI_RESERVED_TAG,
> + buf, data_len, 1);
> + else
> + rc = bnx2i_send_iscsi_nopout(conn, cmnd,
> + ISCSI_RESERVED_TAG,
> + NULL, 0, 1);
> + break;
> +
> + case ISCSI_OP_LOGOUT:
> + rc = bnx2i_send_iscsi_logout(conn, cmnd);
> + break;
> +
> + default:
> + printk(KERN_ALERT "send_gen: unsupported op 0x%x\n",
> + cmnd->iscsi_opcode);
> + }
> + return rc;
> +}
> +
> +
> +/**********************************************************************
> + * SCSI-ML Interface
> + **********************************************************************/
> +
> +static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc,
> + struct bnx2i_cmd *cmd)
> +{
> + u32 dword;
> + int lpcnt = 0;
> + u8 *srcp = NULL;
> + u32 *dstp = NULL;
> + u32 scsi_lun[2];
> +
> + int_to_scsilun(sc->device->lun, (struct scsi_lun *) scsi_lun);
> + cmd->req.lun[0] = ntohl(scsi_lun[0]);
> + cmd->req.lun[1] = ntohl(scsi_lun[1]);
> +
> + lpcnt = cmd->scsi_cmd->cmd_len / sizeof(dword);
> + srcp = (u8 *) sc->cmnd;
> + dstp = (u32 *) cmd->req.cdb;
> + while (lpcnt--) {
> + memcpy(&dword, srcp, 4);
> + *dstp = cpu_to_be32(dword);
> + srcp += 4;
> + dstp++;
> + }
> + if (sc->cmd_len & 0x3) {
> + dword = (u32) srcp[0] | ((u32) srcp[1] << 8);
> + *dstp = cpu_to_be32(dword);
> + }
> +}
> +
> +
> +
> +/*
> + * handles SCSI command queued by SCSI-ML, allocates a command structure,
> + * assigning CMDSN, mapping SG buffers and handing over request to CNIC.
> + */
> +int bnx2i_queuecommand(struct scsi_cmnd *sc,
> + void (*done) (struct scsi_cmnd *))
> +{
> + struct Scsi_Host *shost;
> + struct bnx2i_sess *sess = NULL;
> + struct bnx2i_conn *conn = NULL;
> + struct bnx2i_cmd *cmd = NULL;
> + struct bnx2i_hba *hba = NULL;
> + static int old_recovery_state = 0;
> +
> + sc->scsi_done = done;
> + sc->result = 0;
> + shost = sc->device->host;
> + sess = iscsi_hostdata(shost->hostdata);
> + BUG_ON(shost != sess->host);
> +
> + if (sess) {
> + hba = sess->hba;
> + } else {
> + printk(KERN_ALERT "bnx2i: quecmd: Error dev not found \n");
> + goto dev_not_found;
> + }
> +
> +#define iscsi_cmd_win_closed(_sess) \
> + ((int) (_sess->max_cmdsn - _sess->cmdsn) < 0)
> +
> + if (iscsi_cmd_win_closed(sess)) {
> + goto iscsi_win_closed;
> + }
> +
> + if ((sess->state & BNX2I_SESS_IN_SHUTDOWN) ||
> + (sess->state & BNX2I_SESS_IN_LOGOUT)) {
> + goto dev_not_found;
> + }
> +
> + if (sess->recovery_state) {
> + if (old_recovery_state != sess->recovery_state) {
> + old_recovery_state = sess->recovery_state;
> + }
> +
> + if (sess->recovery_state & ISCSI_SESS_RECOVERY_FAILED)
> + goto dev_not_found;
> + else if (!(sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE))
> + goto iscsi_win_closed;
> + else
> + sess->recovery_state = 0;
> + }
> +
> + cmd = bnx2i_alloc_cmd(sess);
> + if (cmd == NULL) {
> + /* This should never happen as cmd list size == SHT->can_queue
> + */
> + goto cmd_not_accepted;
> + }
> +
> + cmd->conn = conn = sess->lead_conn;
> + cmd->scsi_cmd = sc;
> + cmd->req.total_data_transfer_length = sc->request_bufflen;
> + cmd->iscsi_opcode = ISCSI_OPCODE_SCSI_CMD;
> + cmd->req.cmd_sn = sess->cmdsn++;
> +
> + bnx2i_iscsi_map_sg_list(cmd);
> + bnx2i_cpy_scsi_cdb(sc, cmd);
> +
> + if (sc->sc_data_direction == DMA_TO_DEVICE) {
> + cmd->req.op_attr = ISCSI_CMD_REQUEST_WRITE;
> + cmd->req.itt |= (ISCSI_TASK_TYPE_WRITE <<
> + ISCSI_CMD_REQUEST_TYPE_SHIFT);
> + bnx2i_setup_write_cmd_bd_info(cmd);
> + } else {
> + cmd->req.op_attr = ISCSI_CMD_REQUEST_READ;
> + cmd->req.itt |= (ISCSI_TASK_TYPE_READ <<
> + ISCSI_CMD_REQUEST_TYPE_SHIFT);
> + }
> + cmd->req.num_bds = cmd->bd_tbl->bd_valid;
> + if (!cmd->bd_tbl->bd_valid) {
> + cmd->req.bd_list_addr_lo = (u32) hba->mp_bd_dma;
> + cmd->req.bd_list_addr_hi =
> + (u32) ((u64) hba->mp_bd_dma >> 32);
> + cmd->req.num_bds = 1;
> + }
> +
> + cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
> + sc->SCp.ptr = (char *) cmd;
> +
> + if (cmd->req.itt != ITT_INVALID_SIGNATURE) {
> + bnx2i_send_iscsi_scsicmd(conn, cmd);
> + list_add_tail(&cmd->link, &sess->active_cmds);
> + sess->num_active_cmds++;
> + }
> + return 0;
> +
> +iscsi_win_closed:
> +cmd_not_accepted:
> + return SCSI_MLQUEUE_HOST_BUSY;
> +
> +dev_not_found:
> + sc->sense_buffer[0] = 0x70;
> + sc->sense_buffer[2] = NOT_READY;
> + sc->sense_buffer[7] = 0x6;
> + sc->sense_buffer[12] = 0x08;
> + sc->sense_buffer[13] = 0x00;
> + sc->result = (DID_NO_CONNECT << 16);
> + sc->resid = sc->request_bufflen;
> + sc->scsi_done(sc);
> + return 0;
> +}
> +
> +
> +
> +/*
> + * TMF request timeout handler
> + */
> +static void bnx2i_iscsi_tmf_timer(unsigned long data)
> +{
> + struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) data;
> +
> + printk(KERN_ALERT "TMF timer: abort failed, cmd 0x%p\n", cmd);
> + cmd->cmd_state = ISCSI_CMD_STATE_FAILED;
> + wake_up(&cmd->conn->sess->er_wait);
> +}
> +
> +
> +/*
> + * initiate command abort process by requesting CNIC to send
> + * an iSCSI TMF request to target
> + */
> +static int bnx2i_initiate_abort_cmd(struct scsi_cmnd *sc)
> +{
> + struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
> + struct bnx2i_cmd *tmf_cmd = NULL;
> + struct Scsi_Host *shost = cmd->scsi_cmd->device->host;
> + struct bnx2i_conn *conn = cmd->conn;
> + struct bnx2i_sess *sess = NULL;
> + struct bnx2i_hba *hba = NULL;
> +
> + shost = cmd->scsi_cmd->device->host;
> + sess = iscsi_hostdata(shost->hostdata);
> + BUG_ON(shost != sess->host);
> +
> + if (sess && (is_sess_active(sess))) {
> + hba = sess->hba;
> + } else {
> + return FAILED;
> + }
> +
> + bnx2i_setup_ictx_dump(hba, conn);
> +
> + if (cmd->scsi_cmd != sc) {
> + /* command already completed to scsi mid-layer */
> + goto cmd_not_active;
> + }
> +
> + tmf_cmd = bnx2i_alloc_cmd(sess);
> + if (cmd == NULL) {
> + goto lack_of_resc;
> + }
> +
> + tmf_cmd->conn = conn = sess->lead_conn;
> + tmf_cmd->scsi_cmd = NULL;
> + tmf_cmd->iscsi_opcode = ISCSI_OPCODE_TMF_REQUEST;
> + tmf_cmd->req.cmd_sn = sess->cmdsn;
> + tmf_cmd->tmf_ref_itt = cmd->req.itt;
> + tmf_cmd->tmf_ref_cmd = cmd;
> + tmf_cmd->tmf_ref_sc = cmd->scsi_cmd;
> + cmd->cmd_state = ISCSI_CMD_STATE_ABORT_PEND;
> + tmf_cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
> +
> + sess->abort_timer.expires = 10*HZ + jiffies;
> + sess->abort_timer.function = bnx2i_iscsi_tmf_timer;
> + sess->abort_timer.data = (unsigned long)tmf_cmd;
> + add_timer(&sess->abort_timer);
> +
> + bnx2i_send_iscsi_tmf(conn, tmf_cmd);
> +
> + /* update iSCSI context for this conn, wait for CNIC to complete */
> + wait_event_interruptible(sess->er_wait,
> + tmf_cmd->cmd_state != ISCSI_CMD_STATE_INITIATED);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> +
> + del_timer_sync(&sess->abort_timer);
> +
> + if (tmf_cmd->cmd_state == ISCSI_CMD_STATE_FAILED) {
> + printk(KERN_ALERT "abort: abort failed, cmd 0x%p\n", tmf_cmd);
> + /* TMF timed out, return error status and let SCSI-ML do
> + * session recovery.
> + */
> + list_del_init(&tmf_cmd->link);
> + bnx2i_free_cmd(sess, tmf_cmd);
> + return FAILED;
> + }
> +
> + list_del_init(&tmf_cmd->link);
> + bnx2i_free_cmd(sess, tmf_cmd);
> +
> + if ((cmd->scsi_cmd->result & 0xFF0000) == (DID_ABORT << 16)) {
> + cmd->cmd_state = ISCSI_CMD_STATE_CLEANUP_PEND;
> + bnx2i_send_cmd_cleanup_req(hba, cmd);
> + wait_event_interruptible_timeout(sess->er_wait,
> + (cmd->cmd_state ==
> + ISCSI_CMD_STATE_CLEANUP_CMPL),
> + msecs_to_jiffies(
> + ISCSI_CMD_CLEANUP_TIMEOUT));
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + } else {
> + cmd->scsi_cmd->result = (DID_ABORT << 16);
> + }
> + cmd->conn->sess->num_active_cmds--;
> + list_del_init(&cmd->link);
> + cmd->scsi_cmd = NULL;
> + bnx2i_free_cmd(cmd->conn->sess, cmd);
> +
> +cmd_not_active:
> + return SUCCESS;
> +
> +lack_of_resc:
> + return FAILED;
> +}
> +
> +
> +/*
> + * SCSI abort request handler.
> + */
> +int bnx2i_abort(struct scsi_cmnd *sc)
> +{
> + int reason;
> + struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
> +
> + if (unlikely(!cmd)) {
> + /* command already completed to scsi mid-layer */
> + printk(KERN_INFO "bnx2i_abort: sc 0x%p, not active\n", sc);
> + return SUCCESS;
> + }
> +
> + reason = bnx2i_initiate_abort_cmd(sc);
> + return reason;
> +}
> +
> +
> +
> +/*
> + * hardware reset
> + */
> +int bnx2i_reset(struct scsi_cmnd *sc)
> +{
> + return 0;
> +}
> +
> +
> +void bnx2i_return_failed_command(struct bnx2i_sess *sess,
> + struct bnx2i_cmd *cmd, int err_code)
> +{
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + sc->result = err_code << 16;
> + sc->resid = cmd->scsi_cmd->request_bufflen;
> + cmd->scsi_cmd = NULL;
> + sess->num_active_cmds--;
> + sc->scsi_done(sc);
> +}
> +
> +
> +
> +/*
> + * SCSI host reset handler - iSCSI session recovery
> + */
> +int bnx2i_host_reset(struct scsi_cmnd *sc)
> +{
> + struct Scsi_Host *shost = sc->device->host;
> + struct bnx2i_sess *sess = NULL;
> + int rc = 0;
> +
> + shost = sc->device->host;
> + sess = iscsi_hostdata(shost->hostdata);
> + printk(KERN_INFO "bnx2i: attempting to reset host, #%d\n",
> + sess->host->host_no);
> +
> + BUG_ON(shost != sess->host);
> + rc = bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
> +
> + return rc;
> +}
> +
> +
> +
> +/**********************************************************************
> + * open-iscsi interface
> + **********************************************************************/
> +
> +
> +#define get_bnx2_device(_hba, _devc) do { \
> + if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5706) || \
> + (_hba->pci_did == PCI_DEVICE_ID_NX2_5706S)) { \
> + _devc = '6'; \
> + } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5708) || \
> + (_hba->pci_did == PCI_DEVICE_ID_NX2_5708S)) { \
> + _devc = '8'; \
> + } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5709) || \
> + (_hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) { \
> + _devc = '9'; \
> + } \
> + } while (0)
> +
> +/* from open-iscsi project */
> +/*
> + * iSCSI Session's hostdata organization:
> + *
> + * *------------------* <== hostdata_session(host->hostdata)
> + * | ptr to class sess|
> + * |------------------| <== iscsi_hostdata(host->hostdata)
> + * | iscsi_session |
> + * *------------------*
> + */
> +
> +#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \
> + _sz % sizeof(unsigned long))
> +
> +#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
> +
> +#define session_to_cls(_sess) hostdata_session(_sess->host->hostdata)
> +
> +
> +
> +
> +/*
> + * Function: bnx2i_register_xport
> + * Description: this routine will allocate memory for SCSI host template,
> + * iSCSI template and registers one instance of NX2 device with
> + * iSCSI Transport Kernel module.
> + */
> +int bnx2i_register_xport(struct bnx2i_hba *hba)
> +{
> + void *mem_ptr = NULL;
> + char dev_id = '8';
> +
> + if (!hba)
> + return -EINVAL;
> +
> + get_bnx2_device(hba, dev_id);
> +
> + mem_ptr = kmalloc(sizeof(struct scsi_host_template), GFP_KERNEL);
> + hba->scsi_template = (struct scsi_host_template *) mem_ptr;
> + if (hba->scsi_template == NULL) {
> + printk(KERN_ALERT "bnx2i: failed to alloc memory for sht\n");
> + return -ENOMEM;
> + }
> +
> + mem_ptr = kmalloc(sizeof(struct iscsi_transport), GFP_KERNEL);
> + hba->iscsi_transport = (struct iscsi_transport *) mem_ptr;
> + if (hba->iscsi_transport == NULL) {
> + printk(KERN_ALERT "mem error for iscsi_transport template\n");
> + goto iscsi_xport_err;
> + }
> +
> + mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ALERT "failed to alloc memory for xport name\n");
> + goto scsi_name_mem_err;
> + }
> +
> + memcpy((void *) hba->scsi_template,
> + (const void *) &bnx2i_host_template,
> + sizeof(struct scsi_host_template));
> + hba->scsi_template->name = mem_ptr;
> + memcpy((void *) hba->scsi_template->name,
> + (const void *) bnx2i_host_template.name,
> + strlen(bnx2i_host_template.name) + 1);
> +
> + mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ALERT "failed to alloc proc name mem\n");
> + goto scsi_proc_name_mem_err;
> + }
> + hba->scsi_template->proc_name = mem_ptr;
> +
> + memcpy((void *) hba->iscsi_transport,
> + (const void *) &bnx2i_iscsi_transport,
> + sizeof(struct iscsi_transport));
> +
> + hba->iscsi_transport->host_template = hba->scsi_template;
> +
> + mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ALERT "mem alloc error, iscsi xport name\n");
> + goto xport_name_mem_err;
> + }
> + hba->iscsi_transport->name = mem_ptr;
> + sprintf(mem_ptr, "%s%c-%.2x%.2x%.2x", BRCM_ISCSI_XPORT_NAME_PREFIX,
> + dev_id, (u8)hba->pci_dev->bus->number,
> + hba->pci_devno, (u8)hba->pci_func);
> +
> + memcpy((void *)hba->scsi_template->proc_name,
> + (const void *)mem_ptr, strlen(mem_ptr) + 1);
> +
> + hba->shost_template = iscsi_register_transport(hba->iscsi_transport);
> + if (!hba->shost_template) {
> + printk(KERN_ALERT "bnx2i: xport reg failed, hba 0x%p\n", hba);
> + goto failed_registration;
> + }
> + printk(KERN_ALERT "bnx2i: netif=%s, iscsi=%s\n",
> + hba->netdev->name, hba->scsi_template->proc_name);
> + return 0;
> +
> +failed_registration:
> + kfree(hba->iscsi_transport->name);
> +xport_name_mem_err:
> + kfree(hba->scsi_template->proc_name);
> +scsi_proc_name_mem_err:
> + kfree(hba->scsi_template->name);
> +scsi_name_mem_err:
> + kfree(hba->iscsi_transport);
> +iscsi_xport_err:
> + kfree(hba->scsi_template);
> + printk(KERN_ALERT "register iscsi xport failed, hba 0x%p\n", hba);
> + return -ENOMEM;
> +}
> +
> +
> +/*
> + * Function: bnx2i_deregister_xport
> + * Description: this routine will de-allocate memory for SCSI host template,
> + * iSCSI template and de-registers a NX2 device instance
> + */
> +int bnx2i_deregister_xport(struct bnx2i_hba *hba)
> +{
> + if (!hba)
> + return -EINVAL;
> +
> + iscsi_unregister_transport(hba->iscsi_transport);
> + hba->shost_template = NULL;
> +
> + if (hba->scsi_template->name) {
> + kfree(hba->scsi_template->name);
> + hba->scsi_template->name = NULL;
> + }
> + if (hba->scsi_template) {
> + kfree(hba->scsi_template);
> + hba->scsi_template = NULL;
> + }
> + if (hba->iscsi_transport->name) {
> + kfree(hba->iscsi_transport->name);
> + hba->iscsi_transport->name = NULL;
> + }
> + if (hba->iscsi_transport) {
> + kfree(hba->iscsi_transport);
> + hba->iscsi_transport = NULL;
> + }
> + return 0;
> +}
> +
> +
> +/*
> + * Function: bnx2i_session_create
> + * Description: Creates a new iSCSI session instance on given device.
> + */
> +struct iscsi_cls_session *
> + bnx2i_session_create(struct iscsi_transport *it,
> + struct scsi_transport_template *scsit,
> + uint16_t cmds_max, uint16_t qdepth,
> + uint32_t initial_cmdsn, uint32_t *host_no)
> +{
> + struct bnx2i_hba *hba = NULL;
> + struct bnx2i_sess *sess = NULL;
> + struct Scsi_Host *shost;
> + struct iscsi_cls_session *cls_session;
> + int ret_code = 0;
> +
> + hba = bnx2i_get_hba_from_template(scsit);
> + if (bnx2i_adapter_ready(hba))
> + return NULL;
> +
> + shost = scsi_host_alloc(hba->iscsi_transport->host_template,
> + hostdata_privsize(sizeof(struct bnx2i_sess)));
> + if (!shost)
> + return NULL;
> +
> + shost->max_id = 1;
> + shost->max_channel = 1;
> + shost->max_lun = hba->iscsi_transport->max_lun;
> + shost->max_cmd_len = hba->iscsi_transport->max_cmd_len;
> + if (cmds_max)
> + shost->can_queue = cmds_max;
> + if (qdepth)
> + shost->cmd_per_lun = qdepth;
> + shost->transportt = scsit;
> + *host_no = shost->host_no;
> + sess = iscsi_hostdata(shost->hostdata);
> +
> + if (!sess)
> + goto sess_resc_fail;
> +
> + memset(sess, 0, sizeof(struct bnx2i_sess));
> + sess->hba = hba;
> + sess->host = shost;
> +
> + /*
> + * For Open-iSCSI, only normal sessions go through bnx2i.
> + * Discovery session goes through host stack TCP/IP stack.
> + */
> + ret_code = bnx2i_iscsi_sess_new(hba, sess);
> + if (ret_code) {
> + /*
> + * failed to allocate memory
> + */
> + printk(KERN_ALERT "bnx2i_sess_create: unable to alloc sess\n");
> + goto sess_resc_fail;
> + }
> +
> + /*
> + * Update CmdSN related parameters
> + */
> + sess->cmdsn = initial_cmdsn;
> + sess->exp_cmdsn = initial_cmdsn + 1;
> + sess->max_cmdsn = initial_cmdsn + 1;
> +
> + if (scsi_add_host(shost, NULL))
> + goto add_sh_fail;
> +
> + if (!try_module_get(it->owner))
> + goto cls_sess_falied;
> +
> + cls_session = iscsi_create_session(shost, it, 0);
> + if (!cls_session)
> + goto module_put;
> + *(unsigned long *)shost->hostdata = (unsigned long)cls_session;
> +
> + return hostdata_session(shost->hostdata);
> +
> +module_put:
> + module_put(it->owner);
> +cls_sess_falied:
> + scsi_remove_host(shost);
> +add_sh_fail:
> + bnx2i_iscsi_sess_release(hba, sess);
> +sess_resc_fail:
> + scsi_host_put(shost);
> + return NULL;
> +}
> +
> +
> +/*
> + * Function: bnx2i_session_destroy
> + * Description: Destroys previously created iSCSI session instance.
> + */
> +void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> + struct module *owner = cls_session->transport->owner;
> +
> + if (sess) {
> + bnx2i_iscsi_sess_release(sess->hba, sess);
> + }
> +
> + if (sess->target_name) {
> + kfree(sess->target_name);
> + sess->target_name = NULL;
> + }
> +
> + scsi_remove_host(shost);
> + iscsi_destroy_session(cls_session);
> + scsi_host_put(shost);
> + module_put(owner);
> +}
> +
> +
> +/*
> + * Function: bnx2i_sess_recovery_timeo
> + * Description: session recovery timeout handling routine
> + */
> +void bnx2i_sess_recovery_timeo(struct iscsi_cls_session *cls_session)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> +
> + sess->recovery_state |= ISCSI_SESS_RECOVERY_FAILED;
> + if (sess->state != BNX2I_SESS_IN_FFP) {
> + }
> + wake_up(&sess->er_wait);
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_create
> + * Description: Creates a new iSCSI connection instance for a given session
> + */
> +struct iscsi_cls_conn *bnx2i_conn_create(struct iscsi_cls_session *cls_session,
> + uint32_t cid)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> + struct bnx2i_conn *conn;
> + struct iscsi_cls_conn *cls_conn;
> +
> + cls_conn = iscsi_create_conn(cls_session, cid);
> + if (!cls_conn)
> + return NULL;
> +
> + conn = cls_conn->dd_data;
> + memset(conn, 0, sizeof(struct bnx2i_conn));
> + conn->cls_conn = cls_conn;
> + conn->exp_statsn = STATSN_UPDATE_SIGNATURE;
> + conn->iscsi_conn_cid = conn->fw_cid = 0;
> + conn->header_digest_en = 0;
> + conn->data_digest_en = 0;
> + conn->persist_address = NULL;
> + conn->state = CONN_STATE_IDLE;
> + /*
> + * Initialize the connection structure
> + */
> + bnx2i_iscsi_conn_new(sess, conn);
> + conn->conn_cid = cid;
> + return cls_conn;
> +}
> +
> +
> +
> +/*
> + * Function: bnx2i_conn_bind
> + * Description: Binds together iSCSI session instance, iSCSI connection
> + * instance and the TCP connection. If TCP connection does not belong
> + * on the device iSCSI sess/conn is bound, return failure to user.
> + */
> +int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
> + struct iscsi_cls_conn *cls_conn,
> + uint64_t transport_fd, int is_leading)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> + struct bnx2i_conn *tmp = ERR_PTR(-EEXIST);
> + struct bnx2i_conn *conn = cls_conn->dd_data;
> + int ret_code = 0;
> + struct bnx2i_endpoint *ep;
> +
> + ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
> +
> + if (ep->state == EP_STATE_PEER_DISCONN) {
> + /* Peer disconnect via' FIN or RST */
> + return -EINVAL;
> + }
> +
> + if (ep->hba != sess->hba) {
> + /* Error - TCP connection does not belong to this device
> + */
> + printk(KERN_ALERT "bnx2i: conn bind, ep=0x%p (0x%p) does not",
> + ep, ep->hba);
> + printk(KERN_ALERT "belong to hba 0x%p\n", sess->hba);
> + return -EEXIST;
> + }
> + if (!conn->gen_pdu.cmd)
> + conn->gen_pdu.cmd = bnx2i_alloc_cmd(sess);
> +
> + /* look-up for existing connection, MC/S is not currently supported */
> + spin_lock_bh(&sess->lock);
> + tmp = NULL;
> + if (!list_empty(&sess->conn_list)) {
> + list_for_each_entry(tmp, &sess->conn_list, link) {
> + if (tmp == conn) {
> + break;
> + }
> + }
> + }
> + if ((tmp != conn) && (conn->sess == sess)) {
> + /* bind iSCSI connection to this session */
> + list_add(&conn->link, &sess->conn_list);
> + if (is_leading) {
> + sess->lead_conn = conn;
> + }
> + }
> +
> + conn->ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
> + conn->ep->conn = conn;
> + conn->ep->sess = sess;
> + conn->state = CONN_STATE_XPORT_READY;
> + conn->iscsi_conn_cid = conn->ep->ep_iscsi_cid;
> + conn->fw_cid = conn->ep->ep_cid;
> +
> + bnx2i_bind_conn_to_iscsi_cid(conn, ep->ep_iscsi_cid);
> +
> + spin_unlock_bh(&sess->lock);
> + return ret_code;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_destroy
> + * Description: Destroys a iSCSI connection instance.
> + */
> +void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
> +{
> + struct bnx2i_conn *conn = cls_conn->dd_data;
> +
> + bnx2i_conn_free_login_resources(conn->sess->hba, conn);
> +
> + if (conn->persist_address) {
> + kfree(conn->persist_address);
> + conn->persist_address = NULL;
> + }
> + iscsi_destroy_conn(cls_conn);
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_set_param
> + * Description: During FFP migration, user daemon will issue this call to
> + * update negotiated iSCSI parameters to driver.
> + */
> +int bnx2i_conn_set_param(struct iscsi_cls_conn *cls_conn,
> + enum iscsi_param param, char *buf, int buflen)
> +{
> + struct bnx2i_conn *conn = cls_conn->dd_data;
> + struct bnx2i_sess *sess = conn->sess;
> +
> + spin_lock_bh(&sess->lock);
> + if (conn->state != CONN_STATE_IN_LOGIN) {
> + printk(KERN_ERR "bnx2i: can't change param [%d]\n", param);
> + spin_unlock_bh(&sess->lock);
> + return 0;
> + }
> + spin_unlock_bh(&sess->lock);
> + switch (param) {
> + case ISCSI_PARAM_MAX_RECV_DLENGTH:
> + sscanf(buf, "%d", &conn->max_data_seg_len_recv);
> + break;
> + case ISCSI_PARAM_MAX_XMIT_DLENGTH:
> + sscanf(buf, "%d", &conn->max_data_seg_len_xmit);
> + break;
> + case ISCSI_PARAM_HDRDGST_EN:
> + sscanf(buf, "%d", &conn->header_digest_en);
> + break;
> + case ISCSI_PARAM_DATADGST_EN:
> + sscanf(buf, "%d", &conn->data_digest_en);
> + break;
> + case ISCSI_PARAM_INITIAL_R2T_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->initial_r2t);
> + }
> + break;
> + case ISCSI_PARAM_MAX_R2T:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->max_r2t);
> + }
> + break;
> + case ISCSI_PARAM_IMM_DATA_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->imm_data);
> + }
> + break;
> + case ISCSI_PARAM_FIRST_BURST:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->first_burst_len);
> + }
> + break;
> + case ISCSI_PARAM_MAX_BURST:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->max_burst_len);
> + }
> + break;
> + case ISCSI_PARAM_PDU_INORDER_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->pdu_inorder);
> + }
> + break;
> + case ISCSI_PARAM_DATASEQ_INORDER_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->dataseq_inorder);
> + }
> + break;
> + case ISCSI_PARAM_ERL:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->erl);
> + }
> + break;
> + case ISCSI_PARAM_IFMARKER_EN:
> + sscanf(buf, "%d", &conn->ifmarker_enable);
> + BUG_ON(conn->ifmarker_enable);
> + break;
> + case ISCSI_PARAM_OFMARKER_EN:
> + sscanf(buf, "%d", &conn->ofmarker_enable);
> + BUG_ON(conn->ofmarker_enable);
> + break;
> + case ISCSI_PARAM_EXP_STATSN:
> + sscanf(buf, "%u", &conn->exp_statsn);
> + break;
> + case ISCSI_PARAM_TARGET_NAME:
> + if (sess->target_name)
> + break;
> + sess->target_name = kstrdup(buf, GFP_KERNEL);
> + if (!sess->target_name)
> + return -ENOMEM;
> + break;
> + case ISCSI_PARAM_TPGT:
> + sscanf(buf, "%d", &sess->tgt_prtl_grp);
> + break;
> + case ISCSI_PARAM_PERSISTENT_PORT:
> + {
> + sscanf(buf, "%d", &conn->persist_port);
> + }
> + break;
> + case ISCSI_PARAM_PERSISTENT_ADDRESS:
> + if (conn->persist_address)
> + break;
> + conn->persist_address = kstrdup(buf, GFP_KERNEL);
> + if (!conn->persist_address)
> + return -ENOMEM;
> + break;
> + default:
> + printk(KERN_ALERT "PARAM_UNKNOWN: 0x%x\n", param);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_get_param
> + * Description: Call to retrieve iSCSI connection parameters
> + */
> +int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
> + enum iscsi_param param, char *buf)
> +{
> + struct bnx2i_conn *conn;
> + int len = 0;
> +
> + if (!cls_conn)
> + return -EINVAL;
> + conn = (struct bnx2i_conn *)cls_conn->dd_data;
> + if (!conn || !conn->ep ||
> + (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL))
> + return -EINVAL;
> +
> + switch (param) {
> + case ISCSI_PARAM_MAX_RECV_DLENGTH:
> + len = sprintf(buf, "%u\n", conn->max_data_seg_len_recv);
> + break;
> + case ISCSI_PARAM_MAX_XMIT_DLENGTH:
> + len = sprintf(buf, "%u\n", conn->max_data_seg_len_xmit);
> + break;
> + case ISCSI_PARAM_HDRDGST_EN:
> + len = sprintf(buf, "%d\n", conn->header_digest_en);
> + break;
> + case ISCSI_PARAM_DATADGST_EN:
> + len = sprintf(buf, "%d\n", conn->data_digest_en);
> + break;
> + case ISCSI_PARAM_IFMARKER_EN:
> + len = sprintf(buf, "%u\n", conn->ifmarker_enable);
> + break;
> + case ISCSI_PARAM_OFMARKER_EN:
> + len = sprintf(buf, "%u\n", conn->ofmarker_enable);
> + break;
> + case ISCSI_PARAM_EXP_STATSN:
> + len = sprintf(buf, "%u\n", conn->exp_statsn);
> + break;
> + case ISCSI_PARAM_PERSISTENT_PORT:
> + len = sprintf(buf, "%d\n", conn->persist_port);
> + break;
> + case ISCSI_PARAM_PERSISTENT_ADDRESS:
> + if (conn->persist_address) {
> + len = sprintf(buf, "%s\n", conn->persist_address);
> + }
> + break;
> + case ISCSI_PARAM_CONN_PORT:
> + len = sprintf(buf, "%hu\n", conn->ep->cm_sk->dst_port);
> + break;
> + case ISCSI_PARAM_CONN_ADDRESS:
> + len = sprintf(buf, NIPQUAD_FMT "\n",
> + NIPQUAD(conn->ep->cm_sk->dst_ip));
> + break;
> + default:
> + printk(KERN_ALERT "get_param: conn 0x%p param %d not found\n",
> + conn, (u32)param);
> + return -ENOSYS;
> + }
> +
> + return len;
> +}
> +
> +
> +/*
> + * Function: bnx2i_session_get_param
> + * Description: Call to obtain iSCSI session parameters
> + */
> +int bnx2i_session_get_param(struct iscsi_cls_session *cls_session,
> + enum iscsi_param param, char *buf)
> +{
> + struct Scsi_Host *shost = NULL;
> + struct bnx2i_sess *sess = NULL;
> + int len = 0;
> +
> + if (!cls_session)
> + return -EINVAL;
> +
> + shost = iscsi_session_to_shost(cls_session);
> + sess = iscsi_hostdata(shost->hostdata);
> + if (!sess || !sess->lead_conn)
> + return -EINVAL;
> +
> + switch (param) {
> + case ISCSI_PARAM_INITIAL_R2T_EN:
> + len = sprintf(buf, "%d\n", sess->initial_r2t);
> + break;
> + case ISCSI_PARAM_MAX_R2T:
> + len = sprintf(buf, "%hu\n", sess->max_r2t);
> + break;
> + case ISCSI_PARAM_IMM_DATA_EN:
> + len = sprintf(buf, "%d\n", sess->imm_data);
> + break;
> + case ISCSI_PARAM_FIRST_BURST:
> + len = sprintf(buf, "%u\n", sess->first_burst_len);
> + break;
> + case ISCSI_PARAM_MAX_BURST:
> + len = sprintf(buf, "%u\n", sess->max_burst_len);
> + break;
> + case ISCSI_PARAM_PDU_INORDER_EN:
> + len = sprintf(buf, "%d\n", sess->pdu_inorder);
> + break;
> + case ISCSI_PARAM_DATASEQ_INORDER_EN:
> + len = sprintf(buf, "%d\n", sess->dataseq_inorder);
> + break;
> + case ISCSI_PARAM_ERL:
> + len = sprintf(buf, "%d\n", sess->erl);
> + break;
> + case ISCSI_PARAM_TARGET_NAME:
> + if (sess->target_name) {
> + len = sprintf(buf, "%s\n", sess->target_name);
> + }
> + break;
> + case ISCSI_PARAM_TPGT:
> + len = sprintf(buf, "%d\n", sess->tgt_prtl_grp);
> + break;
> + default:
> + printk(KERN_ALERT "sess_get_param: sess 0x%p", sess);
> + printk(KERN_ALERT "param (0x%x) not found\n", (u32) param);
> + return -ENOSYS;
> + }
> +
> + return len;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_start
> + * Description: last call in FFP migration to handover iscsi conn to the driver
> + */
> +int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
> +{
> + struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
> + struct bnx2i_sess *sess = conn->sess;
> +
> + if (conn->state != CONN_STATE_IN_LOGIN) {
> + printk(KERN_ALERT "conn_start: conn 0x%p state 0x%x err!!\n",
> + conn, conn->state);
> + return -EINVAL;
> + }
> +
> + if (!sess->initial_r2t) {
> + if (sess->first_burst_len > sess->max_burst_len)
> + return -EINVAL;
> + } else if (conn->max_data_seg_len_xmit > sess->max_burst_len) {
> + if (sess->first_burst_len > sess->max_burst_len)
> + return -EINVAL;
> + /* don't bother if only immediate data is supported and
> + * FBL & MBL are greater than MRDSL. In that case initiator
> + * will always send MRDSL worth of immediate data
> + */
> + }
> +
> + conn->state = CONN_STATE_FFP_STATE;
> + if (conn->sess->lead_conn == conn) {
> + conn->sess->state = BNX2I_SESS_IN_FFP;
> + }
> +
> + conn->ep->state = EP_STATE_ULP_UPDATE_START;
> + bnx2i_update_iscsi_conn(conn);
> +
> + conn->ep->ofld_timer.expires = 10*HZ + jiffies;
> + conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer;
> + conn->ep->ofld_timer.data = (unsigned long)conn->ep;
> + add_timer(&conn->ep->ofld_timer);
> + /* update iSCSI context for this conn, wait for CNIC to complete */
> + wait_event_interruptible(conn->ep->ofld_wait,
> + conn->ep->state != EP_STATE_ULP_UPDATE_START);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&conn->ep->ofld_timer);
> + if (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL) {
> + /* should never happen */
> + }
> + /* Free login ITT, not required anymore */
> + if (conn->gen_pdu.cmd) {
> + bnx2i_free_cmd(conn->sess, conn->gen_pdu.cmd);
> + conn->gen_pdu.cmd = NULL;
> + }
> +
> + switch (conn->stop_state) {
> + case STOP_CONN_RECOVER:
> + sess->recovery_state = ISCSI_SESS_RECOVERY_COMPLETE;
> + conn->sess->state = BNX2I_SESS_IN_FFP;
> + iscsi_unblock_session(session_to_cls(sess));
> + wake_up(&sess->er_wait);
> + break;
> + case STOP_CONN_TERM:
> + break;
> + default:
> + ;
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_stop
> + * Description: call to take control of iscsi conn from the driver.
> + * Could be called when login failed, when recovery is to be
> + * attempted or during connection teardown
> + */
> +void bnx2i_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
> +{
> + struct bnx2i_conn *conn = (struct bnx2i_conn *)cls_conn->dd_data;
> +
> + conn->stop_state = flag;
> + iscsi_block_session(session_to_cls(conn->sess));
> +
> + switch (flag) {
> + case STOP_CONN_RECOVER:
> + conn->sess->state = BNX2I_SESS_IN_RECOVERY;
> + break;
> + case STOP_CONN_TERM:
> + if (conn->sess && (conn->sess->state & BNX2I_SESS_IN_FFP)) {
> + conn->sess->state = BNX2I_SESS_IN_SHUTDOWN;
> + }
> + break;
> + default:
> + printk(KERN_ERR "bnx2i: invalid conn stop req %d\n", flag);
> + }
> +
> + return;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_send_pdu
> + * Description: To send iSCSI PDUs prepared by user daemon, only login, logout,
> + * nop-out pdu's should flow this path.
> + */
> +int bnx2i_conn_send_pdu(struct iscsi_cls_conn *cls_conn,
> + struct iscsi_hdr *hdr, char *data,
> + uint32_t data_size)
> +{
> + struct bnx2i_conn *conn = NULL;
> + struct iscsi_hdr *iscsi_hdr = (struct iscsi_hdr *) hdr;
> + struct bnx2i_cmd *cmnd = NULL;
> + uint32_t payload_size = 0;
> + int rc;
> + unsigned long flags;
> +
> + if (!cls_conn) {
> + printk(KERN_ALERT "bnx2i_conn_send_pdu: NULL conn ptr. \n");
> + return -EIO;
> + }
> + conn = (struct bnx2i_conn *)cls_conn->dd_data;
> + if (!conn->gen_pdu.req_buf) {
> + printk(KERN_ALERT "send_pdu: login buf not allocated\n");
> + /* ERR - buffer not allocated, should not happen */
> + return -EIO;
> + }
> +
> + if (conn->gen_pdu.cmd) {
> + if ((conn->state != CONN_STATE_XPORT_READY) &&
> + (conn->state != CONN_STATE_IN_LOGIN)) {
> + printk(KERN_ALERT "send_pdu: %d != XPORT_READY\n",
> + conn->state);
> + return -EPERM;
> + }
> + cmnd = conn->gen_pdu.cmd;
> + } else { /* could be NOPOUT or the LOGOUT request */
> + spin_lock_irqsave(conn->sess->host->host_lock, flags);
> + cmnd = bnx2i_alloc_cmd(conn->sess);
> + spin_unlock_irqrestore(conn->sess->host->host_lock, flags);
> +
> + if (!cmnd) {
> + printk(KERN_ALERT "bnx2i: Error - cmd not allocated\n");
> + return -EIO;
> + }
> + }
> + memset(conn->gen_pdu.req_buf, 0, ISCSI_CONN_LOGIN_BUF_SIZE);
> + /* Login request, copy hdr & data to buffer in conn struct */
> + memcpy((void *) &conn->gen_pdu.pdu_hdr, (const void *) hdr,
> + sizeof(struct iscsi_hdr));
> +
> + cmnd->iscsi_opcode = iscsi_hdr->opcode;
> + switch (iscsi_hdr->opcode & ISCSI_OPCODE_MASK) {
> + case ISCSI_OP_LOGIN:
> + if (conn->state == CONN_STATE_XPORT_READY)
> + conn->state = CONN_STATE_IN_LOGIN;
> + break;
> + case ISCSI_OP_LOGOUT:
> + conn->state = CONN_STATE_IN_LOGOUT;
> + conn->sess->state = BNX2I_SESS_IN_LOGOUT;
> + break;
> + case ISCSI_OP_NOOP_OUT:
> + break;
> + default:
> + ;
> + }
> +
> + conn->gen_pdu.req_buf_size = data_size;
> + payload_size = (hdr->dlength[0] << 16) | (hdr->dlength[1] << 8) |
> + hdr->dlength[2];
> +
> + if (data_size) {
> + memcpy((void *)conn->gen_pdu.req_buf, (const void *)data,
> + data_size);
> + conn->gen_pdu.req_wr_ptr =
> + conn->gen_pdu.req_buf + payload_size;
> + }
> + cmnd->conn = conn;
> + cmnd->scsi_cmd = NULL;
> + rc = bnx2i_iscsi_send_generic_request(cmnd);
> + return rc;
> +}
> +
> +
> +/*
> + * Function : bnx2i_conn_get_stats
> + * Description: Returns iSCSI stats
> + */
> +void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
> + struct iscsi_stats *stats)
> +{
> + struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
> +
> + stats->txdata_octets = conn->total_data_octets_sent;
> + stats->rxdata_octets = conn->total_data_octets_rcvd;
> +
> + stats->noptx_pdus = conn->num_nopin_pdus;
> + stats->scsicmd_pdus = conn->num_scsi_cmd_pdus;
> + stats->tmfcmd_pdus = conn->num_tmf_req_pdus;
> + stats->login_pdus = conn->num_login_req_pdus;
> + stats->text_pdus = 0;
> + stats->dataout_pdus = conn->num_dataout_pdus;
> + stats->logout_pdus = conn->num_logout_req_pdus;
> + stats->snack_pdus = 0;
> +
> + stats->noprx_pdus = conn->num_nopout_pdus;
> + stats->scsirsp_pdus = conn->num_scsi_resp_pdus;
> + stats->tmfrsp_pdus = conn->num_tmf_resp_pdus;
> + stats->textrsp_pdus = 0;
> + stats->datain_pdus = conn->num_datain_pdus;
> + stats->logoutrsp_pdus = conn->num_logout_resp_pdus;
> + stats->r2t_pdus = conn->num_r2t_pdus;
> + stats->async_pdus = conn->num_async_pdus;
> + stats->rjt_pdus = conn->num_reject_pdus;
> +
> + stats->digest_err = 0;
> + stats->timeout_err = 0;
> + stats->custom_length = 0;
> +}
> +
> +
> +
> +/*
> + * Function : bnx2i_check_nx2_dev_busy
> + * Description: this routine unregister devices if there are no active conns
> + */
> +static void bnx2i_check_nx2_dev_busy(void)
> +{
> + if (bnx2i_num_free_ep == bnx2i_max_free_ep) {
> + bnx2i_unreg_dev_all();
> + msleep(2);
> + }
> +}
> +
> +
> +/*
> + * Function : bnx2i_ep_connect
> + * Description: this routine initiates the TCP/IP connection by invoking
> + * Option-2 interface with l5_core and the CNIC
> + */
> +int bnx2i_ep_connect(struct sockaddr *dst_addr, int non_blocking,
> + uint64_t *ep_handle)
> +{
> + u32 iscsi_cid = BNX2I_CID_RESERVED;
> + struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
> + struct bnx2i_endpoint *endpoint;
> + struct bnx2i_hba *hba = NULL;
> + struct cnic_dev *cnic = NULL;
> + struct cnic_dev *tmp_cnic = NULL;
> + struct bnx2i_hba *tmp_hba = NULL;
> + struct cnic_sockaddr saddr;
> + int rc = 0;
> + extern int bnx2i_reg_device;
> + extern struct bnx2i_hba *get_adapter_list_head(void);
> +
> + /*
> + * check if the given destination can be reached through NX2 device
> + */
> +
> + if ((!bnx2i_reg_device) &&
> + (bnx2i_num_free_ep == bnx2i_max_free_ep)) {
> + bnx2i_reg_dev_all();
> + }
> + tmp_hba = get_adapter_list_head();
> + if (tmp_hba && tmp_hba->cnic) {
> + tmp_cnic = tmp_hba->cnic;
> + cnic = tmp_cnic->cm_select_dev(desti, CNIC_ULP_ISCSI);
> + }
> + if (!cnic) {
> + printk(KERN_ALERT "bnx2i: ep_conn, can't connect using cnic\n");
> + rc = 0;
> + goto check_busy;
> + }
> + hba = bnx2i_find_hba_for_cnic(cnic);
> +
> + if (bnx2i_adapter_ready(hba)) {
> + printk(KERN_ALERT "bnx2i: ep_conn, adapter not found\n");
> + rc = 0;
> + goto check_busy;
> + }
> + if (hba->netdev->mtu > hba->mtu_supported) {
> + printk(KERN_ALERT "bnx2i: %s network i/f mtu is set to %d\n",
> + hba->netdev->name, hba->netdev->mtu);
> + printk(KERN_ALERT "bnx2i: iSCSI HBA can support mtu of %d\n",
> + hba->mtu_supported);
> + rc = 0;
> + goto check_busy;
> + }
> + endpoint = bnx2i_alloc_ep();
> + if (!endpoint) {
> + printk(KERN_ALERT "bnx2i: ep_conn, unable to alloc ep\n");
> + *ep_handle = (uint64_t) 0;
> + rc = -ENOMEM;
> + goto check_busy;
> + }
> +
> + endpoint->ep_iscsi_cid = (u16)ISCSI_RESERVED_TAG;
> + iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
> + if (iscsi_cid == (u16) ISCSI_RESERVED_TAG) {
> + printk(KERN_ALERT "alloc_ep: unable to allocate iscsi cid\n");
> + rc = -ENOMEM;
> + goto iscsi_cid_err;
> + }
> + endpoint->hba = hba;
> + endpoint->hba_age = hba->age;
> +
> + rc = bnx2i_alloc_qp_resc(hba, endpoint);
> + if (rc != 0) {
> + printk(KERN_ALERT "bnx2i: ep_conn, alloc QP resc error\n");
> + rc = -ENOMEM;
> + goto qp_resc_err;
> + }
> +
> + endpoint->ep_iscsi_cid = iscsi_cid;
> + endpoint->state = EP_STATE_OFLD_START;
> + bnx2i_ep_ofld_list_add(hba, endpoint);
> +
> + bnx2i_send_conn_ofld_req(hba, endpoint);
> +
> + init_timer(&endpoint->ofld_timer);
> + endpoint->ofld_timer.expires = 2 * HZ + jiffies;
> + endpoint->ofld_timer.function = bnx2i_ep_ofld_timer;
> + endpoint->ofld_timer.data = (unsigned long) endpoint;
> + add_timer(&endpoint->ofld_timer);
> + /* Wait for CNIC hardware to setup conn context and return 'cid' */
> + wait_event_interruptible(endpoint->ofld_wait,
> + endpoint->state != EP_STATE_OFLD_START);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&endpoint->ofld_timer);
> + list_del_init(&endpoint->link);
> +
> + if (endpoint->state != EP_STATE_OFLD_COMPL) {
> + rc = -ENOSPC;
> + goto conn_failed;
> + }
> +
> + rc = -EINVAL;
> + if (hba->reg_with_cnic)
> + rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, endpoint->ep_cid,
> + iscsi_cid, &endpoint->cm_sk, endpoint);
> + if (rc)
> + goto conn_failed;
> +
> + memset(&saddr, 0, sizeof(saddr));
> + saddr.remote_addr.sin_addr.s_addr = desti->sin_addr.s_addr;
> + saddr.remote_addr.sin_port = desti->sin_port;
> + saddr.local_addr.sin_port = htons(endpoint->tcp_port);
> + endpoint->state = EP_STATE_CONNECT_START;
> + rc = -EINVAL;
> + if (hba->reg_with_cnic)
> + rc = cnic->cm_connect(endpoint->cm_sk, &saddr);
> + else
> + goto conn_failed;
> +
> + if (rc)
> + goto release_ep;
> +
> + bnx2i_map_ep_dbell_regs(endpoint);
> +
> + *ep_handle = (uint64_t) (unsigned long) endpoint;
> + return 0;
> +
> +release_ep:
> + cnic->cm_destroy(endpoint->cm_sk);
> +conn_failed:
> +iscsi_cid_err:
> + bnx2i_free_qp_resc(hba, endpoint);
> +qp_resc_err:
> + bnx2i_free_ep(endpoint);
> +check_busy:
> + *ep_handle = (uint64_t) 0;
> + bnx2i_check_nx2_dev_busy();
> + return rc;
> +}
> +
> +
> +
> +/*
> + * Function : bnx2i_ep_poll
> + * Description: polls for TCP connect request to complete
> + */
> +int bnx2i_ep_poll(uint64_t ep_handle, int timeout_ms)
> +{
> + struct bnx2i_endpoint *ep;
> + int rc = 0;
> + ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
> +
> + if (!ep) {
> + return -EINVAL;
> + }
> + if (ep->state == EP_STATE_IDLE) {
> + return -1;
> + }
> + if (ep->state == EP_STATE_CONNECT_COMPL) {
> + return 1;
> + }
> +
> + rc = wait_event_interruptible_timeout(ep->ofld_wait,
> + (ep->state ==
> + EP_STATE_CONNECT_COMPL),
> + msecs_to_jiffies(timeout_ms));
> + if (!rc || (ep->state == EP_STATE_OFLD_FAILED)) {
> + rc = -1;
> + }
> +
> + if (rc > 0) {
> + return 1;
> + } else if (!rc) {
> + return 0; /* timeout */
> + } else {
> + return rc;
> + }
> +}
> +
> +
> +/*
> + * Function : bnx2i_ep_disconnect
> + * Description: initiates TCP/IP connection teardown process
> + */
> +void bnx2i_ep_disconnect(uint64_t ep_handle)
> +{
> + struct bnx2i_endpoint *ep;
> + struct cnic_dev *cnic = NULL;
> + struct bnx2i_sess *sess = NULL;
> + int rc = 0;
> +
> + ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
> + if (!ep || (ep_handle == -1)) {
> + return;
> + }
> + if (ep->state == EP_STATE_IDLE) {
> + goto return_ep;
> + }
> + cnic = ep->hba->cnic;
> +
> + if (ep->state == EP_STATE_PEER_DISCONN) {
> + ep->state = EP_STATE_DISCONN_COMPL;
> + goto peer_discon;
> + }
> +
> + if (test_bit(ADAPTER_STATE_DOWN, &ep->hba->adapter_state)) {
> + goto free_resc;
> + }
> + if (ep->hba_age != ep->hba->age) {
> + goto dev_reset;
> + }
> +
> + ep->state = EP_STATE_DISCONN_START;
> +
> + init_timer(&ep->ofld_timer);
> + ep->ofld_timer.expires = 10*HZ + jiffies;
> + ep->ofld_timer.function = bnx2i_ep_ofld_timer;
> + ep->ofld_timer.data = (unsigned long) ep;
> + add_timer(&ep->ofld_timer);
> +
> + if (ep->hba->reg_with_cnic)
> + cnic->cm_close(ep->cm_sk);
> + else
> + goto free_resc;
> +
> + /* wait for option-2 conn teardown */
> + wait_event_interruptible(ep->ofld_wait,
> + ep->state != EP_STATE_DISCONN_START);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&ep->ofld_timer);
> +
> +peer_discon:
> + if (!ep->hba->reg_with_cnic)
> + goto free_resc;
> +
> + rc = cnic->cm_destroy(ep->cm_sk);
> + ep->state = EP_STATE_CLEANUP_START;
> + init_timer(&ep->ofld_timer);
> + ep->ofld_timer.expires = 10*HZ + jiffies;
> + ep->ofld_timer.function = bnx2i_ep_ofld_timer;
> + ep->ofld_timer.data = (unsigned long) ep;
> + add_timer(&ep->ofld_timer);
> +
> + bnx2i_ep_destroy_list_add(ep->hba, ep);
> +
> + /* destroy iSCSI context, wait for it to complete */
> + bnx2i_send_conn_destroy(ep->hba, ep);
> + wait_event_interruptible(ep->ofld_wait,
> + (ep->state != EP_STATE_CLEANUP_START));
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&ep->ofld_timer);
> + if (ep->state != EP_STATE_CLEANUP_CMPL) {
> + /* should never happen */
> + printk(KERN_ALERT "bnx2i - conn destroy failed\n");
> + }
> +
> +dev_reset:
> + bnx2i_flush_active_cmd_queue(ep->sess, DID_NO_CONNECT);
> +
> +free_resc:
> + bnx2i_free_qp_resc(ep->hba, ep);
> +return_ep:
> + if (ep->conn && ep->conn->sess)
> + /* session recovery in progress */
> + sess = ep->conn->sess;
> + if (sess && (sess->state != BNX2I_SESS_IN_RECOVERY))
> + /* session logged out */
> + sess = NULL;
> +
> + bnx2i_free_ep(ep);
> + if (sess) {
> + wake_up(&sess->er_wait);
> + }
> +
> + bnx2i_check_nx2_dev_busy();
> + return;
> +}
> +
> +
> +
> +
> +/*
> + * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template
> + * used while registering with the iSCSI trnaport module.
> + */
> +struct scsi_host_template bnx2i_host_template = {
> + .module = THIS_MODULE,
> + .name = "Broadcom Offload iSCSI Initiator",
> + .queuecommand = bnx2i_queuecommand,
> + .eh_abort_handler = bnx2i_abort,
> + .eh_host_reset_handler = bnx2i_host_reset,
> + .bios_param = NULL,
> + .can_queue = 128,
> + .max_sectors = 256,
> + .this_id = -1,
> + .cmd_per_lun = 64,
> + .use_clustering = ENABLE_CLUSTERING,
> + .sg_tablesize = ISCSI_MAX_BDS_PER_CMD,
> + .proc_name = NULL
> + };
> +
> +
> +
> +struct iscsi_transport bnx2i_iscsi_transport = {
> + .owner = THIS_MODULE,
> + .name = "bnx2i",
> + .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_MULTI_R2T
> + | CAP_DATADGST,
> + .param_mask = ISCSI_MAX_RECV_DLENGTH |
> + ISCSI_MAX_XMIT_DLENGTH |
> + ISCSI_HDRDGST_EN |
> + ISCSI_DATADGST_EN |
> + ISCSI_INITIAL_R2T_EN |
> + ISCSI_MAX_R2T |
> + ISCSI_IMM_DATA_EN |
> + ISCSI_FIRST_BURST |
> + ISCSI_MAX_BURST |
> + ISCSI_PDU_INORDER_EN |
> + ISCSI_DATASEQ_INORDER_EN |
> + ISCSI_ERL |
> + ISCSI_CONN_PORT |
> + ISCSI_CONN_ADDRESS |
> + ISCSI_EXP_STATSN |
> + ISCSI_PERSISTENT_PORT |
> + ISCSI_PERSISTENT_ADDRESS |
> + ISCSI_TARGET_NAME |
> + ISCSI_TPGT,
> + .host_param_mask = 0,
> + .host_template = &bnx2i_host_template,
> + .sessiondata_size = sizeof(struct bnx2i_sess),
> + .conndata_size = sizeof(struct bnx2i_conn),
> + .max_conn = 1,
> + .max_cmd_len = 16,
> + .max_lun = 512,
> + .create_session = bnx2i_session_create,
> + .destroy_session = bnx2i_session_destroy,
> + .create_conn = bnx2i_conn_create,
> + .bind_conn = bnx2i_conn_bind,
> + .destroy_conn = bnx2i_conn_destroy,
> + .set_param = bnx2i_conn_set_param,
> + .get_conn_param = bnx2i_conn_get_param,
> + .get_session_param = bnx2i_session_get_param,
> + .start_conn = bnx2i_conn_start,
> + .stop_conn = bnx2i_conn_stop,
> + .send_pdu = bnx2i_conn_send_pdu,
> + .get_stats = bnx2i_conn_get_stats,
> + /* iscsi host params */
> + .get_host_param = NULL,
> + .set_host_param = NULL,
> + /* TCP connect - disconnect - option-2 interface calls */
> + .ep_connect = bnx2i_ep_connect,
> + .ep_poll = bnx2i_ep_poll,
> + .ep_disconnect = bnx2i_ep_disconnect,
> + /* Error recovery timeout call */
> + .session_recovery_timedout = bnx2i_sess_recovery_timeo
> +};
> diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c
> new file mode 100644
> index 0000000..6bd6eba
> --- /dev/null
> +++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c
> @@ -0,0 +1,616 @@
> +/* bnx2i_sysfs.c: Broadcom NetXtreme II iSCSI driver.
> + *
> + * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.
> + *
> + * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
> + */
> +
> +#include "bnx2i.h"
> +
> +#define BNX2I_SYSFS_VERSION 0x2
> +
> +static ssize_t bnx2i_show_mips_status(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + ssize_t len = 0;
> +
> + bnx2i_read_mips_idle_counters(hba);
> +
> + len = sprintf(buf, "%d\n%lu\n%d\n%llu\n%llu\n"
> + "%llu\n%llu\n%llu\n%llu\n%llu\n%llu\n",
> + BNX2I_SYSFS_VERSION, jiffies, HZ,
> + hba->mips_idle.cp_idle_count,
> + hba->mips_idle.txp_idle_count,
> + hba->mips_idle.txp_tdma_count,
> + hba->mips_idle.txp_ctx_count,
> + hba->mips_idle.txp_hdrq_count,
> + hba->mips_idle.tpat_idle_count,
> + hba->mips_idle.rxp_idle_count,
> + hba->mips_idle.com_idle_count);
> + return (len);
> +}
> +
> +static ssize_t bnx2i_show_net_if_name(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "%s\n", hba->netdev->name);
> +}
> +
> +static ssize_t bnx2i_show_pci_bar(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%.8x\n",
> + (u32) pci_resource_start(hba->pci_dev, 0));
> +}
> +
> +static ssize_t bnx2i_show_sq_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->max_sqes);
> +}
> +
> +static ssize_t bnx2i_set_sq_info(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + u32 val;
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_SQ_WQES_MIN) &&
> + (val <= BNX2I_SQ_WQES_MAX)) {
> + hba->max_sqes = val;
> + }
> + }
> + return count;
> +}
> +
> +static ssize_t bnx2i_show_cq_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->max_cqes);
> +}
> +
> +static ssize_t bnx2i_set_cq_info(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 val;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_CQ_WQES_MIN) &&
> + (val <= BNX2I_CQ_WQES_MAX)) {
> + hba->max_cqes = val;
> + }
> + }
> + return count;
> +}
> +
> +static ssize_t bnx2i_show_rq_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->max_rqes);
> +}
> +
> +static ssize_t bnx2i_set_rq_info(struct class_device *cdev, const char *buf,
> + size_t count)
> +{
> + u32 val;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_RQ_WQES_MIN) &&
> + (val <= BNX2I_RQ_WQES_MAX)) {
> + hba->max_rqes = val;
> + }
> + }
> + return count;
> +}
> +
> +
> +static ssize_t bnx2i_show_ccell_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->num_ccell);
> +}
> +
> +static ssize_t bnx2i_set_ccell_info(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 val;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_CCELLS_MIN) &&
> + (val <= BNX2I_CCELLS_MAX)) {
> + hba->num_ccell = val;
> + }
> + }
> + return count;
> +}
> +
> +
> +static ssize_t bnx2i_read_pci_trigger_reg(struct class_device *cdev,
> + char *buf)
> +{
> + u32 reg_val = 0;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +#define PCI_EVENT_TRIGGER_REG 0xCAC /* DMA WCHAN STAT10 REG */
> + reg_val = readl(hba->cnic->regview + PCI_EVENT_TRIGGER_REG);
> + return sprintf(buf, "0x%x\n", reg_val);
> +}
> +
> +
> +static ssize_t bnx2i_get_iscsi_cntx_dump(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + unsigned int *ptr = (unsigned int *) hba->ctx_addr;
> + unsigned int *dst_ptr = (unsigned int *) buf;
> + int unit_sz = sizeof(unsigned int);
> +#define SYSFS_BUF_SIZE 4096
> +#define NUM_SYSFS_BUFS_PER_CTX 4
> +
> + if (hba->ctx_read_cnt == NUM_SYSFS_BUFS_PER_CTX)
> + return 0;
> +
> + ptr += (((hba->ctx_read_cnt % NUM_SYSFS_BUFS_PER_CTX) *
> + SYSFS_BUF_SIZE) / unit_sz);
> + hba->ctx_read_cnt++;
> + memcpy(dst_ptr, ptr, SYSFS_BUF_SIZE);
> +
> + return SYSFS_BUF_SIZE;
> +}
> +
> +static ssize_t bnx2i_select_iscsi_cntx_dump(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 iscsi_cid;
> + int ret = 0;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &iscsi_cid) > 0) {
> + ret = bnx2i_select_ctx_dump_cid(hba, iscsi_cid);
> + }
> + if (!ret)
> + ret = count;
> + return ret;
> +}
> +
> +static ssize_t bnx2i_get_active_iscsi_cid_list(struct class_device *cdev,
> + char *buf)
> +{
> + u32 active_iscsi_cid[32];
> + u32 active_cid[32];
> + int num_cid = 0;
> + ssize_t total_len = 0;
> + char *cur_ptr = buf;
> + int i = 0;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + u32 num_ccell = hba->ctx_ccell_tasks & 0xFFFF;
> + u32 num_tasks_per_conn = hba->ctx_ccell_tasks >> 16;
> +
> + if (!hba->ictx_poll_mode) {
> + num_cid = bnx2i_list_iscsi_cid(hba, active_iscsi_cid,
> + active_cid);
> + }
> + total_len += sprintf(cur_ptr, "0x%x\n", BNX2I_SYSFS_VERSION);
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x\n", num_ccell);
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x\n", num_tasks_per_conn);
> + if (hba->ictx_poll_mode) {
> + if (hba->ictx_poll_cid) {
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
> + hba->ictx_poll_iscsi_cid,
> + hba->ictx_poll_cid);
> + hba->ictx_poll_cid = hba->ictx_poll_iscsi_cid = 0;
> + }
> + } else {
> + for (i = 0; i < num_cid; i++) {
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
> + active_iscsi_cid[i],
> + active_cid[i]);
> + }
> + }
> + return total_len;
> +}
> +
> +
> +static ssize_t bnx2i_set_iscsi_cid_err_poll_mode(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 poll_mode;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, "0x%x", &poll_mode) > 0) {
> + if (poll_mode)
> + hba->ictx_poll_mode = 1;
> + else
> + hba->ictx_poll_mode = 0;
> + }
> + return count;
> +}
> +
> +
> +static ssize_t bnx2i_get_qp_shmem_dump(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + int resi_len = hba->sq_cq_size -
> + (hba->sq_cq_rdp - hba->sq_cq_dump);
> +
> + if (!hba->sq_cq_dump || !hba->sq_cq_rdp) {
> + return -EINVAL;
> + } else if ((hba->sq_cq_dump + hba->sq_cq_size) ==
> + hba->sq_cq_rdp) {
> + kfree(hba->sq_cq_dump);
> + hba->sq_cq_dump = hba->sq_cq_rdp = NULL;
> + return 0;
> + }
> +
> + if (resi_len > SYSFS_BUF_SIZE) {
> + resi_len = SYSFS_BUF_SIZE;
> + }
> + memcpy(buf, hba->sq_cq_rdp, resi_len);
> + hba->sq_cq_rdp += resi_len;
> +
> + return resi_len;
> +}
> +
> +
> +
> +
> +static void bnx2i_dup_cq_mem(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn, int count)
> +{
> + struct cqe *cqe_s;
> + struct cqe *cqe_d;
> + int total_cnt = count;
> +
> + if (conn->ep->qp.cq_cons_qe == conn->ep->qp.cq_virt)
> + cqe_s = conn->ep->qp.cq_last_qe;
> + else
> + cqe_s = conn->ep->qp.cq_cons_qe - 1;
> + cqe_d = (struct cqe *)hba->sq_cq_rdp;
> + while (count--) {
> + memcpy(cqe_d, cqe_s, sizeof(struct cqe));
> + if (cqe_s == conn->ep->qp.cq_virt) {
> + cqe_s = conn->ep->qp.cq_last_qe;
> + } else {
> + cqe_s--;
> + }
> + cqe_d++;
> + if ((cqe_d - (struct cqe *)hba->sq_cq_rdp) > total_cnt) {
> + printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
> + break;
> + }
> + }
> +}
> +
> +
> +static int bnx2i_init_cq_dump(struct bnx2i_hba *hba, u32 iscsi_cid,
> + u32 count)
> +{
> + struct bnx2i_conn *conn = NULL;
> + int cq_size = 0;
> +
> + conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
> +
> + if (!conn) {
> + printk(KERN_ALERT "CQ dump: cid #%x not valid\n",
> + iscsi_cid);
> + return -EPERM;
> + }
> +
> + if (hba->sq_cq_dump)
> + return -EPERM;
> +
> + cq_size = (conn->ep->qp.cq_last_qe - conn->ep->qp.cq_virt) + 1;
> +
> + if (!count || (count > cq_size))
> + count = cq_size;
> +
> + hba->sq_cq_size = count * sizeof(struct cqe);
> +
> + if (!hba->sq_cq_size)
> + return -EINVAL;
> +
> + hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
> + if (!hba->sq_cq_dump)
> + return -ENOMEM;
> + hba->sq_cq_rdp = hba->sq_cq_dump;
> +
> + bnx2i_dup_cq_mem(hba, conn, count);
> + return 0;
> +}
> +
> +
> +
> +static void bnx2i_dup_sq_mem(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn, int count)
> +{
> + struct sqe *sqe_s;
> + struct sqe *sqe_d;
> + int total_cnt = count;
> +
> + if (conn->ep->qp.sq_prod_qe == conn->ep->qp.sq_virt)
> + sqe_s = conn->ep->qp.sq_last_qe;
> + else
> + sqe_s = conn->ep->qp.sq_prod_qe - 1;
> + sqe_d = (struct sqe *)hba->sq_cq_rdp;
> + while (count--) {
> + memcpy(sqe_d, sqe_s, sizeof(struct sqe));
> + if (sqe_s == conn->ep->qp.sq_virt) {
> + sqe_s = conn->ep->qp.sq_last_qe;
> + } else {
> + sqe_s--;
> + }
> + sqe_d++;
> + if ((sqe_d - (struct sqe *) hba->sq_cq_rdp) >
> + total_cnt) {
> + printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
> + break;
> + }
> + }
> +}
> +
> +
> +static int bnx2i_init_sq_dump(struct bnx2i_hba *hba,
> + u32 iscsi_cid, u32 count)
> +{
> + struct bnx2i_conn *conn = NULL;
> + int sq_size = 0;
> +
> + conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
> +
> + if (!conn) {
> + printk(KERN_ALERT "SQ dump: cid #%x not valid\n",
> + iscsi_cid);
> + return -EINVAL;
> + }
> +
> + if (hba->sq_cq_dump)
> + return -EINVAL;
> +
> + sq_size = (conn->ep->qp.sq_last_qe - conn->ep->qp.sq_virt) + 1;
> +
> + if (!count || (count > sq_size))
> + count = sq_size;
> +
> + hba->sq_cq_size = count * sizeof(struct sqe);
> +
> + if (!hba->sq_cq_size)
> + return -EINVAL;
> +
> + hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
> + if (!hba->sq_cq_dump)
> + return -ENOMEM;
> + hba->sq_cq_rdp = hba->sq_cq_dump;
> +
> + bnx2i_dup_sq_mem(hba, conn, count);
> + return 0;
> +}
> +
> +static ssize_t bnx2i_setup_qp_shmem_dump(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + u32 iscsi_cid;
> + char queue[32];
> + ssize_t ret = count;
> + u32 num_count;
> +
> +
> + if (sscanf(buf, "%c%c,%d,%d", &queue[0], &queue[1],
> + &iscsi_cid, &num_count) > 0) {
> + if (!strncmp(queue, "SQ", 2)) {
> + ret = bnx2i_init_sq_dump(hba, iscsi_cid, num_count);
> + } else if (!strncmp(queue, "CQ", 2)) {
> + ret = bnx2i_init_cq_dump(hba, iscsi_cid, num_count);
> + } else {
> + ret = -EINVAL;
> + }
> + }
> + return ret;
> +}
> +
> +
> +static ssize_t bnx2i_read_tcp_portd_options(struct class_device *cdev,
> + char *buf)
> +{
> + extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
> + return sprintf(buf, "0x%x\n", bnx2i_tcp_port_tbl.num_required);
> +}
> +
> +static ssize_t bnx2i_write_tcp_portd_results(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
> + u32 tcp_port, bind_stat;
> +
> + if (!bnx2i_tcp_port_tbl.free_q)
> + return count;
> +
> + if (sscanf(buf, "%d,%d", &tcp_port, &bind_stat) > 0) {
> + if (bind_stat && tcp_port) {
> + bnx2i_tcp_port_new_entry(tcp_port);
> + }
> + }
> + return count;
> +}
> +
> +
> +static CLASS_DEVICE_ATTR (mips_info, S_IRUGO,
> + bnx2i_show_mips_status, NULL);
> +static CLASS_DEVICE_ATTR (net_if_name, S_IRUGO,
> + bnx2i_show_net_if_name, NULL);
> +static CLASS_DEVICE_ATTR (pci_bar, S_IRUGO,
> + bnx2i_show_pci_bar, NULL);
> +static CLASS_DEVICE_ATTR (sq_size, S_IRUGO | S_IWUSR,
> + bnx2i_show_sq_info, bnx2i_set_sq_info);
> +static CLASS_DEVICE_ATTR (cq_size, S_IRUGO | S_IWUSR,
> + bnx2i_show_cq_info, bnx2i_set_cq_info);
> +static CLASS_DEVICE_ATTR (rq_size, S_IRUGO | S_IWUSR,
> + bnx2i_show_rq_info, bnx2i_set_rq_info);
> +static CLASS_DEVICE_ATTR (num_ccell, S_IRUGO | S_IWUSR,
> + bnx2i_show_ccell_info, bnx2i_set_ccell_info);
> +static CLASS_DEVICE_ATTR (pci_trigger, S_IRUGO,
> + bnx2i_read_pci_trigger_reg, NULL);
> +static CLASS_DEVICE_ATTR (ctx_dump, S_IRUGO | S_IWUSR,
> + bnx2i_get_iscsi_cntx_dump,
> + bnx2i_select_iscsi_cntx_dump);
> +static CLASS_DEVICE_ATTR (cid_list, S_IRUGO | S_IWUSR,
> + bnx2i_get_active_iscsi_cid_list,
> + bnx2i_set_iscsi_cid_err_poll_mode);
> +static CLASS_DEVICE_ATTR (qp_shmem_dump, S_IRUGO | S_IWUSR,
> + bnx2i_get_qp_shmem_dump,
> + bnx2i_setup_qp_shmem_dump);
> +static CLASS_DEVICE_ATTR (port_bind, S_IRUGO | S_IWUSR,
> + bnx2i_read_tcp_portd_options,
> + bnx2i_write_tcp_portd_results);
> +
> +
> +static struct class_device_attribute *bnx2i_class_attributes[] = {
> + &class_device_attr_mips_info,
> + &class_device_attr_net_if_name,
> + &class_device_attr_pci_bar,
> + &class_device_attr_sq_size,
> + &class_device_attr_cq_size,
> + &class_device_attr_rq_size,
> + &class_device_attr_num_ccell,
> + &class_device_attr_pci_trigger,
> + &class_device_attr_ctx_dump,
> + &class_device_attr_cid_list,
> + &class_device_attr_qp_shmem_dump,
> +};
> +
> +static struct class_device_attribute *tcp_port_class_attributes[] = {
> + &class_device_attr_port_bind
> +};
> +
> +static void bnx2i_sysfs_release(struct class_device *class_dev)
> +{
> +}
> +
> +struct class_device port_class_dev;
> +
> +
> +static struct class bnx2i_class = {
> + .name = "bnx2i",
> + .release = bnx2i_sysfs_release,
> +};
> +
> +
> +
> +static int bnx2i_register_port_class_dev(struct class_device *class_dev)
> +{
> + char dev_name[BUS_ID_SIZE];
> + int ret;
> + int i;
> +
> + class_dev->class = &bnx2i_class;
> + class_dev->class_data = class_dev;
> + snprintf(dev_name, BUS_ID_SIZE, "%s", "tcp_portd");
> + strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
> +
> + ret = class_device_register(class_dev);
> + if (ret)
> + goto err;
> +
> + for (i = 0; i < ARRAY_SIZE(tcp_port_class_attributes); ++i) {
> + ret = class_device_create_file(class_dev,
> + tcp_port_class_attributes[i]);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + return 0;
> +
> +err_unregister:
> + class_device_unregister(class_dev);
> +err:
> + return ret;
> +}
> +
> +
> +int bnx2i_register_sysfs(struct bnx2i_hba *hba)
> +{
> + struct class_device *class_dev = &hba->class_dev;
> + char dev_name[BUS_ID_SIZE];
> + int ret;
> + int i;
> +
> + class_dev->class = &bnx2i_class;
> + class_dev->class_data = hba;
> + snprintf(dev_name, BUS_ID_SIZE, "%.2x:%.2x.%.1x",
> + hba->pci_dev->bus->number,
> + PCI_SLOT(hba->pci_dev->devfn),
> + PCI_FUNC(hba->pci_dev->devfn));
> + strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
> +
> + ret = class_device_register(class_dev);
> + if (ret)
> + goto err;
> +
> + for (i = 0; i < ARRAY_SIZE(bnx2i_class_attributes); ++i) {
> + ret = class_device_create_file(class_dev,
> + bnx2i_class_attributes[i]);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + return 0;
> +
> +err_unregister:
> + class_device_unregister(class_dev);
> +err:
> + return ret;
> +}
> +
> +void bnx2i_unregister_sysfs(struct bnx2i_hba *hba)
> +{
> + class_device_unregister(&hba->class_dev);
> +}
> +
> +int bnx2i_sysfs_setup(void)
> +{
> + int ret;
> + ret = class_register(&bnx2i_class);
> +
> + bnx2i_register_port_class_dev(&port_class_dev);
> + return ret;
> +}
> +
> +void bnx2i_sysfs_cleanup(void)
> +{
> + class_device_unregister(&port_class_dev);
> + class_unregister(&bnx2i_class);
> +}
>
>
> -
> 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
* [RFC Resend 3/3][BNX2]: Add iSCSI support to BNX2 devices.
From: Michael Chan @ 2007-08-05 0:19 UTC (permalink / raw)
To: Jeff Garzik
Cc: David Miller, mchristi, netdev, open-iscsi, anilgv, talm,
lusinsky, uri
In-Reply-To: <46B43D2D.2090800@garzik.org>
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
new file mode 100644
index 0000000..0576e1b
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -0,0 +1,3718 @@
+/* bnx2i_iscsi.c: Broadcom NetXtreme II iSCSI driver.
+
+ *
+ * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+struct scsi_host_template bnx2i_host_template;
+struct iscsi_transport bnx2i_iscsi_transport;
+
+/*
+ * Global endpoint resource info
+ */
+void *bnx2i_ep_pages[MAX_PAGES_PER_CTRL_STRUCT_POOL];
+struct list_head bnx2i_free_ep_list;
+struct list_head bnx2i_unbound_ep;
+u32 bnx2i_num_free_ep;
+u32 bnx2i_max_free_ep;
+spinlock_t bnx2i_resc_lock;
+struct tcp_port_mngt bnx2i_tcp_port_tbl;
+static u16 bnx2i_local_tcp_port = 63000;
+
+
+static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
+ struct bnx2i_cmd *);
+static void bnx2i_free_tcp_port(u16 port);
+static u16 bnx2i_alloc_tcp_port(void);
+
+
+static int bnx2i_adapter_ready(struct bnx2i_hba *hba)
+{
+ int retval = 0;
+
+ if (!hba || !test_bit(ADAPTER_STATE_UP, &hba->adapter_state) ||
+ test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state))
+ retval = -EPERM;
+ return retval;
+}
+
+/*
+ * identifies & marks various bd info for imm data, unsolicited data
+ * and the first solicited data seq.
+ */
+static void bnx2i_get_write_cmd_bd_idx(struct bnx2i_cmd *cmd, u32 buf_off,
+ u32 *start_bd_off, u32 *start_bd_idx)
+{
+ u32 cur_offset = 0;
+ u32 cur_bd_idx = 0;
+ struct iscsi_bd *bd_tbl = cmd->bd_tbl->bd_tbl;
+
+ if (buf_off) {
+ while (buf_off >= (cur_offset + bd_tbl->buffer_length)) {
+ cur_offset += bd_tbl->buffer_length;
+ cur_bd_idx++;
+ bd_tbl++;
+ }
+ }
+
+ *start_bd_off = buf_off - cur_offset;
+ *start_bd_idx = cur_bd_idx;
+}
+
+/*
+ * identifies & marks various bd info for immediate data,
+ * unsolicited data and first solicited data seq.
+ */
+static void bnx2i_setup_write_cmd_bd_info(struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_conn *conn = NULL;
+ struct bnx2i_sess *sess = NULL;
+ u32 start_bd_offset = 0;
+ u32 start_bd_idx = 0;
+ u32 buffer_offset = 0;
+ u32 seq_len = 0;
+ u32 fbl = 0, mrdsl = 0;
+ u32 cmd_len = cmd->req.total_data_transfer_length;
+
+ if (cmd)
+ conn = cmd->conn;
+ if (conn->sess)
+ sess = conn->sess;
+
+ /* if ImmediateData is turned off & IntialR2T is turned on,
+ * there will be no immediate or unsolicited data, just return.
+ */
+ if (sess->initial_r2t && !sess->imm_data) {
+ return;
+ }
+ fbl = sess->first_burst_len;
+ mrdsl = conn->max_data_seg_len_xmit;
+
+ /* Immediate data */
+ if (sess->imm_data) {
+ seq_len = min(mrdsl, fbl);
+ seq_len = min(cmd_len, seq_len);
+ buffer_offset += seq_len;
+ }
+ if (seq_len == cmd_len) {
+ return;
+ }
+
+ if (!sess->initial_r2t) {
+ if (seq_len >= fbl)
+ goto r2t_data;
+ seq_len = min(fbl, cmd_len) - seq_len;
+ bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+ &start_bd_offset, &start_bd_idx);
+ cmd->req.ud_buffer_offset = start_bd_offset;
+ cmd->req.ud_start_bd_index = start_bd_idx;
+ buffer_offset += seq_len;
+ }
+r2t_data:
+ if (buffer_offset != cmd_len) {
+ bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+ &start_bd_offset, &start_bd_idx);
+ if ((start_bd_offset > fbl) ||
+ (start_bd_idx > cmd->scsi_cmd->use_sg)) {
+ int i = 0;
+
+ printk(KERN_ALERT "bnx2i- error, buf offset 0x%x "
+ "bd_valid %d use_sg %d\n",
+ buffer_offset, cmd->bd_tbl->bd_valid,
+ cmd->scsi_cmd->use_sg);
+ for (i = 0; i < cmd->bd_tbl->bd_valid; i++)
+ printk(KERN_ALERT "bnx2i err, bd[%d]: len %x\n",
+ i, cmd->bd_tbl->bd_tbl[i].\
+ buffer_length);
+ }
+ cmd->req.sd_buffer_offset = start_bd_offset;
+ cmd->req.sd_start_bd_index = start_bd_idx;
+ }
+}
+
+
+/*
+ */
+static int bnx2i_split_bd(struct bnx2i_cmd *cmd, u64 addr, int sg_len,
+ int bd_index)
+{
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ int frag_size = 0, sg_frags = 0;
+
+ while (sg_len) {
+ if (sg_len >= BD_SPLIT_SIZE)
+ frag_size = BD_SPLIT_SIZE;
+ else
+ frag_size = sg_len;
+ bd[bd_index + sg_frags].buffer_addr_lo = (u32) addr;
+ bd[bd_index + sg_frags].buffer_addr_hi = addr >> 32;
+ bd[bd_index + sg_frags].buffer_length = frag_size;
+ bd[bd_index + sg_frags].flags = 0;
+ if ((bd_index + sg_frags) == 0)
+ bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN;
+ addr += (u64) frag_size;
+ sg_frags++;
+ sg_len -= frag_size;
+ }
+ return sg_frags;
+}
+
+
+/*
+ * map single buffer
+ */
+static int bnx2i_map_single_buf(struct bnx2i_hba *hba,
+ struct bnx2i_cmd *cmd)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ int byte_count = 0;
+ int bd_count = 0;
+ u64 addr;
+
+ byte_count = sc->request_bufflen;
+ sc->SCp.dma_handle =
+ pci_map_single(hba->pci_dev, sc->request_buffer,
+ sc->request_bufflen, sc->sc_data_direction);
+ addr = sc->SCp.dma_handle;
+
+ if (byte_count > MAX_BD_LENGTH) {
+ bd_count = bnx2i_split_bd(cmd, addr, byte_count, 0);
+ } else {
+ bd_count = 1;
+ bd[0].buffer_addr_lo = addr & 0xffffffff;
+ bd[0].buffer_addr_hi = addr >> 32;
+ bd[0].buffer_length = sc->request_bufflen;
+ bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN |
+ ISCSI_BD_LAST_IN_BD_CHAIN;
+ }
+ bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
+
+ return bd_count;
+}
+
+
+/*
+ * map SG list
+ */
+static int bnx2i_map_sg(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ struct scatterlist *sg;
+ int byte_count = 0;
+ int sg_frags = 0;
+ int bd_count = 0;
+ int sg_count = 0;
+ int sg_len;
+ u64 addr;
+ int i;
+
+ sg = (struct scatterlist *) sc->request_buffer;
+
+ sg_count = pci_map_sg(hba->pci_dev, sg, sc->use_sg,
+ sc->sc_data_direction);
+
+ for (i = 0; i < sg_count; i++) {
+ sg_len = sg_dma_len(sg);
+ addr = sg_dma_address(sg);
+ if (sg_len > MAX_BD_LENGTH) {
+ sg_frags = bnx2i_split_bd(cmd, addr, sg_len,
+ bd_count);
+ } else {
+ sg_frags = 1;
+ bd[bd_count].buffer_addr_lo = addr & 0xffffffff;
+ bd[bd_count].buffer_addr_hi = addr >> 32;
+ bd[bd_count].buffer_length = sg_len;
+ bd[bd_count].flags = 0;
+ if (bd_count == 0)
+ bd[bd_count].flags =
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+ }
+ byte_count += sg_len;
+ sg++;
+ bd_count += sg_frags;
+ }
+ bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
+
+ BUG_ON(byte_count != sc->request_bufflen);
+ return bd_count;
+}
+
+/*
+ * creates BD list table for the command
+ */
+static int bnx2i_iscsi_map_sg_list(struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_hba *hba = cmd->conn->sess->hba;
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ int bd_count = 0;
+
+ if (sc->use_sg)
+ bd_count = bnx2i_map_sg(hba, cmd);
+ else if (sc->request_bufflen)
+ bd_count = bnx2i_map_single_buf(hba, cmd);
+ else {
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ bd_count = 0;
+ bd[0].buffer_addr_lo = bd[0].buffer_addr_hi = 0;
+ bd[0].buffer_length = bd[0].flags = 0;
+ }
+ cmd->bd_tbl->bd_valid = bd_count;
+ return 0;
+}
+
+
+/*
+ * create BD list table for the command
+ */
+int bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_hba *hba = cmd->conn->sess->hba;
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct pci_dev *pdev = hba->pci_dev;
+ struct scatterlist *sg;
+
+ if (cmd->bd_tbl->bd_valid && sc) {
+ if (sc->use_sg) {
+ sg = (struct scatterlist *) sc->request_buffer;
+ pci_unmap_sg(pdev, sg, sc->use_sg,
+ sc->sc_data_direction);
+ } else {
+ pci_unmap_single(pdev, sc->SCp.dma_handle,
+ sc->request_bufflen,
+ sc->sc_data_direction);
+ }
+ cmd->bd_tbl->bd_valid = 0;
+ }
+ return 0;
+}
+
+
+
+static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd)
+{
+ memset(&cmd->req, 0x00, sizeof(cmd->req));
+ cmd->req.op_code = ISCSI_OPCODE_SCSI_CMD;
+ cmd->req.bd_list_addr_lo = (u32) cmd->bd_tbl->bd_tbl_dma;
+ cmd->req.bd_list_addr_hi =
+ (u32) ((u64) cmd->bd_tbl->bd_tbl_dma >> 32);
+
+}
+
+
+/*
+ * update iscsi cid table entry with connection pointer
+ */
+static void bnx2i_bind_conn_to_iscsi_cid(struct bnx2i_conn *conn,
+ u32 iscsi_cid)
+{
+ struct bnx2i_hba *hba = NULL;
+
+ if (!conn || !conn->sess)
+ return;
+
+ hba = conn->sess->hba;
+
+ if (hba->cid_que.conn_cid_tbl[iscsi_cid])
+ printk(KERN_ERR "bnx2i: conn bind - entry #%d not free\n",
+ iscsi_cid);
+ hba->cid_que.conn_cid_tbl[iscsi_cid] = conn;
+}
+
+
+/*
+ * maps an iscsi cid to corresponding conn ptr
+ */
+struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
+ u16 iscsi_cid)
+{
+ if (!hba->cid_que.conn_cid_tbl) {
+ printk(KERN_ERR "bnx2i: ERROR - missing conn<->cid table\n");
+ return NULL;
+
+ } else if (iscsi_cid >= hba->max_active_conns) {
+ printk(KERN_ERR "bnx2i: wrong cid #%d\n", iscsi_cid);
+ return NULL;
+ }
+ return(hba->cid_que.conn_cid_tbl[iscsi_cid]);
+}
+
+
+
+/*
+ * allocates a iscsi_cid from free pool
+ */
+static u32 bnx2i_alloc_iscsi_cid(struct bnx2i_hba *hba)
+{
+ int idx = 0;
+
+ if (!hba->cid_que.cid_free_cnt)
+ return (ISCSI_RESERVED_TAG);
+
+ idx = hba->cid_que.cid_q_cons_idx;
+ hba->cid_que.cid_q_cons_idx++;
+ if (hba->cid_que.cid_q_cons_idx == hba->cid_que.cid_q_max_idx) {
+ hba->cid_que.cid_q_cons_idx = 0;
+ }
+
+ hba->cid_que.cid_free_cnt--;
+ return hba->cid_que.cid_que[idx];
+}
+
+
+/*
+ * return iscsi_cid back to free pool
+ */
+static void bnx2i_free_iscsi_cid(struct bnx2i_hba *hba, u16 iscsi_cid)
+{
+ int idx = 0;
+
+ if (iscsi_cid == (u16)ISCSI_RESERVED_TAG)
+ return;
+
+ hba->cid_que.cid_free_cnt++;
+
+ idx = hba->cid_que.cid_q_prod_idx;
+ hba->cid_que.cid_que[idx] = iscsi_cid;
+ hba->cid_que.conn_cid_tbl[iscsi_cid] = NULL;
+ hba->cid_que.cid_q_prod_idx++;
+ if (hba->cid_que.cid_q_prod_idx == hba->cid_que.cid_q_max_idx) {
+ hba->cid_que.cid_q_prod_idx = 0;
+ }
+}
+
+
+
+/*
+ * setup iscsi_cid queue, 'iscsi_cid' value ranges from 0 to (MAX_CONNS -1)
+ */
+static int bnx2i_setup_free_cid_que(struct bnx2i_hba *hba)
+{
+ int mem_size;
+ int i = 0;
+
+ mem_size = hba->max_active_conns * sizeof(u16);
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+ hba->cid_que.cid_que_base = kmalloc(mem_size, GFP_KERNEL);
+ if (!hba->cid_que.cid_que_base)
+ return -ENOMEM;
+
+ mem_size = hba->max_active_conns * sizeof(struct bnx2i_conn *);
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ hba->cid_que.conn_cid_tbl =
+ (struct bnx2i_conn **)kmalloc(mem_size, GFP_KERNEL);
+ if (!hba->cid_que.conn_cid_tbl) {
+ kfree(hba->cid_que.cid_que_base);
+ hba->cid_que.cid_que_base = NULL;
+ }
+
+ hba->cid_que.cid_que = (u32 *)hba->cid_que.cid_que_base;
+ hba->cid_que.cid_q_prod_idx = 0;
+ hba->cid_que.cid_q_cons_idx = 0;
+ hba->cid_que.cid_q_max_idx = hba->max_active_conns;
+ hba->cid_que.cid_free_cnt = hba->max_active_conns;
+
+ for (i = 0; i < hba->max_active_conns; i++) {
+ hba->cid_que.cid_que[i] = i;
+ hba->cid_que.conn_cid_tbl[i] = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * Releases resources held by free 'iscsi_cid' queue
+ */
+static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba)
+{
+ if (hba->cid_que.cid_que_base) {
+ kfree(hba->cid_que.cid_que_base);
+ hba->cid_que.cid_que_base = NULL;
+ }
+
+ if (hba->cid_que.conn_cid_tbl) {
+ kfree(hba->cid_que.conn_cid_tbl);
+ hba->cid_que.conn_cid_tbl = NULL;
+ }
+}
+
+
+/*
+ * routine allocates a free endpoint structure from the global pool
+ */
+struct bnx2i_endpoint *bnx2i_alloc_ep(void)
+{
+ struct bnx2i_endpoint *endpoint = NULL;
+ struct list_head *listp;
+ u16 tcp_port;
+
+ spin_lock_bh(&bnx2i_resc_lock);
+
+ tcp_port = bnx2i_alloc_tcp_port();
+ if (!tcp_port) {
+ spin_unlock_bh(&bnx2i_resc_lock);
+ return NULL;
+ }
+ if (list_empty(&bnx2i_free_ep_list)) {
+ spin_unlock_bh(&bnx2i_resc_lock);
+ printk(KERN_ERR "alloc_ep: unable to alloc ep struct\n");
+ return endpoint;
+ }
+ listp = (struct list_head *)bnx2i_free_ep_list.next;
+ list_del_init(listp);
+ bnx2i_num_free_ep--;
+
+ endpoint = (struct bnx2i_endpoint *)listp;
+ endpoint->in_use = 1;
+ endpoint->tcp_port = tcp_port;
+ init_waitqueue_head(&endpoint->ofld_wait);
+
+ spin_unlock_bh(&bnx2i_resc_lock);
+ return endpoint;
+}
+
+
+/*
+ * free endpoint structure to global free pool
+ */
+void bnx2i_free_ep(struct bnx2i_endpoint *endpoint)
+{
+ if (!endpoint)
+ return;
+
+ spin_lock_bh(&bnx2i_resc_lock);
+ endpoint->state = EP_STATE_IDLE;
+ endpoint->in_use = 0;
+ bnx2i_free_iscsi_cid(endpoint->hba, endpoint->ep_iscsi_cid);
+ if (endpoint->conn) {
+ endpoint->conn->ep = NULL;
+ endpoint->conn = NULL;
+ }
+ endpoint->sess = NULL;
+
+ if (endpoint->tcp_port) {
+ bnx2i_free_tcp_port(endpoint->tcp_port);
+ }
+ endpoint->hba = NULL;
+ list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
+ bnx2i_num_free_ep++;
+ spin_unlock_bh(&bnx2i_resc_lock);
+}
+
+
+/*
+ * allocates free pool of endpoint structurres, endpoint structures
+ * are used to store QP related control and PT info
+ */
+int bnx2i_alloc_ep_pool(void)
+{
+ struct bnx2i_endpoint *endpoint = NULL;
+ int index = 0, count = 0;
+ int ret_val = 1;
+ int total_endpoints = 0;
+ int page_count = 0;
+ int num_endpoints_per_page = 0;
+ void *mem_ptr = NULL;
+
+ spin_lock_init(&bnx2i_resc_lock);
+ INIT_LIST_HEAD(&bnx2i_free_ep_list);
+ INIT_LIST_HEAD(&bnx2i_unbound_ep);
+
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ bnx2i_ep_pages[index] = NULL;
+ }
+
+ num_endpoints_per_page =
+ PAGE_SIZE / sizeof(struct bnx2i_endpoint);
+
+ total_endpoints = ISCSI_MAX_CONNS_PER_HBA;
+ if (total_endpoints >
+ (num_endpoints_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
+ total_endpoints = (num_endpoints_per_page *
+ MAX_PAGES_PER_CTRL_STRUCT_POOL);
+ }
+
+ bnx2i_num_free_ep = 0;
+ for (index = 0; index < total_endpoints;) {
+ mem_ptr = (void *)kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ERR "ep_pool: mem alloc failed\n");
+ break;
+ }
+ bnx2i_ep_pages[page_count++] = (void *)mem_ptr;
+
+ memset(mem_ptr, 0, PAGE_SIZE);
+
+ endpoint = (struct bnx2i_endpoint *)mem_ptr;
+ for (count = 0; count < num_endpoints_per_page; count++) {
+ endpoint->in_use = 0;
+ list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
+ endpoint++;
+ }
+
+ bnx2i_num_free_ep += num_endpoints_per_page;
+ index += num_endpoints_per_page;
+ }
+ if (bnx2i_num_free_ep == 0)
+ ret_val = 0;
+ bnx2i_max_free_ep = bnx2i_num_free_ep;
+
+ return(ret_val);
+}
+
+
+/*
+ * Free memory resources held by global endpoint pool
+ */
+void bnx2i_release_ep_pool(void)
+{
+ int index = 0;
+ void *mem_ptr = NULL;
+
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ mem_ptr = bnx2i_ep_pages[index];
+ if (mem_ptr) {
+ kfree((void *) mem_ptr);
+ break;
+ }
+ bnx2i_ep_pages[index] = NULL;
+ }
+ bnx2i_num_free_ep = 0;
+ return;
+}
+
+
+/*
+ * iSCSI Session ITT queue management code
+ */
+static u32 bnx2i_alloc_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
+{
+ u32 itt_val = ITT_INVALID_SIGNATURE;
+
+ if (sess->itt_q.itt_q_count) {
+ itt_val = sess->itt_q.itt_que[sess->itt_q.itt_q_cons_idx++];
+ sess->itt_q.itt_q_cons_idx %= sess->itt_q.itt_q_max_idx;
+ sess->itt_q.itt_cmd[itt_val] = cmd;
+ sess->itt_q.itt_q_count--;
+ }
+ return itt_val;
+}
+
+
+static void bnx2i_free_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
+{
+ if (cmd->req.itt == ITT_INVALID_SIGNATURE) {
+ printk(KERN_ALERT "free_itt: RSVD ITT - sess 0x%p\n", sess);
+ }
+ sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] = cmd->req.itt;
+ sess->itt_q.itt_q_prod_idx %= sess->itt_q.itt_q_max_idx;
+ sess->itt_q.itt_cmd[cmd->req.itt] = NULL;
+ sess->itt_q.itt_q_count++;
+ cmd->req.itt = ITT_INVALID_SIGNATURE;
+}
+
+
+/*
+ * setup ITT queue during iSCSI session creation. ITT queue is a
+ * circular array of ITTs [range 0 - (SQ SIZE - 1)] managed by
+ * producer and consumer index
+ */
+static int bnx2i_setup_free_itt_queue(struct bnx2i_sess *sess)
+{
+ u16 itt_q_size = (u16)sess->sq_size;
+ u32 itt_value = 0;
+ int unit_size = sizeof(u16);
+ int mem_size = PAGE_SIZE;
+
+ if ((itt_q_size * unit_size) > mem_size)
+ mem_size = (itt_q_size * unit_size);
+
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ sess->itt_q.itt_que_base = kmalloc(mem_size, GFP_KERNEL);
+ if (!sess->itt_q.itt_que_base) {
+ return -ENOMEM;
+ }
+
+ mem_size = (itt_q_size * sizeof(struct bnx2i_cmd *));
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ sess->itt_q.itt_cmd =
+ (struct bnx2i_cmd **) kmalloc(mem_size, GFP_KERNEL);
+ if (!sess->itt_q.itt_cmd) {
+ kfree(sess->itt_q.itt_que_base);
+ sess->itt_q.itt_que_base = NULL;
+ return -1;
+ }
+ memset(sess->itt_q.itt_cmd, 0x00, mem_size);
+
+ sess->itt_q.itt_que = (u32 *)sess->itt_q.itt_que_base;
+ sess->itt_q.itt_q_prod_idx = 0;
+ sess->itt_q.itt_q_cons_idx = 0;
+ sess->itt_q.itt_q_max_idx = itt_q_size;
+ sess->itt_q.itt_q_count = itt_q_size;
+
+ itt_value = 0;
+ while (itt_value < itt_q_size) {
+ sess->itt_q.itt_cmd[itt_value] = (struct bnx2i_cmd *)NULL;
+ sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] =
+ itt_value++;
+ if (sess->itt_q.itt_q_prod_idx >= sess->itt_q.itt_q_max_idx) {
+ sess->itt_q.itt_q_prod_idx = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * free resources held by free ITT queue
+ */
+static void bnx2i_release_free_itt_queue(struct bnx2i_sess *sess)
+{
+ sess->itt_q.itt_q_count = 0;
+ if (sess->itt_q.itt_que_base) {
+ kfree (sess->itt_q.itt_que_base);
+ sess->itt_q.itt_que_base = NULL;
+ }
+
+ if (sess->itt_q.itt_cmd) {
+ kfree (sess->itt_q.itt_cmd);
+ sess->itt_q.itt_cmd = NULL;
+ }
+ return;
+}
+
+
+/*
+ * allocates a command structures from free poll
+ */
+struct bnx2i_cmd *bnx2i_alloc_cmd(struct bnx2i_sess *sess)
+{
+ struct bnx2i_cmd *cmd = NULL;
+ struct list_head *listp;
+
+ if (unlikely(!sess || (sess->num_free_cmds == 0))) {
+ return cmd;
+ }
+
+ listp = (struct list_head *) sess->free_cmds.next;
+ list_del_init(listp);
+ sess->num_free_cmds--;
+ cmd = (struct bnx2i_cmd *)listp;
+ cmd->in_use = 1;
+ cmd->scsi_status_rcvd = cmd->resi_len = 0;
+ cmd->scsi_uflow = cmd->scsi_oflow = 0;
+
+ bnx2i_setup_cmd_wqe_template(cmd);
+
+ cmd->req.itt = bnx2i_alloc_itt(sess, cmd);
+
+ return cmd;
+}
+
+
+/*
+ * return command structure and ITT back to free pool.
+ */
+void bnx2i_free_cmd(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
+{
+ if (!sess || !cmd)
+ return;
+
+ cmd->in_use = 0;
+ bnx2i_free_itt(sess, cmd);
+ list_add_tail(&cmd->link, &sess->free_cmds);
+ sess->num_free_cmds++;
+}
+
+
+/*
+ * Allocate command structure pool for a given iSCSI session
+ */
+int bnx2i_alloc_cmd_pool(struct bnx2i_sess *sess)
+{
+ struct bnx2i_cmd *cmdp = NULL;
+ int index = 0, count = 0;
+ int ret_val = 0;
+ int total_cmds = 0;
+ int num_cmds = 0;
+ int page_count = 0;
+ int num_cmds_per_page = 0;
+ void *mem_ptr = NULL;
+
+ if (!sess)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&sess->free_cmds);
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ sess->cmd_pages[index] = NULL;
+ }
+
+ num_cmds_per_page = PAGE_SIZE / sizeof(struct bnx2i_cmd);
+ total_cmds = sess->hba->scsi_template->can_queue + 1;
+ if (total_cmds >
+ (num_cmds_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
+ total_cmds = num_cmds_per_page *
+ MAX_PAGES_PER_CTRL_STRUCT_POOL;
+ }
+
+ for (index = 0; index < total_cmds;) {
+ mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ break;
+ }
+ sess->cmd_pages[page_count++] = (void *)mem_ptr;
+
+ num_cmds = num_cmds_per_page;
+ if ((total_cmds - index) < num_cmds_per_page)
+ num_cmds = (total_cmds - index);
+
+ memset(mem_ptr, 0, PAGE_SIZE);
+ cmdp = (struct bnx2i_cmd *) mem_ptr;
+ for (count = 0; count < num_cmds; count++) {
+ cmdp->in_use = 0;
+ cmdp->req.itt = ITT_INVALID_SIGNATURE;
+
+ /* Allocate BD table */
+ cmdp->bd_tbl = bnx2i_alloc_bd_table(sess, cmdp);
+ if (!cmdp->bd_tbl) {
+ /* should never fail, as it's guaranteed to have
+ * (ISCSI_MAX_CMDS_PER_SESS + 1) BD tables
+ * allocated before calling this function.
+ */
+ printk(KERN_ERR "no BD table cmd %p\n", cmdp);
+ goto bd_table_failed;
+ }
+ list_add_tail(&cmdp->link, &sess->free_cmds);
+ cmdp++;
+ }
+
+ sess->num_free_cmds += num_cmds;
+ index += num_cmds;
+ }
+ sess->allocated_cmds = sess->num_free_cmds;
+
+ if (sess->num_free_cmds == 0)
+ ret_val = -ENOMEM;
+ return(ret_val);
+
+bd_table_failed:
+ return(-ENOMEM);
+}
+
+
+/*
+ * Release memory held by command struct pool.
+ */
+void bnx2i_free_cmd_pool(struct bnx2i_sess *sess)
+{
+ int index = 0;
+ void *mem_ptr = NULL;
+
+ if (unlikely(!sess))
+ return;
+
+ if (sess->num_free_cmds != sess->allocated_cmds) {
+ /*
+ * WARN: either there is some command struct leak or
+ * still some SCSI commands are pending.
+ * TODO: post mortem required...
+ */
+ }
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ mem_ptr = sess->cmd_pages[index];
+ if (mem_ptr) {
+ kfree((void *) mem_ptr);
+ break;
+ }
+ sess->cmd_pages[index] = NULL;
+ }
+ sess->num_free_cmds = sess->allocated_cmds = 0;
+ return;
+}
+
+
+/*
+ * Allocate a BD table
+ */
+static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
+ struct bnx2i_cmd *cmd)
+{
+ struct io_bdt *bd_tbl = NULL;
+
+ if (list_empty(&sess->bd_tbl_list)) {
+ return NULL;
+ }
+ bd_tbl = (struct io_bdt *)sess->bd_tbl_list.next;
+ list_del(&bd_tbl->link);
+ list_add_tail(&bd_tbl->link, &sess->bd_tbl_active);
+ bd_tbl->bd_valid = 0;
+ if (!bd_tbl->cmdp) {
+ bd_tbl->cmdp = cmd;
+ }
+ return bd_tbl;
+}
+
+
+/*
+ * Free up memory pages allocated held by BD resources
+ */
+static void bnx2i_free_all_bdt_resc_pages(struct bnx2i_sess *sess)
+{
+ int i = 0;
+ struct bd_resc_page *resc_page = NULL;
+
+ spin_lock_bh(&sess->lock);
+ while (!list_empty(&sess->bd_resc_page)) {
+ resc_page = (struct bd_resc_page *)sess->bd_resc_page.prev;
+ list_del(sess->bd_resc_page.prev);
+ for(i = 0; i < resc_page->num_valid; i++) {
+ kfree(resc_page->page[i]);
+ }
+ kfree(resc_page);
+ }
+ spin_unlock_bh(&sess->lock);
+}
+
+
+
+/*
+ * allocated 4K page to track BD table memory
+ */
+struct bd_resc_page *bnx2i_alloc_bdt_resc_page(struct bnx2i_sess *sess)
+{
+ void *mem_ptr;
+ struct bd_resc_page *resc_page = NULL;
+
+ mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!mem_ptr)
+ return NULL;
+
+ resc_page = (struct bd_resc_page *) mem_ptr;
+ list_add_tail(&resc_page->link, &sess->bd_resc_page);
+ resc_page->max_ptrs = (PAGE_SIZE -
+ (u32)&((struct bd_resc_page *) 0)->page[0]) / sizeof(void *);
+ resc_page->num_valid = 0;
+
+ return resc_page;
+}
+
+
+/*
+ * link newly allocated memory page to the list
+ */
+int bnx2i_add_bdt_resc_page(struct bnx2i_sess *sess, void *bd_page)
+{
+ struct bd_resc_page *resc_page = NULL;
+
+#define is_resc_page_full(_resc_pg) (_resc_pg->num_valid == _resc_pg->max_ptrs)
+#define active_resc_page(_resc_list) \
+ (list_empty(_resc_list) ? NULL : (_resc_list)->prev)
+ if (list_empty(&sess->bd_resc_page)) {
+ resc_page = bnx2i_alloc_bdt_resc_page(sess);
+ } else {
+ resc_page = (struct bd_resc_page *)
+ active_resc_page(&sess->bd_resc_page);
+ }
+
+ if (!resc_page)
+ return -ENOMEM;
+
+ resc_page->page[resc_page->num_valid++] = bd_page;
+ if (is_resc_page_full(resc_page)) {
+ resc_page = bnx2i_alloc_bdt_resc_page(sess);
+ }
+ return 0;
+}
+
+
+/*
+ * Allocate BD table pool, DMA'able memory for a given session.
+ */
+int bnx2i_alloc_bd_table_pool(struct bnx2i_sess *sess)
+{
+ int index = 0, count = 0;
+ int ret_val = 0;
+ int num_elem_per_page;
+ struct io_bdt *bdt_info;
+ char *mem_ptr = NULL;
+ u32 bd_tbl_size = 0;
+ u32 mem_size = 0;
+ int total_bd_tbl = 0;
+
+ INIT_LIST_HEAD(&sess->bd_resc_page);
+ INIT_LIST_HEAD(&sess->bd_tbl_list);
+ INIT_LIST_HEAD(&sess->bd_tbl_active);
+ total_bd_tbl = sess->hba->scsi_template->can_queue + 1;
+ mem_size = total_bd_tbl * sizeof(struct io_bdt);
+ num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
+ for (index = 0; index < total_bd_tbl; index += num_elem_per_page) {
+ if (((total_bd_tbl - index) * sizeof(struct io_bdt))
+ >= PAGE_SIZE) {
+ mem_size = PAGE_SIZE;
+ num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
+ } else {
+ mem_size =
+ (total_bd_tbl - index) * sizeof(struct io_bdt);
+ num_elem_per_page = (total_bd_tbl - index);
+ }
+ mem_ptr = (void *)kmalloc(mem_size, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ERR "alloc_bd_tbl: mem alloc failed\n");
+ ret_val = -ENOMEM;
+ goto resc_alloc_failed;
+ }
+ bnx2i_add_bdt_resc_page(sess, mem_ptr);
+
+ memset(mem_ptr, 0, mem_size);
+ bdt_info = (struct io_bdt *)mem_ptr;
+ for (count = 0; count < num_elem_per_page; count++) {
+ list_add_tail(&bdt_info->link, &sess->bd_tbl_list);
+ bdt_info++;
+ }
+ }
+
+ bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
+ bdt_info = (struct io_bdt *)sess->bd_tbl_list.next;
+ while (bdt_info && (bdt_info != (struct io_bdt *)&sess->bd_tbl_list)) {
+ mem_ptr = (char *)pci_alloc_consistent(sess->hba->pci_dev,
+ bd_tbl_size,
+ &bdt_info->bd_tbl_dma);
+ if (!mem_ptr) {
+ printk(KERN_ERR "bd_tbl: DMA mem alloc failed\n");
+ ret_val = -ENOMEM;
+ goto dma_alloc_failed;
+ }
+ bdt_info->bd_tbl = (struct iscsi_bd *)mem_ptr;
+ bdt_info->max_bd_cnt = ISCSI_MAX_BDS_PER_CMD;
+ bdt_info->bd_valid = 0;
+ bdt_info->cmdp = NULL;
+
+ bdt_info = (struct io_bdt *)bdt_info->link.next;
+ }
+ return(ret_val);
+
+resc_alloc_failed:
+dma_alloc_failed:
+ return(ret_val);
+}
+
+
+/*
+ * releases BD table pool memory
+ */
+void bnx2i_free_bd_table_pool(struct bnx2i_sess *sess)
+{
+ struct list_head *list;
+ struct io_bdt *bdt_info;
+ u32 bd_tbl_size = 0;
+
+ bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
+ list_for_each(list, &sess->bd_tbl_list) {
+ bdt_info = list_entry(list, struct io_bdt, link);
+ pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
+ (void *)bdt_info->bd_tbl,
+ bdt_info->bd_tbl_dma);
+ bdt_info->bd_tbl = NULL;
+ if (bdt_info->cmdp) {
+ bdt_info->cmdp->bd_tbl = NULL;
+ bdt_info->cmdp = NULL;
+ }
+ }
+
+ list_for_each(list, &sess->bd_tbl_active) {
+ bdt_info = list_entry(list, struct io_bdt, link);
+ pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
+ (void *)bdt_info->bd_tbl,
+ bdt_info->bd_tbl_dma);
+ bdt_info->bd_tbl = NULL;
+ if (bdt_info->cmdp) {
+ bdt_info->cmdp->bd_tbl = NULL;
+ bdt_info->cmdp = NULL;
+ }
+ }
+}
+
+
+/*
+ * allocate memory for dummy buffer and associated BD table
+ * to be used by middle path (MP) requests
+ */
+static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
+{
+ int rc = 0;
+ struct iscsi_bd *mp_bdt;
+ u64 addr;
+ hba->mp_bd_tbl = NULL;
+ if (hba->cnic_dev_type == CNIC_10GIG_GEN1)
+ return rc;
+
+ hba->mp_bd_tbl = pci_alloc_consistent(hba->pci_dev,
+ PAGE_SIZE, &hba->mp_bd_dma);
+ if (!hba->mp_bd_tbl) {
+ printk(KERN_ERR "unable to allocate Middle Path BDT\n");
+ rc = -1;
+ goto out;
+ }
+
+ hba->dummy_buffer =
+ pci_alloc_consistent(hba->pci_dev,
+ PAGE_SIZE, &hba->dummy_buf_dma);
+ if (!hba->dummy_buffer) {
+ printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n");
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ hba->mp_bd_tbl, hba->mp_bd_dma);
+ hba->mp_bd_tbl = NULL;
+ rc = -1;
+ goto out;
+ }
+
+ mp_bdt = (struct iscsi_bd *)hba->mp_bd_tbl;
+ addr = (unsigned long)hba->dummy_buf_dma;
+ mp_bdt->buffer_addr_lo = addr & 0xffffffff;
+ mp_bdt->buffer_addr_hi = addr >> 32;
+ mp_bdt->buffer_length = PAGE_SIZE;
+ mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+out:
+ return rc;
+}
+
+
+/*
+ * free MP dummy buffer and associated BD table
+ */
+static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
+{
+
+ if (hba->mp_bd_tbl) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ hba->mp_bd_tbl, hba->mp_bd_dma);
+ hba->mp_bd_tbl = NULL;
+ }
+ if (hba->dummy_buffer) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ hba->dummy_buffer, hba->dummy_buf_dma);
+ hba->dummy_buffer = NULL;
+ }
+ return;
+}
+
+
+static u16 bnx2i_alloc_tcp_port()
+{
+ return bnx2i_local_tcp_port++;
+}
+
+
+/*
+ * Function : bnx2i_free_tcp_port
+ * Description:
+ */
+static void bnx2i_free_tcp_port(u16 port)
+{
+ if (!bnx2i_tcp_port_tbl.free_q)
+ return;
+
+ bnx2i_tcp_port_tbl.free_q[bnx2i_tcp_port_tbl.prod_idx] = port;
+ bnx2i_tcp_port_tbl.prod_idx++;
+ bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
+ bnx2i_tcp_port_tbl.num_free_ports++;
+}
+
+void bnx2i_tcp_port_new_entry(u16 tcp_port)
+{
+ u32 idx = bnx2i_tcp_port_tbl.prod_idx;
+
+ spin_lock(&bnx2i_resc_lock);
+ bnx2i_tcp_port_tbl.free_q[idx] = (u16)tcp_port;
+ bnx2i_tcp_port_tbl.prod_idx++;
+ bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
+ bnx2i_tcp_port_tbl.num_free_ports++;
+ bnx2i_tcp_port_tbl.num_required--;
+ spin_unlock(&bnx2i_resc_lock);
+}
+
+/*
+ * Function : bnx2i_init_tcp_port_mngr
+ * Description:
+ */
+void bnx2i_init_tcp_port_mngr(void)
+{
+ int mem_size = 0;
+
+ bnx2i_tcp_port_tbl.num_free_ports = 0;
+ bnx2i_tcp_port_tbl.prod_idx = 0;
+ bnx2i_tcp_port_tbl.cons_idx = 0;
+ bnx2i_tcp_port_tbl.max_idx = 0;
+ bnx2i_tcp_port_tbl.num_required = 0;
+
+#define BNX2I_MAX_TCP_PORTS 1024
+
+ bnx2i_tcp_port_tbl.port_tbl_size = BNX2I_MAX_TCP_PORTS;
+
+ mem_size = sizeof(u16) * bnx2i_tcp_port_tbl.port_tbl_size;
+ if (bnx2i_tcp_port_tbl.port_tbl_size) {
+ bnx2i_tcp_port_tbl.free_q =
+ (u16 *)kmalloc(mem_size, GFP_KERNEL);
+
+ if (bnx2i_tcp_port_tbl.free_q)
+ bnx2i_tcp_port_tbl.max_idx =
+ bnx2i_tcp_port_tbl.port_tbl_size;
+ }
+}
+
+
+/*
+ * Function : bnx2i_cleanup_tcp_port_mngr
+ * Description:
+ */
+void bnx2i_cleanup_tcp_port_mngr(void)
+{
+ if (bnx2i_tcp_port_tbl.free_q) {
+ kfree(bnx2i_tcp_port_tbl.free_q);
+ bnx2i_tcp_port_tbl.free_q = NULL;
+ }
+ bnx2i_tcp_port_tbl.num_free_ports = 0;
+}
+
+
+
+/*
+ * interface was brought down by the user, fail all iSCSI sessions
+ * on this adapter,
+ */
+void bnx2i_start_iscsi_hba_shutdown(struct bnx2i_hba *hba)
+{
+ struct list_head *list = NULL;
+ struct list_head *tmp = NULL;
+ struct bnx2i_sess *sess;
+
+ list_for_each_safe(list, tmp, &hba->active_sess) {
+ sess = (struct bnx2i_sess *)list;
+ bnx2i_do_iscsi_sess_recovery(sess, DID_NO_CONNECT);
+ }
+}
+
+
+/*
+ * IP address change indication, fail all iSCSI sessions on this adapter
+ */
+void bnx2i_iscsi_handle_ip_event(struct bnx2i_hba *hba)
+{
+ struct list_head *list = NULL;
+ struct list_head *tmp = NULL;
+ struct bnx2i_sess *sess;
+
+ spin_lock(&hba->lock);
+ list_for_each_safe(list, tmp, &hba->active_sess) {
+ sess = (struct bnx2i_sess *)list;
+ spin_unlock(&hba->lock);
+ bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
+ spin_lock(&hba->lock);
+ }
+ spin_unlock(&hba->lock);
+}
+
+
+
+static void
+conn_err_recovery_task(struct work_struct *work)
+{
+ struct bnx2i_hba *hba = container_of(work, struct bnx2i_hba,
+ err_rec_task);
+ struct bnx2i_sess *sess;
+ int cons_idx = hba->sess_recov_cons_idx;
+
+ while (hba->sess_recov_prod_idx != cons_idx) {
+ sess = hba->sess_recov_list[cons_idx];
+ bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
+ if (cons_idx == hba->sess_recov_max_idx)
+ cons_idx = 0;
+ else
+ cons_idx++;
+ }
+ hba->sess_recov_cons_idx = cons_idx;
+}
+
+
+
+
+/*
+ * allocate memory buffer to extract conn context
+ */
+static void bnx2i_init_ctx_dump_mem(struct bnx2i_hba *hba)
+{
+ if (hba->ctx_addr)
+ return;
+
+ hba->ictx_poll_mode = 0;
+ hba->ctx_size = 0;
+ hba->ctx_read_cnt = 0xffffffff;
+ hba->ctx_addr = pci_alloc_consistent(hba->pci_dev,
+ BNX2I_CONN_CTX_BUF_SIZE,
+ &hba->ctx_dma_hndl);
+ if (!hba->ctx_addr)
+ return;
+ hba->ctx_size = BNX2I_CONN_CTX_BUF_SIZE;
+}
+
+
+/*
+ * free context memory buffer
+ */
+static void bnx2i_free_ctx_dump_mem(struct bnx2i_hba *hba)
+{
+ if (!hba->ctx_addr || (hba->ctx_size == 0))
+ return;
+
+ pci_free_consistent(hba->pci_dev, hba->ctx_size,
+ hba->ctx_addr, hba->ctx_dma_hndl);
+ hba->ctx_dma_hndl = 0;
+ hba->ctx_addr = NULL;
+ hba->ctx_size = 0;
+}
+
+
+static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ int cur_idx;
+
+ write_lock(&hba->ep_rdwr_lock);
+ cur_idx = hba->ep_destroy_prod_idx++;
+ hba->ep_destroy_list[cur_idx] = ep;
+ hba->ep_destroy_prod_idx %= hba->ep_destroy_max_idx;
+ write_unlock(&hba->ep_rdwr_lock);
+ return 0;
+}
+
+struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba)
+{
+ int cur_idx;
+
+ read_lock(&hba->ep_rdwr_lock);
+ if (hba->ep_destroy_prod_idx == hba->ep_destroy_cons_idx) {
+ read_unlock(&hba->ep_rdwr_lock);
+ return NULL;
+ }
+ cur_idx = hba->ep_destroy_cons_idx++;
+ hba->ep_destroy_cons_idx %= hba->ep_destroy_max_idx;
+ read_unlock(&hba->ep_rdwr_lock);
+
+ return (hba->ep_destroy_list[cur_idx]);
+}
+
+static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ int cur_idx;
+
+ write_lock(&hba->ep_rdwr_lock);
+ cur_idx = hba->ep_ofld_prod_idx++;
+ hba->ep_ofld_list[cur_idx] = ep;
+ hba->ep_ofld_prod_idx %= hba->ep_ofld_max_idx;
+ write_unlock(&hba->ep_rdwr_lock);
+ return 0;
+}
+
+struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba)
+{
+ int cur_idx;
+
+ read_lock(&hba->ep_rdwr_lock);
+ if (hba->ep_ofld_prod_idx == hba->ep_ofld_cons_idx) {
+ read_unlock(&hba->ep_rdwr_lock);
+ return NULL;
+ }
+ cur_idx = hba->ep_ofld_cons_idx++;
+ hba->ep_ofld_cons_idx %= hba->ep_ofld_max_idx;
+ read_unlock(&hba->ep_rdwr_lock);
+
+ return (hba->ep_ofld_list[cur_idx]);
+}
+
+static int bnx2i_init_ep_ofld_destroy_que(struct bnx2i_hba *hba)
+{
+ rwlock_init(&hba->ep_rdwr_lock);
+ hba->ep_ofld_list = (struct bnx2i_endpoint **)
+ kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!hba->ep_ofld_list)
+ return -ENOMEM;
+
+ hba->ep_ofld_prod_idx = 0;
+ hba->ep_ofld_cons_idx = 0;
+ hba->ep_ofld_max_idx =
+ PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
+
+ hba->ep_destroy_list = (struct bnx2i_endpoint **)
+ kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!hba->ep_destroy_list) {
+ kfree(hba->ep_ofld_list);
+ hba->ep_ofld_list = NULL;
+ return -ENOMEM;
+ }
+
+ hba->ep_destroy_prod_idx = 0;
+ hba->ep_destroy_cons_idx = 0;
+ hba->ep_destroy_max_idx =
+ PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
+ return 0;
+}
+
+
+static void bnx2i_free_ep_ofld_destroy_que(struct bnx2i_hba *hba)
+{
+ if (hba->ep_ofld_list) {
+ kfree(hba->ep_ofld_list);
+ hba->ep_ofld_list = NULL;
+ }
+ if (hba->ep_destroy_list) {
+ kfree(hba->ep_destroy_list);
+ hba->ep_destroy_list = NULL;
+ }
+}
+
+/*
+ * allocate & initialize adapter structure and call other
+ * support routines to do per adapter initialization
+ */
+struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
+{
+ struct bnx2i_hba *hba = NULL;
+
+ hba = kmalloc(sizeof(struct bnx2i_hba), GFP_KERNEL);
+
+ if (hba == NULL)
+ return NULL;
+
+ memset((void *) hba, 0, sizeof(struct bnx2i_hba));
+
+ /* Get PCI related information and update hba struct members */
+ hba->pci_dev = cnic->pcidev;
+ if (hba->pci_dev) {
+ hba->pci_did = hba->pci_dev->device;
+ hba->pci_vid = hba->pci_dev->vendor;
+ hba->pci_sdid = hba->pci_dev->subsystem_device;
+ hba->pci_svid = hba->pci_dev->subsystem_vendor;
+ hba->pci_func = PCI_FUNC(hba->pci_dev->devfn);
+ hba->pci_devno = PCI_SLOT(hba->pci_dev->devfn);
+ hba->pci_intr_num = hba->pci_dev->irq;
+ }
+
+ INIT_LIST_HEAD(&hba->active_sess);
+ if (bnx2i_init_ep_ofld_destroy_que(hba))
+ goto ep_ofld_que_err;
+
+ hba->mtu_supported = BNX2I_MAX_MTU_SUPPORTED;
+
+ /* TODO: different values for Teton/Xinan/Everest */
+ hba->max_active_conns = ISCSI_MAX_CONNS_PER_HBA;
+
+ if (bnx2i_setup_free_cid_que(hba))
+ goto cid_que_err;
+
+ /* SQ/RQ/CQ size can be changed via sysfx interface */
+ hba->max_sqes = BNX2I_SQ_WQES_DEFAULT;
+ hba->max_rqes = BNX2I_RQ_WQES_DEFAULT;
+ hba->max_cqes = BNX2I_CQ_WQES_DEFAULT;
+ hba->num_ccell = BNX2I_CCELLS_DEFAULT;
+
+ if (bnx2i_setup_mp_bdt(hba)) {
+ goto mp_bdt_err;
+ }
+
+ spin_lock_init(&hba->lock);
+ /* initialize timer and wait queue used for resource cleanup when
+ * interface is brought down */
+ init_timer(&hba->hba_timer);
+ init_waitqueue_head(&hba->eh_wait);
+
+ INIT_WORK(&hba->err_rec_task, conn_err_recovery_task);
+ hba->sess_recov_prod_idx = 0;
+ hba->sess_recov_cons_idx = 0;
+ hba->sess_recov_max_idx = 0;
+ hba->sess_recov_list =
+ (struct bnx2i_sess **)kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!hba->sess_recov_list)
+ goto rec_que_err;
+ hba->sess_recov_max_idx = PAGE_SIZE / sizeof (struct bnx2i_sess *) - 1;
+
+ bnx2i_init_ctx_dump_mem(hba);
+
+ return hba;
+
+rec_que_err:
+ bnx2i_free_mp_bdt(hba);
+mp_bdt_err:
+ bnx2i_release_free_cid_que(hba);
+cid_que_err:
+ bnx2i_free_ep_ofld_destroy_que(hba);
+ep_ofld_que_err:
+ bnx2i_free_hba(hba);
+
+ return NULL;
+}
+
+
+/*
+ * free adapter structure and call various cleanup routines.
+ */
+void bnx2i_free_hba(struct bnx2i_hba *hba)
+{
+ if (hba == NULL)
+ return;
+
+ bnx2i_free_ctx_dump_mem(hba);
+
+ bnx2i_free_mp_bdt(hba);
+ bnx2i_release_free_cid_que(hba);
+ bnx2i_free_ep_ofld_destroy_que(hba);
+
+ INIT_LIST_HEAD(&hba->active_sess);
+ /* Free memory held by hba structure */
+ kfree((void *)hba);
+}
+
+
+
+
+/*
+ * return all commands in active queue which should already have been
+ * cleaned up by the cnic device.
+ */
+static void bnx2i_flush_active_cmd_queue(struct bnx2i_sess *sess, int err_code)
+{
+ struct list_head *list;
+ struct list_head *tmp;
+ struct bnx2i_cmd *cmd;
+ unsigned long flags;
+ if (!sess->num_active_cmds)
+ return;
+
+ spin_lock_irqsave(sess->host->host_lock, flags);
+ list_for_each_safe(list, tmp, &sess->active_cmds) {
+ cmd = (struct bnx2i_cmd *) list;
+ cmd->req.itt &= ISCSI_CMD_RESPONSE_INDEX;
+ bnx2i_iscsi_unmap_sg_list(cmd);
+ cmd->cmd_state = ISCSI_CMD_STATE_COMPLETED;
+ list_del_init(&cmd->link);
+ bnx2i_return_failed_command(sess, cmd, err_code);
+ bnx2i_free_cmd(sess, cmd);
+ }
+ spin_unlock_irqrestore(sess->host->host_lock, flags);
+}
+
+
+/*
+ * initiate cleanup of outstanding commands for sess recovery
+ */
+static int bnx2i_session_recovery_start(struct bnx2i_sess *sess, int err_code)
+{
+ if (unlikely(!sess)) {
+ printk(KERN_ALERT "sess_recov_start: sess not active\n");
+ return FAILED;
+ }
+
+ if (!is_sess_active(sess)) {
+ wait_event_interruptible_timeout(sess->er_wait,
+ (sess->state ==
+ BNX2I_SESS_IN_FFP), HZ);
+ if (signal_pending(current))
+ flush_signals(current);
+ if (!is_sess_active(sess)) {
+ printk(KERN_ALERT "sess_reco: sess still not active\n");
+ sess->lead_conn->state = CONN_STATE_XPORT_FREEZE;
+ return FAILED;
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+/*
+ * SCSI host reset handler, which is translates to iSCSI session
+ * recovery
+ */
+int bnx2i_do_iscsi_sess_recovery(struct bnx2i_sess *sess, int err_code)
+{
+ struct bnx2i_hba *hba = NULL;
+ struct bnx2i_conn *conn = sess->lead_conn;
+
+ if (bnx2i_session_recovery_start(sess, err_code) != SUCCESS) {
+ printk(KERN_INFO "bnx2i: sess rec start returned error\n");
+ return FAILED;
+ }
+ hba = sess->hba;
+
+ sess->recovery_state = ISCSI_SESS_RECOVERY_OPEN_ISCSI;
+ iscsi_conn_error(conn->cls_conn, ISCSI_ERR_CONN_FAILED);
+
+ /* if session teardown is because of net interface down,
+ * no need to wait for complete recovery */
+ if (err_code == DID_NO_CONNECT) {
+ wait_event_interruptible_timeout(sess->er_wait,
+ !conn->ep,
+ msecs_to_jiffies(1000));
+ } else {
+ wait_event_interruptible(sess->er_wait,
+ ((sess->recovery_state &
+ ISCSI_SESS_RECOVERY_COMPLETE) ||
+ (sess->recovery_state &
+ ISCSI_SESS_RECOVERY_FAILED)));
+ }
+
+ if (signal_pending(current))
+ flush_signals(current);
+
+ if (err_code == DID_NO_CONNECT)
+ return SUCCESS;
+
+ if (sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE) {
+ printk(KERN_INFO "bnx2i: host #%d reset succeeded\n",
+ sess->host->host_no);
+ sess->state = BNX2I_SESS_IN_FFP;
+ } else {
+ return FAILED;
+ }
+ sess->recovery_state = 0;
+ return SUCCESS;
+}
+
+
+/*
+ * free up resources held by this session
+ */
+int bnx2i_iscsi_sess_release(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
+{
+ if (!sess)
+ return 0;
+
+ bnx2i_release_free_itt_queue(sess);
+ bnx2i_free_cmd_pool(sess);
+ bnx2i_free_bd_table_pool(sess);
+ bnx2i_free_all_bdt_resc_pages(sess);
+
+ list_del_init(&sess->link);
+ hba->num_active_sess--;
+
+ return 0;
+}
+
+
+/*
+ * initialize various per session statistic counters
+ */
+static void bnx2i_init_iscsi_sess_stats(struct bnx2i_sess *sess)
+{
+ if (!sess)
+ return;
+
+ sess->violation_notified = 0;
+
+ sess->total_data_octets_sent = 0;
+ sess->total_data_octets_rcvd = 0;
+ sess->conn_login_ok = 0;
+ sess->conn_login_failed = 0;
+ sess->num_login_req_pdus = 0;
+ sess->num_login_resp_pdus = 0;
+ sess->num_scsi_cmd_pdus = 0;
+ sess->num_scsi_resp_pdus = 0;
+ sess->num_nopout_pdus = 0;
+ sess->num_nopin_pdus = 0;
+ sess->num_reject_pdus = 0;
+ sess->num_async_pdus = 0;
+ sess->num_dataout_pdus = 0;
+ sess->num_r2t_pdus = 0;
+ sess->num_datain_pdus = 0;
+ sess->num_snack_pdus = 0;
+ sess->num_text_req_pdus = 0;
+ sess->num_text_resp_pdus = 0;
+ sess->num_tmf_req_pdus = 0;
+ sess->num_tmf_resp_pdus = 0;
+ sess->num_logout_req_pdus = 0;
+ sess->num_logout_resp_pdus = 0;
+}
+
+
+/*
+ * set iSCSI parameter values to defaults, as defined in rfc3720
+ */
+static void bnx2i_sess_set_param_defaults(struct bnx2i_sess *sess)
+{
+ sess->initial_r2t = ISCSI_DEFAULT_INITIAL_R2T;
+ sess->max_r2t = ISCSI_DEFAULT_MAX_OUTSTANDING_R2T;
+ sess->imm_data = ISCSI_DEFAULT_IMMEDIATE_DATA;
+ sess->first_burst_len = ISCSI_DEFAULT_FIRST_BURST_LENGTH;
+ sess->max_burst_len = ISCSI_DEFAULT_MAX_BURST_LENGTH;
+ sess->time2wait = 2;
+ sess->time2retain = 20;
+}
+
+
+/*
+ * initialize session structure elements and allocate per sess resources
+ */
+int bnx2i_iscsi_sess_new(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
+{
+ int rc;
+
+ spin_lock(&hba->lock);
+ list_add_tail(&sess->link, &hba->active_sess);
+ hba->num_active_sess++;
+ spin_unlock(&hba->lock);
+
+ sess->sq_size = hba->max_sqes;
+ sess->tsih = 0;
+ sess->lead_conn = NULL;
+
+ spin_lock_init(&sess->lock);
+
+ /* initialize active connection list */
+ INIT_LIST_HEAD(&sess->conn_list);
+ INIT_LIST_HEAD(&sess->free_cmds);
+
+ INIT_LIST_HEAD(&sess->active_cmds);
+ sess->num_active_cmds = 0;
+
+ sess->num_active_conn = 0;
+ sess->max_conns = 1;
+ sess->conn_id = 0;
+ sess->target_name = NULL;
+
+ sess->state = BNX2I_SESS_INITIAL;
+ sess->recovery_state = 0;
+
+ if (bnx2i_alloc_bd_table_pool(sess) != 0) {
+ printk(KERN_ERR "sess_new: unable to alloc bd table pool\n");
+ rc = -ENOMEM;
+ goto err_bd_pool;
+ }
+
+ if (bnx2i_alloc_cmd_pool(sess) != 0) {
+ printk(KERN_ERR "sess_new: alloc cmd pool failed\n");
+ rc = -ENOMEM;
+ goto err_cmd_pool;
+ }
+
+ rc = bnx2i_setup_free_itt_queue(sess);
+ if (rc) {
+ rc = -ENOMEM;
+ goto err_itt_que;
+ }
+
+ init_timer(&sess->abort_timer);
+ init_waitqueue_head(&sess->er_wait);
+ init_timer(&sess->poll_timer);
+
+ bnx2i_init_iscsi_sess_stats(sess);
+ bnx2i_sess_set_param_defaults(sess);
+
+ return 0;
+
+err_itt_que:
+ bnx2i_free_cmd_pool(sess);
+err_cmd_pool:
+ bnx2i_free_bd_table_pool(sess);
+err_bd_pool:
+ return rc;
+}
+
+
+/*
+ * Login related resources is freed in this routine.
+ */
+void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn)
+{
+ if (conn->gen_pdu.resp_bd_tbl) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ conn->gen_pdu.resp_bd_tbl,
+ conn->gen_pdu.resp_bd_dma);
+ conn->gen_pdu.resp_bd_tbl = NULL;
+ }
+
+ if (conn->gen_pdu.req_bd_tbl) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ conn->gen_pdu.req_bd_tbl,
+ conn->gen_pdu.req_bd_dma);
+ conn->gen_pdu.req_bd_tbl = NULL;
+ }
+
+ if (conn->gen_pdu.resp_buf) {
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.resp_buf,
+ conn->gen_pdu.resp_dma_addr);
+ conn->gen_pdu.resp_buf = NULL;
+ }
+
+ if (conn->gen_pdu.req_buf) {
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.req_buf,
+ conn->gen_pdu.req_dma_addr);
+ conn->gen_pdu.req_buf = NULL;
+ }
+}
+
+
+/*
+ * Login & nop-in related resources is allocated in this routine.
+ */
+static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn)
+{
+ /* Allocate memory for login request/response buffers */
+ conn->gen_pdu.req_buf =
+ (char *) pci_alloc_consistent(hba->pci_dev,
+ ISCSI_CONN_LOGIN_BUF_SIZE,
+ &conn->gen_pdu.req_dma_addr);
+ if (conn->gen_pdu.req_buf == NULL)
+ goto login_req_buf_failure;
+
+ conn->gen_pdu.req_buf_size = 0;
+ conn->gen_pdu.req_wr_ptr = conn->gen_pdu.req_buf;
+
+ conn->gen_pdu.resp_buf =
+ (char *) pci_alloc_consistent(hba->pci_dev,
+ ISCSI_CONN_LOGIN_BUF_SIZE,
+ &conn->gen_pdu.resp_dma_addr);
+ if (conn->gen_pdu.resp_buf == NULL)
+ goto login_resp_buf_failure;
+
+ conn->gen_pdu.resp_buf_size = ISCSI_CONN_LOGIN_BUF_SIZE;
+ conn->gen_pdu.resp_wr_ptr = conn->gen_pdu.resp_buf;
+
+ conn->gen_pdu.req_bd_tbl =
+ (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
+ &conn->gen_pdu.req_bd_dma);
+ if (conn->gen_pdu.req_bd_tbl == NULL)
+ goto login_req_bd_tbl_failure;
+
+ conn->gen_pdu.resp_bd_tbl =
+ (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
+ &conn->gen_pdu.resp_bd_dma);
+ if (conn->gen_pdu.resp_bd_tbl == NULL)
+ goto login_resp_bd_tbl_failure;
+
+ return 0;
+
+login_resp_bd_tbl_failure:
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE, conn->gen_pdu.req_bd_tbl,
+ conn->gen_pdu.req_bd_dma);
+ conn->gen_pdu.req_bd_tbl = NULL;
+
+login_req_bd_tbl_failure:
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.resp_buf,
+ conn->gen_pdu.resp_dma_addr);
+ conn->gen_pdu.resp_buf = NULL;
+login_resp_buf_failure:
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.req_buf, conn->gen_pdu.req_dma_addr);
+ conn->gen_pdu.req_buf = NULL;
+login_req_buf_failure:
+ printk(KERN_ERR "bnx2i:a conn login resource alloc failed!!\n");
+ return -ENOMEM;
+
+}
+
+
+/*
+ * connection structure is initialized in this routine.
+ */
+int bnx2i_iscsi_conn_new(struct bnx2i_sess *sess, struct bnx2i_conn *conn)
+{
+ int ret_code = 0;
+ struct bnx2i_hba *hba = sess->hba;
+
+ if (!sess || !conn || !hba)
+ return -EINVAL;
+
+ conn->sess = sess;
+ conn->header_digest_en = 0;
+ conn->data_digest_en = 0;
+
+ spin_lock_init(&conn->lock);
+
+ init_timer(&conn->poll_timer);
+ conn->gen_pdu.cmd = NULL;
+
+ /* 'ep' ptr will be assigned in bind() call */
+ conn->ep = NULL;
+
+ ret_code = bnx2i_conn_alloc_login_resources(hba, conn);
+ if (ret_code != 0) {
+ printk(KERN_ALERT "conn_new: login resc alloc failed!!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+
+/*
+ * extract & update SN counters from login response
+ */
+static int bnx2i_login_resp_update_cmdsn(struct bnx2i_conn *conn)
+{
+ u32 max_cmdsn;
+ u32 exp_cmdsn;
+ u32 stat_sn;
+ struct bnx2i_sess *sess = conn->sess;
+ struct iscsi_nopin *hdr = NULL;
+
+ hdr = (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
+
+ max_cmdsn = ntohl(hdr->max_cmdsn);
+ exp_cmdsn = ntohl(hdr->exp_cmdsn);
+ stat_sn = ntohl(hdr->statsn);
+#define SN_DELTA_ISLAND 0xffff
+ if (max_cmdsn < exp_cmdsn -1 &&
+ max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND)
+ return -EINVAL;
+
+ if (max_cmdsn > sess->max_cmdsn ||
+ max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
+ sess->max_cmdsn = max_cmdsn;
+
+ if (exp_cmdsn > sess->exp_cmdsn ||
+ exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
+ sess->exp_cmdsn = exp_cmdsn;
+ }
+ if (stat_sn == conn->exp_statsn)
+ conn->exp_statsn++;
+
+ return 0;
+}
+
+
+/*
+ * update iSCSI SN counters for the given session
+ */
+void bnx2i_update_cmd_sequence(struct bnx2i_sess *sess,
+ u32 exp_sn, u32 max_sn)
+{
+ u32 exp_cmdsn = exp_sn;
+ u32 max_cmdsn = max_sn;
+
+ if (max_cmdsn < exp_cmdsn -1 &&
+ max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND) {
+ printk(KERN_ALERT "cmd_sequence: error, exp 0x%x, max 0x%x\n",
+ exp_cmdsn, max_cmdsn);
+ BUG_ON(1);
+ }
+ if (max_cmdsn > sess->max_cmdsn ||
+ max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
+ sess->max_cmdsn = max_cmdsn;
+ if (exp_cmdsn > sess->exp_cmdsn ||
+ exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
+ sess->exp_cmdsn = exp_cmdsn;
+ }
+
+ return;
+}
+
+
+/*
+ * This function propogates SCSI response to SCSI-ML by calling
+ * scsi_done() and also returns command struct back to free pool
+ */
+int bnx2i_process_scsi_resp(struct bnx2i_cmd *cmd)
+{
+ int ret = 0;
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct Scsi_Host *host;
+ int res_count = 0;
+
+ if (!sc)
+ return 0;
+
+ host = cmd->conn->sess->host;
+ sc->result = (DID_OK << 16) | cmd->scsi_status;
+
+ if (cmd->iscsi_resp != ISCSI_STATUS_CMD_COMPLETED) {
+ sc->result = (DID_ERROR << 16);
+ goto call_scsi_done;
+ }
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ goto call_scsi_done;
+ }
+
+ if (cmd->scsi_uflow) {
+ res_count = cmd->resi_len;
+ if (res_count > 0 && res_count <= sc->request_bufflen)
+ sc->resid = res_count;
+ else
+ sc->result = (DID_BAD_TARGET << 16) |
+ cmd->scsi_status;
+ } else if (cmd->scsi_oflow) {
+ sc->resid = res_count;
+ }
+
+call_scsi_done:
+ if ((cmd->cmd_state == ISCSI_CMD_STATE_ABORT_PEND) ||
+ (cmd->cmd_state == ISCSI_CMD_STATE_CLEANUP_PEND)) {
+ printk(KERN_ALERT "scsi_resp: command is being aborted\n");
+ return -1;
+ }
+
+ spin_lock(host->host_lock);
+ cmd->scsi_cmd = NULL;
+ cmd->conn->sess->num_active_cmds--;
+ sc->scsi_done(sc);
+ bnx2i_free_cmd(cmd->conn->sess, cmd);
+ spin_unlock(host->host_lock);
+ return ret;
+}
+
+
+
+/*
+ * login response PDU is pushed to application daemon by
+ * calling iscsi_recv_pdu()
+ */
+int bnx2i_indicate_login_resp(struct bnx2i_conn *conn)
+{
+ int ret = 0;
+ int data_len = 0;
+ struct iscsi_login_rsp *login_resp =
+ (struct iscsi_login_rsp *) &conn->gen_pdu.resp_hdr;
+
+ /* check if this is the first login response for this connection.
+ * If yes, we need to copy initial StatSN to connection structure.
+ */
+ if (conn->exp_statsn == STATSN_UPDATE_SIGNATURE) {
+ conn->exp_statsn = ntohl(login_resp->statsn) + 1;
+ }
+
+ ret = bnx2i_login_resp_update_cmdsn(conn);
+ if (ret != 0) {
+ return -EINVAL;
+ }
+
+ data_len = conn->gen_pdu.resp_wr_ptr - conn->gen_pdu.resp_buf;
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) login_resp,
+ (char *) conn->gen_pdu.resp_buf, data_len);
+
+ return 0;
+}
+
+
+/*
+ * deliver logout response PDU to application daemon
+ */
+int bnx2i_indicate_logout_resp(struct bnx2i_conn *conn)
+{
+ struct iscsi_logout_rsp *logout_resp =
+ (struct iscsi_logout_rsp *) &conn->gen_pdu.resp_hdr;
+
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) logout_resp,
+ (char *) NULL, 0);
+
+ return 0;
+}
+
+
+/*
+ * deliver iSCSI async PDU to user daemon
+ */
+int bnx2i_indicate_async_mesg(struct bnx2i_conn *conn)
+{
+ struct iscsi_async *async_msg =
+ (struct iscsi_async *) &conn->gen_pdu.resp_hdr;
+
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) async_msg,
+ (char *) NULL, 0);
+
+ return 0;
+}
+
+
+
+/*
+ * Function : bnx2i_process_nopin
+ */
+int bnx2i_process_nopin(struct bnx2i_conn *conn, struct bnx2i_cmd *cmd,
+ char *data_buf, int data_len)
+{
+ struct iscsi_nopin *nopin_msg =
+ (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
+
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) nopin_msg,
+ (char *) data_buf, data_len);
+
+ spin_lock(conn->sess->host->host_lock);
+ list_del_init(&cmd->link);
+ bnx2i_free_cmd(cmd->conn->sess, cmd);
+ spin_unlock(conn->sess->host->host_lock);
+
+ return 0;
+}
+
+
+
+/*
+ * Allocates buffers and BD tables before shipping requests to cnic
+ * for PDUs prepared by 'iscsid' daemon
+ */
+static void bnx2i_iscsi_prep_generic_pdu_bd(struct bnx2i_conn *conn)
+{
+ struct iscsi_bd *bd_tbl = NULL;
+
+ bd_tbl = (struct iscsi_bd *) conn->gen_pdu.req_bd_tbl;
+
+ bd_tbl->buffer_addr_hi =
+ (u32) ((u64) conn->gen_pdu.req_dma_addr >> 32);
+ bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.req_dma_addr;
+ bd_tbl->buffer_length = conn->gen_pdu.req_wr_ptr -
+ conn->gen_pdu.req_buf;
+ bd_tbl->reserved0 = 0;
+ bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+ bd_tbl = (struct iscsi_bd *) conn->gen_pdu.resp_bd_tbl;
+ bd_tbl->buffer_addr_hi = (u64) conn->gen_pdu.resp_dma_addr >> 32;
+ bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.resp_dma_addr;
+ bd_tbl->buffer_length = ISCSI_CONN_LOGIN_BUF_SIZE;
+ bd_tbl->reserved0 = 0;
+ bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+}
+
+
+
+/*
+ * called to transmit PDUs prepared by the 'iscsid' daemon. iSCSI login,
+ * Nop-out and Logout requests flow through this path.
+ */
+static int bnx2i_iscsi_send_generic_request(struct bnx2i_cmd *cmnd)
+{
+ int rc = 0;
+ char *buf = NULL;
+ int data_len = 0;
+ struct bnx2i_conn *conn = cmnd->conn;
+
+ bnx2i_iscsi_prep_generic_pdu_bd(conn);
+ switch (cmnd->iscsi_opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_LOGIN:
+ bnx2i_send_iscsi_login(conn, cmnd);
+ break;
+
+ case ISCSI_OP_NOOP_OUT:
+ data_len = conn->gen_pdu.req_buf_size;
+ buf = conn->gen_pdu.req_buf;
+ if (data_len)
+ rc = bnx2i_send_iscsi_nopout(conn, cmnd,
+ ISCSI_RESERVED_TAG,
+ buf, data_len, 1);
+ else
+ rc = bnx2i_send_iscsi_nopout(conn, cmnd,
+ ISCSI_RESERVED_TAG,
+ NULL, 0, 1);
+ break;
+
+ case ISCSI_OP_LOGOUT:
+ rc = bnx2i_send_iscsi_logout(conn, cmnd);
+ break;
+
+ default:
+ printk(KERN_ALERT "send_gen: unsupported op 0x%x\n",
+ cmnd->iscsi_opcode);
+ }
+ return rc;
+}
+
+
+/**********************************************************************
+ * SCSI-ML Interface
+ **********************************************************************/
+
+static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc,
+ struct bnx2i_cmd *cmd)
+{
+ u32 dword;
+ int lpcnt = 0;
+ u8 *srcp = NULL;
+ u32 *dstp = NULL;
+ u32 scsi_lun[2];
+
+ int_to_scsilun(sc->device->lun, (struct scsi_lun *) scsi_lun);
+ cmd->req.lun[0] = ntohl(scsi_lun[0]);
+ cmd->req.lun[1] = ntohl(scsi_lun[1]);
+
+ lpcnt = cmd->scsi_cmd->cmd_len / sizeof(dword);
+ srcp = (u8 *) sc->cmnd;
+ dstp = (u32 *) cmd->req.cdb;
+ while (lpcnt--) {
+ memcpy(&dword, srcp, 4);
+ *dstp = cpu_to_be32(dword);
+ srcp += 4;
+ dstp++;
+ }
+ if (sc->cmd_len & 0x3) {
+ dword = (u32) srcp[0] | ((u32) srcp[1] << 8);
+ *dstp = cpu_to_be32(dword);
+ }
+}
+
+
+
+/*
+ * handles SCSI command queued by SCSI-ML, allocates a command structure,
+ * assigning CMDSN, mapping SG buffers and handing over request to CNIC.
+ */
+int bnx2i_queuecommand(struct scsi_cmnd *sc,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct Scsi_Host *shost;
+ struct bnx2i_sess *sess = NULL;
+ struct bnx2i_conn *conn = NULL;
+ struct bnx2i_cmd *cmd = NULL;
+ struct bnx2i_hba *hba = NULL;
+ static int old_recovery_state = 0;
+
+ sc->scsi_done = done;
+ sc->result = 0;
+ shost = sc->device->host;
+ sess = iscsi_hostdata(shost->hostdata);
+ BUG_ON(shost != sess->host);
+
+ if (sess) {
+ hba = sess->hba;
+ } else {
+ printk(KERN_ALERT "bnx2i: quecmd: Error dev not found \n");
+ goto dev_not_found;
+ }
+
+#define iscsi_cmd_win_closed(_sess) \
+ ((int) (_sess->max_cmdsn - _sess->cmdsn) < 0)
+
+ if (iscsi_cmd_win_closed(sess)) {
+ goto iscsi_win_closed;
+ }
+
+ if ((sess->state & BNX2I_SESS_IN_SHUTDOWN) ||
+ (sess->state & BNX2I_SESS_IN_LOGOUT)) {
+ goto dev_not_found;
+ }
+
+ if (sess->recovery_state) {
+ if (old_recovery_state != sess->recovery_state) {
+ old_recovery_state = sess->recovery_state;
+ }
+
+ if (sess->recovery_state & ISCSI_SESS_RECOVERY_FAILED)
+ goto dev_not_found;
+ else if (!(sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE))
+ goto iscsi_win_closed;
+ else
+ sess->recovery_state = 0;
+ }
+
+ cmd = bnx2i_alloc_cmd(sess);
+ if (cmd == NULL) {
+ /* This should never happen as cmd list size == SHT->can_queue
+ */
+ goto cmd_not_accepted;
+ }
+
+ cmd->conn = conn = sess->lead_conn;
+ cmd->scsi_cmd = sc;
+ cmd->req.total_data_transfer_length = sc->request_bufflen;
+ cmd->iscsi_opcode = ISCSI_OPCODE_SCSI_CMD;
+ cmd->req.cmd_sn = sess->cmdsn++;
+
+ bnx2i_iscsi_map_sg_list(cmd);
+ bnx2i_cpy_scsi_cdb(sc, cmd);
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ cmd->req.op_attr = ISCSI_CMD_REQUEST_WRITE;
+ cmd->req.itt |= (ISCSI_TASK_TYPE_WRITE <<
+ ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ bnx2i_setup_write_cmd_bd_info(cmd);
+ } else {
+ cmd->req.op_attr = ISCSI_CMD_REQUEST_READ;
+ cmd->req.itt |= (ISCSI_TASK_TYPE_READ <<
+ ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ }
+ cmd->req.num_bds = cmd->bd_tbl->bd_valid;
+ if (!cmd->bd_tbl->bd_valid) {
+ cmd->req.bd_list_addr_lo = (u32) hba->mp_bd_dma;
+ cmd->req.bd_list_addr_hi =
+ (u32) ((u64) hba->mp_bd_dma >> 32);
+ cmd->req.num_bds = 1;
+ }
+
+ cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
+ sc->SCp.ptr = (char *) cmd;
+
+ if (cmd->req.itt != ITT_INVALID_SIGNATURE) {
+ bnx2i_send_iscsi_scsicmd(conn, cmd);
+ list_add_tail(&cmd->link, &sess->active_cmds);
+ sess->num_active_cmds++;
+ }
+ return 0;
+
+iscsi_win_closed:
+cmd_not_accepted:
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+dev_not_found:
+ sc->sense_buffer[0] = 0x70;
+ sc->sense_buffer[2] = NOT_READY;
+ sc->sense_buffer[7] = 0x6;
+ sc->sense_buffer[12] = 0x08;
+ sc->sense_buffer[13] = 0x00;
+ sc->result = (DID_NO_CONNECT << 16);
+ sc->resid = sc->request_bufflen;
+ sc->scsi_done(sc);
+ return 0;
+}
+
+
+
+/*
+ * TMF request timeout handler
+ */
+static void bnx2i_iscsi_tmf_timer(unsigned long data)
+{
+ struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) data;
+
+ printk(KERN_ALERT "TMF timer: abort failed, cmd 0x%p\n", cmd);
+ cmd->cmd_state = ISCSI_CMD_STATE_FAILED;
+ wake_up(&cmd->conn->sess->er_wait);
+}
+
+
+/*
+ * initiate command abort process by requesting CNIC to send
+ * an iSCSI TMF request to target
+ */
+static int bnx2i_initiate_abort_cmd(struct scsi_cmnd *sc)
+{
+ struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
+ struct bnx2i_cmd *tmf_cmd = NULL;
+ struct Scsi_Host *shost = cmd->scsi_cmd->device->host;
+ struct bnx2i_conn *conn = cmd->conn;
+ struct bnx2i_sess *sess = NULL;
+ struct bnx2i_hba *hba = NULL;
+
+ shost = cmd->scsi_cmd->device->host;
+ sess = iscsi_hostdata(shost->hostdata);
+ BUG_ON(shost != sess->host);
+
+ if (sess && (is_sess_active(sess))) {
+ hba = sess->hba;
+ } else {
+ return FAILED;
+ }
+
+ bnx2i_setup_ictx_dump(hba, conn);
+
+ if (cmd->scsi_cmd != sc) {
+ /* command already completed to scsi mid-layer */
+ goto cmd_not_active;
+ }
+
+ tmf_cmd = bnx2i_alloc_cmd(sess);
+ if (cmd == NULL) {
+ goto lack_of_resc;
+ }
+
+ tmf_cmd->conn = conn = sess->lead_conn;
+ tmf_cmd->scsi_cmd = NULL;
+ tmf_cmd->iscsi_opcode = ISCSI_OPCODE_TMF_REQUEST;
+ tmf_cmd->req.cmd_sn = sess->cmdsn;
+ tmf_cmd->tmf_ref_itt = cmd->req.itt;
+ tmf_cmd->tmf_ref_cmd = cmd;
+ tmf_cmd->tmf_ref_sc = cmd->scsi_cmd;
+ cmd->cmd_state = ISCSI_CMD_STATE_ABORT_PEND;
+ tmf_cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
+
+ sess->abort_timer.expires = 10*HZ + jiffies;
+ sess->abort_timer.function = bnx2i_iscsi_tmf_timer;
+ sess->abort_timer.data = (unsigned long)tmf_cmd;
+ add_timer(&sess->abort_timer);
+
+ bnx2i_send_iscsi_tmf(conn, tmf_cmd);
+
+ /* update iSCSI context for this conn, wait for CNIC to complete */
+ wait_event_interruptible(sess->er_wait,
+ tmf_cmd->cmd_state != ISCSI_CMD_STATE_INITIATED);
+
+ if (signal_pending(current))
+ flush_signals(current);
+
+ del_timer_sync(&sess->abort_timer);
+
+ if (tmf_cmd->cmd_state == ISCSI_CMD_STATE_FAILED) {
+ printk(KERN_ALERT "abort: abort failed, cmd 0x%p\n", tmf_cmd);
+ /* TMF timed out, return error status and let SCSI-ML do
+ * session recovery.
+ */
+ list_del_init(&tmf_cmd->link);
+ bnx2i_free_cmd(sess, tmf_cmd);
+ return FAILED;
+ }
+
+ list_del_init(&tmf_cmd->link);
+ bnx2i_free_cmd(sess, tmf_cmd);
+
+ if ((cmd->scsi_cmd->result & 0xFF0000) == (DID_ABORT << 16)) {
+ cmd->cmd_state = ISCSI_CMD_STATE_CLEANUP_PEND;
+ bnx2i_send_cmd_cleanup_req(hba, cmd);
+ wait_event_interruptible_timeout(sess->er_wait,
+ (cmd->cmd_state ==
+ ISCSI_CMD_STATE_CLEANUP_CMPL),
+ msecs_to_jiffies(
+ ISCSI_CMD_CLEANUP_TIMEOUT));
+
+ if (signal_pending(current))
+ flush_signals(current);
+ } else {
+ cmd->scsi_cmd->result = (DID_ABORT << 16);
+ }
+ cmd->conn->sess->num_active_cmds--;
+ list_del_init(&cmd->link);
+ cmd->scsi_cmd = NULL;
+ bnx2i_free_cmd(cmd->conn->sess, cmd);
+
+cmd_not_active:
+ return SUCCESS;
+
+lack_of_resc:
+ return FAILED;
+}
+
+
+/*
+ * SCSI abort request handler.
+ */
+int bnx2i_abort(struct scsi_cmnd *sc)
+{
+ int reason;
+ struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
+
+ if (unlikely(!cmd)) {
+ /* command already completed to scsi mid-layer */
+ printk(KERN_INFO "bnx2i_abort: sc 0x%p, not active\n", sc);
+ return SUCCESS;
+ }
+
+ reason = bnx2i_initiate_abort_cmd(sc);
+ return reason;
+}
+
+
+
+/*
+ * hardware reset
+ */
+int bnx2i_reset(struct scsi_cmnd *sc)
+{
+ return 0;
+}
+
+
+void bnx2i_return_failed_command(struct bnx2i_sess *sess,
+ struct bnx2i_cmd *cmd, int err_code)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ sc->result = err_code << 16;
+ sc->resid = cmd->scsi_cmd->request_bufflen;
+ cmd->scsi_cmd = NULL;
+ sess->num_active_cmds--;
+ sc->scsi_done(sc);
+}
+
+
+
+/*
+ * SCSI host reset handler - iSCSI session recovery
+ */
+int bnx2i_host_reset(struct scsi_cmnd *sc)
+{
+ struct Scsi_Host *shost = sc->device->host;
+ struct bnx2i_sess *sess = NULL;
+ int rc = 0;
+
+ shost = sc->device->host;
+ sess = iscsi_hostdata(shost->hostdata);
+ printk(KERN_INFO "bnx2i: attempting to reset host, #%d\n",
+ sess->host->host_no);
+
+ BUG_ON(shost != sess->host);
+ rc = bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
+
+ return rc;
+}
+
+
+
+/**********************************************************************
+ * open-iscsi interface
+ **********************************************************************/
+
+
+#define get_bnx2_device(_hba, _devc) do { \
+ if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5706) || \
+ (_hba->pci_did == PCI_DEVICE_ID_NX2_5706S)) { \
+ _devc = '6'; \
+ } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5708) || \
+ (_hba->pci_did == PCI_DEVICE_ID_NX2_5708S)) { \
+ _devc = '8'; \
+ } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5709) || \
+ (_hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) { \
+ _devc = '9'; \
+ } \
+ } while (0)
+
+/* from open-iscsi project */
+/*
+ * iSCSI Session's hostdata organization:
+ *
+ * *------------------* <== hostdata_session(host->hostdata)
+ * | ptr to class sess|
+ * |------------------| <== iscsi_hostdata(host->hostdata)
+ * | iscsi_session |
+ * *------------------*
+ */
+
+#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \
+ _sz % sizeof(unsigned long))
+
+#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
+
+#define session_to_cls(_sess) hostdata_session(_sess->host->hostdata)
+
+
+
+
+/*
+ * Function: bnx2i_register_xport
+ * Description: this routine will allocate memory for SCSI host template,
+ * iSCSI template and registers one instance of NX2 device with
+ * iSCSI Transport Kernel module.
+ */
+int bnx2i_register_xport(struct bnx2i_hba *hba)
+{
+ void *mem_ptr = NULL;
+ char dev_id = '8';
+
+ if (!hba)
+ return -EINVAL;
+
+ get_bnx2_device(hba, dev_id);
+
+ mem_ptr = kmalloc(sizeof(struct scsi_host_template), GFP_KERNEL);
+ hba->scsi_template = (struct scsi_host_template *) mem_ptr;
+ if (hba->scsi_template == NULL) {
+ printk(KERN_ALERT "bnx2i: failed to alloc memory for sht\n");
+ return -ENOMEM;
+ }
+
+ mem_ptr = kmalloc(sizeof(struct iscsi_transport), GFP_KERNEL);
+ hba->iscsi_transport = (struct iscsi_transport *) mem_ptr;
+ if (hba->iscsi_transport == NULL) {
+ printk(KERN_ALERT "mem error for iscsi_transport template\n");
+ goto iscsi_xport_err;
+ }
+
+ mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ALERT "failed to alloc memory for xport name\n");
+ goto scsi_name_mem_err;
+ }
+
+ memcpy((void *) hba->scsi_template,
+ (const void *) &bnx2i_host_template,
+ sizeof(struct scsi_host_template));
+ hba->scsi_template->name = mem_ptr;
+ memcpy((void *) hba->scsi_template->name,
+ (const void *) bnx2i_host_template.name,
+ strlen(bnx2i_host_template.name) + 1);
+
+ mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ALERT "failed to alloc proc name mem\n");
+ goto scsi_proc_name_mem_err;
+ }
+ hba->scsi_template->proc_name = mem_ptr;
+
+ memcpy((void *) hba->iscsi_transport,
+ (const void *) &bnx2i_iscsi_transport,
+ sizeof(struct iscsi_transport));
+
+ hba->iscsi_transport->host_template = hba->scsi_template;
+
+ mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ALERT "mem alloc error, iscsi xport name\n");
+ goto xport_name_mem_err;
+ }
+ hba->iscsi_transport->name = mem_ptr;
+ sprintf(mem_ptr, "%s%c-%.2x%.2x%.2x", BRCM_ISCSI_XPORT_NAME_PREFIX,
+ dev_id, (u8)hba->pci_dev->bus->number,
+ hba->pci_devno, (u8)hba->pci_func);
+
+ memcpy((void *)hba->scsi_template->proc_name,
+ (const void *)mem_ptr, strlen(mem_ptr) + 1);
+
+ hba->shost_template = iscsi_register_transport(hba->iscsi_transport);
+ if (!hba->shost_template) {
+ printk(KERN_ALERT "bnx2i: xport reg failed, hba 0x%p\n", hba);
+ goto failed_registration;
+ }
+ printk(KERN_ALERT "bnx2i: netif=%s, iscsi=%s\n",
+ hba->netdev->name, hba->scsi_template->proc_name);
+ return 0;
+
+failed_registration:
+ kfree(hba->iscsi_transport->name);
+xport_name_mem_err:
+ kfree(hba->scsi_template->proc_name);
+scsi_proc_name_mem_err:
+ kfree(hba->scsi_template->name);
+scsi_name_mem_err:
+ kfree(hba->iscsi_transport);
+iscsi_xport_err:
+ kfree(hba->scsi_template);
+ printk(KERN_ALERT "register iscsi xport failed, hba 0x%p\n", hba);
+ return -ENOMEM;
+}
+
+
+/*
+ * Function: bnx2i_deregister_xport
+ * Description: this routine will de-allocate memory for SCSI host template,
+ * iSCSI template and de-registers a NX2 device instance
+ */
+int bnx2i_deregister_xport(struct bnx2i_hba *hba)
+{
+ if (!hba)
+ return -EINVAL;
+
+ iscsi_unregister_transport(hba->iscsi_transport);
+ hba->shost_template = NULL;
+
+ if (hba->scsi_template->name) {
+ kfree(hba->scsi_template->name);
+ hba->scsi_template->name = NULL;
+ }
+ if (hba->scsi_template) {
+ kfree(hba->scsi_template);
+ hba->scsi_template = NULL;
+ }
+ if (hba->iscsi_transport->name) {
+ kfree(hba->iscsi_transport->name);
+ hba->iscsi_transport->name = NULL;
+ }
+ if (hba->iscsi_transport) {
+ kfree(hba->iscsi_transport);
+ hba->iscsi_transport = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * Function: bnx2i_session_create
+ * Description: Creates a new iSCSI session instance on given device.
+ */
+struct iscsi_cls_session *
+ bnx2i_session_create(struct iscsi_transport *it,
+ struct scsi_transport_template *scsit,
+ uint16_t cmds_max, uint16_t qdepth,
+ uint32_t initial_cmdsn, uint32_t *host_no)
+{
+ struct bnx2i_hba *hba = NULL;
+ struct bnx2i_sess *sess = NULL;
+ struct Scsi_Host *shost;
+ struct iscsi_cls_session *cls_session;
+ int ret_code = 0;
+
+ hba = bnx2i_get_hba_from_template(scsit);
+ if (bnx2i_adapter_ready(hba))
+ return NULL;
+
+ shost = scsi_host_alloc(hba->iscsi_transport->host_template,
+ hostdata_privsize(sizeof(struct bnx2i_sess)));
+ if (!shost)
+ return NULL;
+
+ shost->max_id = 1;
+ shost->max_channel = 1;
+ shost->max_lun = hba->iscsi_transport->max_lun;
+ shost->max_cmd_len = hba->iscsi_transport->max_cmd_len;
+ if (cmds_max)
+ shost->can_queue = cmds_max;
+ if (qdepth)
+ shost->cmd_per_lun = qdepth;
+ shost->transportt = scsit;
+ *host_no = shost->host_no;
+ sess = iscsi_hostdata(shost->hostdata);
+
+ if (!sess)
+ goto sess_resc_fail;
+
+ memset(sess, 0, sizeof(struct bnx2i_sess));
+ sess->hba = hba;
+ sess->host = shost;
+
+ /*
+ * For Open-iSCSI, only normal sessions go through bnx2i.
+ * Discovery session goes through host stack TCP/IP stack.
+ */
+ ret_code = bnx2i_iscsi_sess_new(hba, sess);
+ if (ret_code) {
+ /*
+ * failed to allocate memory
+ */
+ printk(KERN_ALERT "bnx2i_sess_create: unable to alloc sess\n");
+ goto sess_resc_fail;
+ }
+
+ /*
+ * Update CmdSN related parameters
+ */
+ sess->cmdsn = initial_cmdsn;
+ sess->exp_cmdsn = initial_cmdsn + 1;
+ sess->max_cmdsn = initial_cmdsn + 1;
+
+ if (scsi_add_host(shost, NULL))
+ goto add_sh_fail;
+
+ if (!try_module_get(it->owner))
+ goto cls_sess_falied;
+
+ cls_session = iscsi_create_session(shost, it, 0);
+ if (!cls_session)
+ goto module_put;
+ *(unsigned long *)shost->hostdata = (unsigned long)cls_session;
+
+ return hostdata_session(shost->hostdata);
+
+module_put:
+ module_put(it->owner);
+cls_sess_falied:
+ scsi_remove_host(shost);
+add_sh_fail:
+ bnx2i_iscsi_sess_release(hba, sess);
+sess_resc_fail:
+ scsi_host_put(shost);
+ return NULL;
+}
+
+
+/*
+ * Function: bnx2i_session_destroy
+ * Description: Destroys previously created iSCSI session instance.
+ */
+void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+ struct module *owner = cls_session->transport->owner;
+
+ if (sess) {
+ bnx2i_iscsi_sess_release(sess->hba, sess);
+ }
+
+ if (sess->target_name) {
+ kfree(sess->target_name);
+ sess->target_name = NULL;
+ }
+
+ scsi_remove_host(shost);
+ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+}
+
+
+/*
+ * Function: bnx2i_sess_recovery_timeo
+ * Description: session recovery timeout handling routine
+ */
+void bnx2i_sess_recovery_timeo(struct iscsi_cls_session *cls_session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+
+ sess->recovery_state |= ISCSI_SESS_RECOVERY_FAILED;
+ if (sess->state != BNX2I_SESS_IN_FFP) {
+ }
+ wake_up(&sess->er_wait);
+}
+
+
+/*
+ * Function: bnx2i_conn_create
+ * Description: Creates a new iSCSI connection instance for a given session
+ */
+struct iscsi_cls_conn *bnx2i_conn_create(struct iscsi_cls_session *cls_session,
+ uint32_t cid)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+ struct bnx2i_conn *conn;
+ struct iscsi_cls_conn *cls_conn;
+
+ cls_conn = iscsi_create_conn(cls_session, cid);
+ if (!cls_conn)
+ return NULL;
+
+ conn = cls_conn->dd_data;
+ memset(conn, 0, sizeof(struct bnx2i_conn));
+ conn->cls_conn = cls_conn;
+ conn->exp_statsn = STATSN_UPDATE_SIGNATURE;
+ conn->iscsi_conn_cid = conn->fw_cid = 0;
+ conn->header_digest_en = 0;
+ conn->data_digest_en = 0;
+ conn->persist_address = NULL;
+ conn->state = CONN_STATE_IDLE;
+ /*
+ * Initialize the connection structure
+ */
+ bnx2i_iscsi_conn_new(sess, conn);
+ conn->conn_cid = cid;
+ return cls_conn;
+}
+
+
+
+/*
+ * Function: bnx2i_conn_bind
+ * Description: Binds together iSCSI session instance, iSCSI connection
+ * instance and the TCP connection. If TCP connection does not belong
+ * on the device iSCSI sess/conn is bound, return failure to user.
+ */
+int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn,
+ uint64_t transport_fd, int is_leading)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+ struct bnx2i_conn *tmp = ERR_PTR(-EEXIST);
+ struct bnx2i_conn *conn = cls_conn->dd_data;
+ int ret_code = 0;
+ struct bnx2i_endpoint *ep;
+
+ ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
+
+ if (ep->state == EP_STATE_PEER_DISCONN) {
+ /* Peer disconnect via' FIN or RST */
+ return -EINVAL;
+ }
+
+ if (ep->hba != sess->hba) {
+ /* Error - TCP connection does not belong to this device
+ */
+ printk(KERN_ALERT "bnx2i: conn bind, ep=0x%p (0x%p) does not",
+ ep, ep->hba);
+ printk(KERN_ALERT "belong to hba 0x%p\n", sess->hba);
+ return -EEXIST;
+ }
+ if (!conn->gen_pdu.cmd)
+ conn->gen_pdu.cmd = bnx2i_alloc_cmd(sess);
+
+ /* look-up for existing connection, MC/S is not currently supported */
+ spin_lock_bh(&sess->lock);
+ tmp = NULL;
+ if (!list_empty(&sess->conn_list)) {
+ list_for_each_entry(tmp, &sess->conn_list, link) {
+ if (tmp == conn) {
+ break;
+ }
+ }
+ }
+ if ((tmp != conn) && (conn->sess == sess)) {
+ /* bind iSCSI connection to this session */
+ list_add(&conn->link, &sess->conn_list);
+ if (is_leading) {
+ sess->lead_conn = conn;
+ }
+ }
+
+ conn->ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
+ conn->ep->conn = conn;
+ conn->ep->sess = sess;
+ conn->state = CONN_STATE_XPORT_READY;
+ conn->iscsi_conn_cid = conn->ep->ep_iscsi_cid;
+ conn->fw_cid = conn->ep->ep_cid;
+
+ bnx2i_bind_conn_to_iscsi_cid(conn, ep->ep_iscsi_cid);
+
+ spin_unlock_bh(&sess->lock);
+ return ret_code;
+}
+
+
+/*
+ * Function: bnx2i_conn_destroy
+ * Description: Destroys a iSCSI connection instance.
+ */
+void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
+{
+ struct bnx2i_conn *conn = cls_conn->dd_data;
+
+ bnx2i_conn_free_login_resources(conn->sess->hba, conn);
+
+ if (conn->persist_address) {
+ kfree(conn->persist_address);
+ conn->persist_address = NULL;
+ }
+ iscsi_destroy_conn(cls_conn);
+}
+
+
+/*
+ * Function: bnx2i_conn_set_param
+ * Description: During FFP migration, user daemon will issue this call to
+ * update negotiated iSCSI parameters to driver.
+ */
+int bnx2i_conn_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf, int buflen)
+{
+ struct bnx2i_conn *conn = cls_conn->dd_data;
+ struct bnx2i_sess *sess = conn->sess;
+
+ spin_lock_bh(&sess->lock);
+ if (conn->state != CONN_STATE_IN_LOGIN) {
+ printk(KERN_ERR "bnx2i: can't change param [%d]\n", param);
+ spin_unlock_bh(&sess->lock);
+ return 0;
+ }
+ spin_unlock_bh(&sess->lock);
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ sscanf(buf, "%d", &conn->max_data_seg_len_recv);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ sscanf(buf, "%d", &conn->max_data_seg_len_xmit);
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ sscanf(buf, "%d", &conn->header_digest_en);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ sscanf(buf, "%d", &conn->data_digest_en);
+ break;
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->initial_r2t);
+ }
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->max_r2t);
+ }
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->imm_data);
+ }
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->first_burst_len);
+ }
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->max_burst_len);
+ }
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->pdu_inorder);
+ }
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->dataseq_inorder);
+ }
+ break;
+ case ISCSI_PARAM_ERL:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->erl);
+ }
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ sscanf(buf, "%d", &conn->ifmarker_enable);
+ BUG_ON(conn->ifmarker_enable);
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ sscanf(buf, "%d", &conn->ofmarker_enable);
+ BUG_ON(conn->ofmarker_enable);
+ break;
+ case ISCSI_PARAM_EXP_STATSN:
+ sscanf(buf, "%u", &conn->exp_statsn);
+ break;
+ case ISCSI_PARAM_TARGET_NAME:
+ if (sess->target_name)
+ break;
+ sess->target_name = kstrdup(buf, GFP_KERNEL);
+ if (!sess->target_name)
+ return -ENOMEM;
+ break;
+ case ISCSI_PARAM_TPGT:
+ sscanf(buf, "%d", &sess->tgt_prtl_grp);
+ break;
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ {
+ sscanf(buf, "%d", &conn->persist_port);
+ }
+ break;
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ if (conn->persist_address)
+ break;
+ conn->persist_address = kstrdup(buf, GFP_KERNEL);
+ if (!conn->persist_address)
+ return -ENOMEM;
+ break;
+ default:
+ printk(KERN_ALERT "PARAM_UNKNOWN: 0x%x\n", param);
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Function: bnx2i_conn_get_param
+ * Description: Call to retrieve iSCSI connection parameters
+ */
+int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
+{
+ struct bnx2i_conn *conn;
+ int len = 0;
+
+ if (!cls_conn)
+ return -EINVAL;
+ conn = (struct bnx2i_conn *)cls_conn->dd_data;
+ if (!conn || !conn->ep ||
+ (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL))
+ return -EINVAL;
+
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_data_seg_len_recv);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_data_seg_len_xmit);
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ len = sprintf(buf, "%d\n", conn->header_digest_en);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ len = sprintf(buf, "%d\n", conn->data_digest_en);
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ len = sprintf(buf, "%u\n", conn->ifmarker_enable);
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ len = sprintf(buf, "%u\n", conn->ofmarker_enable);
+ break;
+ case ISCSI_PARAM_EXP_STATSN:
+ len = sprintf(buf, "%u\n", conn->exp_statsn);
+ break;
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ len = sprintf(buf, "%d\n", conn->persist_port);
+ break;
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ if (conn->persist_address) {
+ len = sprintf(buf, "%s\n", conn->persist_address);
+ }
+ break;
+ case ISCSI_PARAM_CONN_PORT:
+ len = sprintf(buf, "%hu\n", conn->ep->cm_sk->dst_port);
+ break;
+ case ISCSI_PARAM_CONN_ADDRESS:
+ len = sprintf(buf, NIPQUAD_FMT "\n",
+ NIPQUAD(conn->ep->cm_sk->dst_ip));
+ break;
+ default:
+ printk(KERN_ALERT "get_param: conn 0x%p param %d not found\n",
+ conn, (u32)param);
+ return -ENOSYS;
+ }
+
+ return len;
+}
+
+
+/*
+ * Function: bnx2i_session_get_param
+ * Description: Call to obtain iSCSI session parameters
+ */
+int bnx2i_session_get_param(struct iscsi_cls_session *cls_session,
+ enum iscsi_param param, char *buf)
+{
+ struct Scsi_Host *shost = NULL;
+ struct bnx2i_sess *sess = NULL;
+ int len = 0;
+
+ if (!cls_session)
+ return -EINVAL;
+
+ shost = iscsi_session_to_shost(cls_session);
+ sess = iscsi_hostdata(shost->hostdata);
+ if (!sess || !sess->lead_conn)
+ return -EINVAL;
+
+ switch (param) {
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ len = sprintf(buf, "%d\n", sess->initial_r2t);
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ len = sprintf(buf, "%hu\n", sess->max_r2t);
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ len = sprintf(buf, "%d\n", sess->imm_data);
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ len = sprintf(buf, "%u\n", sess->first_burst_len);
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ len = sprintf(buf, "%u\n", sess->max_burst_len);
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ len = sprintf(buf, "%d\n", sess->pdu_inorder);
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ len = sprintf(buf, "%d\n", sess->dataseq_inorder);
+ break;
+ case ISCSI_PARAM_ERL:
+ len = sprintf(buf, "%d\n", sess->erl);
+ break;
+ case ISCSI_PARAM_TARGET_NAME:
+ if (sess->target_name) {
+ len = sprintf(buf, "%s\n", sess->target_name);
+ }
+ break;
+ case ISCSI_PARAM_TPGT:
+ len = sprintf(buf, "%d\n", sess->tgt_prtl_grp);
+ break;
+ default:
+ printk(KERN_ALERT "sess_get_param: sess 0x%p", sess);
+ printk(KERN_ALERT "param (0x%x) not found\n", (u32) param);
+ return -ENOSYS;
+ }
+
+ return len;
+}
+
+
+/*
+ * Function: bnx2i_conn_start
+ * Description: last call in FFP migration to handover iscsi conn to the driver
+ */
+int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
+{
+ struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
+ struct bnx2i_sess *sess = conn->sess;
+
+ if (conn->state != CONN_STATE_IN_LOGIN) {
+ printk(KERN_ALERT "conn_start: conn 0x%p state 0x%x err!!\n",
+ conn, conn->state);
+ return -EINVAL;
+ }
+
+ if (!sess->initial_r2t) {
+ if (sess->first_burst_len > sess->max_burst_len)
+ return -EINVAL;
+ } else if (conn->max_data_seg_len_xmit > sess->max_burst_len) {
+ if (sess->first_burst_len > sess->max_burst_len)
+ return -EINVAL;
+ /* don't bother if only immediate data is supported and
+ * FBL & MBL are greater than MRDSL. In that case initiator
+ * will always send MRDSL worth of immediate data
+ */
+ }
+
+ conn->state = CONN_STATE_FFP_STATE;
+ if (conn->sess->lead_conn == conn) {
+ conn->sess->state = BNX2I_SESS_IN_FFP;
+ }
+
+ conn->ep->state = EP_STATE_ULP_UPDATE_START;
+ bnx2i_update_iscsi_conn(conn);
+
+ conn->ep->ofld_timer.expires = 10*HZ + jiffies;
+ conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ conn->ep->ofld_timer.data = (unsigned long)conn->ep;
+ add_timer(&conn->ep->ofld_timer);
+ /* update iSCSI context for this conn, wait for CNIC to complete */
+ wait_event_interruptible(conn->ep->ofld_wait,
+ conn->ep->state != EP_STATE_ULP_UPDATE_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&conn->ep->ofld_timer);
+ if (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL) {
+ /* should never happen */
+ }
+ /* Free login ITT, not required anymore */
+ if (conn->gen_pdu.cmd) {
+ bnx2i_free_cmd(conn->sess, conn->gen_pdu.cmd);
+ conn->gen_pdu.cmd = NULL;
+ }
+
+ switch (conn->stop_state) {
+ case STOP_CONN_RECOVER:
+ sess->recovery_state = ISCSI_SESS_RECOVERY_COMPLETE;
+ conn->sess->state = BNX2I_SESS_IN_FFP;
+ iscsi_unblock_session(session_to_cls(sess));
+ wake_up(&sess->er_wait);
+ break;
+ case STOP_CONN_TERM:
+ break;
+ default:
+ ;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Function: bnx2i_conn_stop
+ * Description: call to take control of iscsi conn from the driver.
+ * Could be called when login failed, when recovery is to be
+ * attempted or during connection teardown
+ */
+void bnx2i_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+{
+ struct bnx2i_conn *conn = (struct bnx2i_conn *)cls_conn->dd_data;
+
+ conn->stop_state = flag;
+ iscsi_block_session(session_to_cls(conn->sess));
+
+ switch (flag) {
+ case STOP_CONN_RECOVER:
+ conn->sess->state = BNX2I_SESS_IN_RECOVERY;
+ break;
+ case STOP_CONN_TERM:
+ if (conn->sess && (conn->sess->state & BNX2I_SESS_IN_FFP)) {
+ conn->sess->state = BNX2I_SESS_IN_SHUTDOWN;
+ }
+ break;
+ default:
+ printk(KERN_ERR "bnx2i: invalid conn stop req %d\n", flag);
+ }
+
+ return;
+}
+
+
+/*
+ * Function: bnx2i_conn_send_pdu
+ * Description: To send iSCSI PDUs prepared by user daemon, only login, logout,
+ * nop-out pdu's should flow this path.
+ */
+int bnx2i_conn_send_pdu(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_hdr *hdr, char *data,
+ uint32_t data_size)
+{
+ struct bnx2i_conn *conn = NULL;
+ struct iscsi_hdr *iscsi_hdr = (struct iscsi_hdr *) hdr;
+ struct bnx2i_cmd *cmnd = NULL;
+ uint32_t payload_size = 0;
+ int rc;
+ unsigned long flags;
+
+ if (!cls_conn) {
+ printk(KERN_ALERT "bnx2i_conn_send_pdu: NULL conn ptr. \n");
+ return -EIO;
+ }
+ conn = (struct bnx2i_conn *)cls_conn->dd_data;
+ if (!conn->gen_pdu.req_buf) {
+ printk(KERN_ALERT "send_pdu: login buf not allocated\n");
+ /* ERR - buffer not allocated, should not happen */
+ return -EIO;
+ }
+
+ if (conn->gen_pdu.cmd) {
+ if ((conn->state != CONN_STATE_XPORT_READY) &&
+ (conn->state != CONN_STATE_IN_LOGIN)) {
+ printk(KERN_ALERT "send_pdu: %d != XPORT_READY\n",
+ conn->state);
+ return -EPERM;
+ }
+ cmnd = conn->gen_pdu.cmd;
+ } else { /* could be NOPOUT or the LOGOUT request */
+ spin_lock_irqsave(conn->sess->host->host_lock, flags);
+ cmnd = bnx2i_alloc_cmd(conn->sess);
+ spin_unlock_irqrestore(conn->sess->host->host_lock, flags);
+
+ if (!cmnd) {
+ printk(KERN_ALERT "bnx2i: Error - cmd not allocated\n");
+ return -EIO;
+ }
+ }
+ memset(conn->gen_pdu.req_buf, 0, ISCSI_CONN_LOGIN_BUF_SIZE);
+ /* Login request, copy hdr & data to buffer in conn struct */
+ memcpy((void *) &conn->gen_pdu.pdu_hdr, (const void *) hdr,
+ sizeof(struct iscsi_hdr));
+
+ cmnd->iscsi_opcode = iscsi_hdr->opcode;
+ switch (iscsi_hdr->opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_LOGIN:
+ if (conn->state == CONN_STATE_XPORT_READY)
+ conn->state = CONN_STATE_IN_LOGIN;
+ break;
+ case ISCSI_OP_LOGOUT:
+ conn->state = CONN_STATE_IN_LOGOUT;
+ conn->sess->state = BNX2I_SESS_IN_LOGOUT;
+ break;
+ case ISCSI_OP_NOOP_OUT:
+ break;
+ default:
+ ;
+ }
+
+ conn->gen_pdu.req_buf_size = data_size;
+ payload_size = (hdr->dlength[0] << 16) | (hdr->dlength[1] << 8) |
+ hdr->dlength[2];
+
+ if (data_size) {
+ memcpy((void *)conn->gen_pdu.req_buf, (const void *)data,
+ data_size);
+ conn->gen_pdu.req_wr_ptr =
+ conn->gen_pdu.req_buf + payload_size;
+ }
+ cmnd->conn = conn;
+ cmnd->scsi_cmd = NULL;
+ rc = bnx2i_iscsi_send_generic_request(cmnd);
+ return rc;
+}
+
+
+/*
+ * Function : bnx2i_conn_get_stats
+ * Description: Returns iSCSI stats
+ */
+void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
+{
+ struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
+
+ stats->txdata_octets = conn->total_data_octets_sent;
+ stats->rxdata_octets = conn->total_data_octets_rcvd;
+
+ stats->noptx_pdus = conn->num_nopin_pdus;
+ stats->scsicmd_pdus = conn->num_scsi_cmd_pdus;
+ stats->tmfcmd_pdus = conn->num_tmf_req_pdus;
+ stats->login_pdus = conn->num_login_req_pdus;
+ stats->text_pdus = 0;
+ stats->dataout_pdus = conn->num_dataout_pdus;
+ stats->logout_pdus = conn->num_logout_req_pdus;
+ stats->snack_pdus = 0;
+
+ stats->noprx_pdus = conn->num_nopout_pdus;
+ stats->scsirsp_pdus = conn->num_scsi_resp_pdus;
+ stats->tmfrsp_pdus = conn->num_tmf_resp_pdus;
+ stats->textrsp_pdus = 0;
+ stats->datain_pdus = conn->num_datain_pdus;
+ stats->logoutrsp_pdus = conn->num_logout_resp_pdus;
+ stats->r2t_pdus = conn->num_r2t_pdus;
+ stats->async_pdus = conn->num_async_pdus;
+ stats->rjt_pdus = conn->num_reject_pdus;
+
+ stats->digest_err = 0;
+ stats->timeout_err = 0;
+ stats->custom_length = 0;
+}
+
+
+
+/*
+ * Function : bnx2i_check_nx2_dev_busy
+ * Description: this routine unregister devices if there are no active conns
+ */
+static void bnx2i_check_nx2_dev_busy(void)
+{
+ if (bnx2i_num_free_ep == bnx2i_max_free_ep) {
+ bnx2i_unreg_dev_all();
+ msleep(2);
+ }
+}
+
+
+/*
+ * Function : bnx2i_ep_connect
+ * Description: this routine initiates the TCP/IP connection by invoking
+ * Option-2 interface with l5_core and the CNIC
+ */
+int bnx2i_ep_connect(struct sockaddr *dst_addr, int non_blocking,
+ uint64_t *ep_handle)
+{
+ u32 iscsi_cid = BNX2I_CID_RESERVED;
+ struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
+ struct bnx2i_endpoint *endpoint;
+ struct bnx2i_hba *hba = NULL;
+ struct cnic_dev *cnic = NULL;
+ struct cnic_dev *tmp_cnic = NULL;
+ struct bnx2i_hba *tmp_hba = NULL;
+ struct cnic_sockaddr saddr;
+ int rc = 0;
+ extern int bnx2i_reg_device;
+ extern struct bnx2i_hba *get_adapter_list_head(void);
+
+ /*
+ * check if the given destination can be reached through NX2 device
+ */
+
+ if ((!bnx2i_reg_device) &&
+ (bnx2i_num_free_ep == bnx2i_max_free_ep)) {
+ bnx2i_reg_dev_all();
+ }
+ tmp_hba = get_adapter_list_head();
+ if (tmp_hba && tmp_hba->cnic) {
+ tmp_cnic = tmp_hba->cnic;
+ cnic = tmp_cnic->cm_select_dev(desti, CNIC_ULP_ISCSI);
+ }
+ if (!cnic) {
+ printk(KERN_ALERT "bnx2i: ep_conn, can't connect using cnic\n");
+ rc = 0;
+ goto check_busy;
+ }
+ hba = bnx2i_find_hba_for_cnic(cnic);
+
+ if (bnx2i_adapter_ready(hba)) {
+ printk(KERN_ALERT "bnx2i: ep_conn, adapter not found\n");
+ rc = 0;
+ goto check_busy;
+ }
+ if (hba->netdev->mtu > hba->mtu_supported) {
+ printk(KERN_ALERT "bnx2i: %s network i/f mtu is set to %d\n",
+ hba->netdev->name, hba->netdev->mtu);
+ printk(KERN_ALERT "bnx2i: iSCSI HBA can support mtu of %d\n",
+ hba->mtu_supported);
+ rc = 0;
+ goto check_busy;
+ }
+ endpoint = bnx2i_alloc_ep();
+ if (!endpoint) {
+ printk(KERN_ALERT "bnx2i: ep_conn, unable to alloc ep\n");
+ *ep_handle = (uint64_t) 0;
+ rc = -ENOMEM;
+ goto check_busy;
+ }
+
+ endpoint->ep_iscsi_cid = (u16)ISCSI_RESERVED_TAG;
+ iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
+ if (iscsi_cid == (u16) ISCSI_RESERVED_TAG) {
+ printk(KERN_ALERT "alloc_ep: unable to allocate iscsi cid\n");
+ rc = -ENOMEM;
+ goto iscsi_cid_err;
+ }
+ endpoint->hba = hba;
+ endpoint->hba_age = hba->age;
+
+ rc = bnx2i_alloc_qp_resc(hba, endpoint);
+ if (rc != 0) {
+ printk(KERN_ALERT "bnx2i: ep_conn, alloc QP resc error\n");
+ rc = -ENOMEM;
+ goto qp_resc_err;
+ }
+
+ endpoint->ep_iscsi_cid = iscsi_cid;
+ endpoint->state = EP_STATE_OFLD_START;
+ bnx2i_ep_ofld_list_add(hba, endpoint);
+
+ bnx2i_send_conn_ofld_req(hba, endpoint);
+
+ init_timer(&endpoint->ofld_timer);
+ endpoint->ofld_timer.expires = 2 * HZ + jiffies;
+ endpoint->ofld_timer.function = bnx2i_ep_ofld_timer;
+ endpoint->ofld_timer.data = (unsigned long) endpoint;
+ add_timer(&endpoint->ofld_timer);
+ /* Wait for CNIC hardware to setup conn context and return 'cid' */
+ wait_event_interruptible(endpoint->ofld_wait,
+ endpoint->state != EP_STATE_OFLD_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&endpoint->ofld_timer);
+ list_del_init(&endpoint->link);
+
+ if (endpoint->state != EP_STATE_OFLD_COMPL) {
+ rc = -ENOSPC;
+ goto conn_failed;
+ }
+
+ rc = -EINVAL;
+ if (hba->reg_with_cnic)
+ rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, endpoint->ep_cid,
+ iscsi_cid, &endpoint->cm_sk, endpoint);
+ if (rc)
+ goto conn_failed;
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.remote_addr.sin_addr.s_addr = desti->sin_addr.s_addr;
+ saddr.remote_addr.sin_port = desti->sin_port;
+ saddr.local_addr.sin_port = htons(endpoint->tcp_port);
+ endpoint->state = EP_STATE_CONNECT_START;
+ rc = -EINVAL;
+ if (hba->reg_with_cnic)
+ rc = cnic->cm_connect(endpoint->cm_sk, &saddr);
+ else
+ goto conn_failed;
+
+ if (rc)
+ goto release_ep;
+
+ bnx2i_map_ep_dbell_regs(endpoint);
+
+ *ep_handle = (uint64_t) (unsigned long) endpoint;
+ return 0;
+
+release_ep:
+ cnic->cm_destroy(endpoint->cm_sk);
+conn_failed:
+iscsi_cid_err:
+ bnx2i_free_qp_resc(hba, endpoint);
+qp_resc_err:
+ bnx2i_free_ep(endpoint);
+check_busy:
+ *ep_handle = (uint64_t) 0;
+ bnx2i_check_nx2_dev_busy();
+ return rc;
+}
+
+
+
+/*
+ * Function : bnx2i_ep_poll
+ * Description: polls for TCP connect request to complete
+ */
+int bnx2i_ep_poll(uint64_t ep_handle, int timeout_ms)
+{
+ struct bnx2i_endpoint *ep;
+ int rc = 0;
+ ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
+
+ if (!ep) {
+ return -EINVAL;
+ }
+ if (ep->state == EP_STATE_IDLE) {
+ return -1;
+ }
+ if (ep->state == EP_STATE_CONNECT_COMPL) {
+ return 1;
+ }
+
+ rc = wait_event_interruptible_timeout(ep->ofld_wait,
+ (ep->state ==
+ EP_STATE_CONNECT_COMPL),
+ msecs_to_jiffies(timeout_ms));
+ if (!rc || (ep->state == EP_STATE_OFLD_FAILED)) {
+ rc = -1;
+ }
+
+ if (rc > 0) {
+ return 1;
+ } else if (!rc) {
+ return 0; /* timeout */
+ } else {
+ return rc;
+ }
+}
+
+
+/*
+ * Function : bnx2i_ep_disconnect
+ * Description: initiates TCP/IP connection teardown process
+ */
+void bnx2i_ep_disconnect(uint64_t ep_handle)
+{
+ struct bnx2i_endpoint *ep;
+ struct cnic_dev *cnic = NULL;
+ struct bnx2i_sess *sess = NULL;
+ int rc = 0;
+
+ ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
+ if (!ep || (ep_handle == -1)) {
+ return;
+ }
+ if (ep->state == EP_STATE_IDLE) {
+ goto return_ep;
+ }
+ cnic = ep->hba->cnic;
+
+ if (ep->state == EP_STATE_PEER_DISCONN) {
+ ep->state = EP_STATE_DISCONN_COMPL;
+ goto peer_discon;
+ }
+
+ if (test_bit(ADAPTER_STATE_DOWN, &ep->hba->adapter_state)) {
+ goto free_resc;
+ }
+ if (ep->hba_age != ep->hba->age) {
+ goto dev_reset;
+ }
+
+ ep->state = EP_STATE_DISCONN_START;
+
+ init_timer(&ep->ofld_timer);
+ ep->ofld_timer.expires = 10*HZ + jiffies;
+ ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ ep->ofld_timer.data = (unsigned long) ep;
+ add_timer(&ep->ofld_timer);
+
+ if (ep->hba->reg_with_cnic)
+ cnic->cm_close(ep->cm_sk);
+ else
+ goto free_resc;
+
+ /* wait for option-2 conn teardown */
+ wait_event_interruptible(ep->ofld_wait,
+ ep->state != EP_STATE_DISCONN_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&ep->ofld_timer);
+
+peer_discon:
+ if (!ep->hba->reg_with_cnic)
+ goto free_resc;
+
+ rc = cnic->cm_destroy(ep->cm_sk);
+ ep->state = EP_STATE_CLEANUP_START;
+ init_timer(&ep->ofld_timer);
+ ep->ofld_timer.expires = 10*HZ + jiffies;
+ ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ ep->ofld_timer.data = (unsigned long) ep;
+ add_timer(&ep->ofld_timer);
+
+ bnx2i_ep_destroy_list_add(ep->hba, ep);
+
+ /* destroy iSCSI context, wait for it to complete */
+ bnx2i_send_conn_destroy(ep->hba, ep);
+ wait_event_interruptible(ep->ofld_wait,
+ (ep->state != EP_STATE_CLEANUP_START));
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&ep->ofld_timer);
+ if (ep->state != EP_STATE_CLEANUP_CMPL) {
+ /* should never happen */
+ printk(KERN_ALERT "bnx2i - conn destroy failed\n");
+ }
+
+dev_reset:
+ bnx2i_flush_active_cmd_queue(ep->sess, DID_NO_CONNECT);
+
+free_resc:
+ bnx2i_free_qp_resc(ep->hba, ep);
+return_ep:
+ if (ep->conn && ep->conn->sess)
+ /* session recovery in progress */
+ sess = ep->conn->sess;
+ if (sess && (sess->state != BNX2I_SESS_IN_RECOVERY))
+ /* session logged out */
+ sess = NULL;
+
+ bnx2i_free_ep(ep);
+ if (sess) {
+ wake_up(&sess->er_wait);
+ }
+
+ bnx2i_check_nx2_dev_busy();
+ return;
+}
+
+
+
+
+/*
+ * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template
+ * used while registering with the iSCSI trnaport module.
+ */
+struct scsi_host_template bnx2i_host_template = {
+ .module = THIS_MODULE,
+ .name = "Broadcom Offload iSCSI Initiator",
+ .queuecommand = bnx2i_queuecommand,
+ .eh_abort_handler = bnx2i_abort,
+ .eh_host_reset_handler = bnx2i_host_reset,
+ .bios_param = NULL,
+ .can_queue = 128,
+ .max_sectors = 256,
+ .this_id = -1,
+ .cmd_per_lun = 64,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = ISCSI_MAX_BDS_PER_CMD,
+ .proc_name = NULL
+ };
+
+
+
+struct iscsi_transport bnx2i_iscsi_transport = {
+ .owner = THIS_MODULE,
+ .name = "bnx2i",
+ .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_MULTI_R2T
+ | CAP_DATADGST,
+ .param_mask = ISCSI_MAX_RECV_DLENGTH |
+ ISCSI_MAX_XMIT_DLENGTH |
+ ISCSI_HDRDGST_EN |
+ ISCSI_DATADGST_EN |
+ ISCSI_INITIAL_R2T_EN |
+ ISCSI_MAX_R2T |
+ ISCSI_IMM_DATA_EN |
+ ISCSI_FIRST_BURST |
+ ISCSI_MAX_BURST |
+ ISCSI_PDU_INORDER_EN |
+ ISCSI_DATASEQ_INORDER_EN |
+ ISCSI_ERL |
+ ISCSI_CONN_PORT |
+ ISCSI_CONN_ADDRESS |
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+ .host_param_mask = 0,
+ .host_template = &bnx2i_host_template,
+ .sessiondata_size = sizeof(struct bnx2i_sess),
+ .conndata_size = sizeof(struct bnx2i_conn),
+ .max_conn = 1,
+ .max_cmd_len = 16,
+ .max_lun = 512,
+ .create_session = bnx2i_session_create,
+ .destroy_session = bnx2i_session_destroy,
+ .create_conn = bnx2i_conn_create,
+ .bind_conn = bnx2i_conn_bind,
+ .destroy_conn = bnx2i_conn_destroy,
+ .set_param = bnx2i_conn_set_param,
+ .get_conn_param = bnx2i_conn_get_param,
+ .get_session_param = bnx2i_session_get_param,
+ .start_conn = bnx2i_conn_start,
+ .stop_conn = bnx2i_conn_stop,
+ .send_pdu = bnx2i_conn_send_pdu,
+ .get_stats = bnx2i_conn_get_stats,
+ /* iscsi host params */
+ .get_host_param = NULL,
+ .set_host_param = NULL,
+ /* TCP connect - disconnect - option-2 interface calls */
+ .ep_connect = bnx2i_ep_connect,
+ .ep_poll = bnx2i_ep_poll,
+ .ep_disconnect = bnx2i_ep_disconnect,
+ /* Error recovery timeout call */
+ .session_recovery_timedout = bnx2i_sess_recovery_timeo
+};
diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c
new file mode 100644
index 0000000..6bd6eba
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c
@@ -0,0 +1,616 @@
+/* bnx2i_sysfs.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+#define BNX2I_SYSFS_VERSION 0x2
+
+static ssize_t bnx2i_show_mips_status(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ ssize_t len = 0;
+
+ bnx2i_read_mips_idle_counters(hba);
+
+ len = sprintf(buf, "%d\n%lu\n%d\n%llu\n%llu\n"
+ "%llu\n%llu\n%llu\n%llu\n%llu\n%llu\n",
+ BNX2I_SYSFS_VERSION, jiffies, HZ,
+ hba->mips_idle.cp_idle_count,
+ hba->mips_idle.txp_idle_count,
+ hba->mips_idle.txp_tdma_count,
+ hba->mips_idle.txp_ctx_count,
+ hba->mips_idle.txp_hdrq_count,
+ hba->mips_idle.tpat_idle_count,
+ hba->mips_idle.rxp_idle_count,
+ hba->mips_idle.com_idle_count);
+ return (len);
+}
+
+static ssize_t bnx2i_show_net_if_name(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "%s\n", hba->netdev->name);
+}
+
+static ssize_t bnx2i_show_pci_bar(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%.8x\n",
+ (u32) pci_resource_start(hba->pci_dev, 0));
+}
+
+static ssize_t bnx2i_show_sq_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->max_sqes);
+}
+
+static ssize_t bnx2i_set_sq_info(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ u32 val;
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_SQ_WQES_MIN) &&
+ (val <= BNX2I_SQ_WQES_MAX)) {
+ hba->max_sqes = val;
+ }
+ }
+ return count;
+}
+
+static ssize_t bnx2i_show_cq_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->max_cqes);
+}
+
+static ssize_t bnx2i_set_cq_info(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 val;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_CQ_WQES_MIN) &&
+ (val <= BNX2I_CQ_WQES_MAX)) {
+ hba->max_cqes = val;
+ }
+ }
+ return count;
+}
+
+static ssize_t bnx2i_show_rq_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->max_rqes);
+}
+
+static ssize_t bnx2i_set_rq_info(struct class_device *cdev, const char *buf,
+ size_t count)
+{
+ u32 val;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_RQ_WQES_MIN) &&
+ (val <= BNX2I_RQ_WQES_MAX)) {
+ hba->max_rqes = val;
+ }
+ }
+ return count;
+}
+
+
+static ssize_t bnx2i_show_ccell_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->num_ccell);
+}
+
+static ssize_t bnx2i_set_ccell_info(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 val;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_CCELLS_MIN) &&
+ (val <= BNX2I_CCELLS_MAX)) {
+ hba->num_ccell = val;
+ }
+ }
+ return count;
+}
+
+
+static ssize_t bnx2i_read_pci_trigger_reg(struct class_device *cdev,
+ char *buf)
+{
+ u32 reg_val = 0;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+#define PCI_EVENT_TRIGGER_REG 0xCAC /* DMA WCHAN STAT10 REG */
+ reg_val = readl(hba->cnic->regview + PCI_EVENT_TRIGGER_REG);
+ return sprintf(buf, "0x%x\n", reg_val);
+}
+
+
+static ssize_t bnx2i_get_iscsi_cntx_dump(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ unsigned int *ptr = (unsigned int *) hba->ctx_addr;
+ unsigned int *dst_ptr = (unsigned int *) buf;
+ int unit_sz = sizeof(unsigned int);
+#define SYSFS_BUF_SIZE 4096
+#define NUM_SYSFS_BUFS_PER_CTX 4
+
+ if (hba->ctx_read_cnt == NUM_SYSFS_BUFS_PER_CTX)
+ return 0;
+
+ ptr += (((hba->ctx_read_cnt % NUM_SYSFS_BUFS_PER_CTX) *
+ SYSFS_BUF_SIZE) / unit_sz);
+ hba->ctx_read_cnt++;
+ memcpy(dst_ptr, ptr, SYSFS_BUF_SIZE);
+
+ return SYSFS_BUF_SIZE;
+}
+
+static ssize_t bnx2i_select_iscsi_cntx_dump(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 iscsi_cid;
+ int ret = 0;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &iscsi_cid) > 0) {
+ ret = bnx2i_select_ctx_dump_cid(hba, iscsi_cid);
+ }
+ if (!ret)
+ ret = count;
+ return ret;
+}
+
+static ssize_t bnx2i_get_active_iscsi_cid_list(struct class_device *cdev,
+ char *buf)
+{
+ u32 active_iscsi_cid[32];
+ u32 active_cid[32];
+ int num_cid = 0;
+ ssize_t total_len = 0;
+ char *cur_ptr = buf;
+ int i = 0;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ u32 num_ccell = hba->ctx_ccell_tasks & 0xFFFF;
+ u32 num_tasks_per_conn = hba->ctx_ccell_tasks >> 16;
+
+ if (!hba->ictx_poll_mode) {
+ num_cid = bnx2i_list_iscsi_cid(hba, active_iscsi_cid,
+ active_cid);
+ }
+ total_len += sprintf(cur_ptr, "0x%x\n", BNX2I_SYSFS_VERSION);
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x\n", num_ccell);
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x\n", num_tasks_per_conn);
+ if (hba->ictx_poll_mode) {
+ if (hba->ictx_poll_cid) {
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
+ hba->ictx_poll_iscsi_cid,
+ hba->ictx_poll_cid);
+ hba->ictx_poll_cid = hba->ictx_poll_iscsi_cid = 0;
+ }
+ } else {
+ for (i = 0; i < num_cid; i++) {
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
+ active_iscsi_cid[i],
+ active_cid[i]);
+ }
+ }
+ return total_len;
+}
+
+
+static ssize_t bnx2i_set_iscsi_cid_err_poll_mode(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 poll_mode;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, "0x%x", &poll_mode) > 0) {
+ if (poll_mode)
+ hba->ictx_poll_mode = 1;
+ else
+ hba->ictx_poll_mode = 0;
+ }
+ return count;
+}
+
+
+static ssize_t bnx2i_get_qp_shmem_dump(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ int resi_len = hba->sq_cq_size -
+ (hba->sq_cq_rdp - hba->sq_cq_dump);
+
+ if (!hba->sq_cq_dump || !hba->sq_cq_rdp) {
+ return -EINVAL;
+ } else if ((hba->sq_cq_dump + hba->sq_cq_size) ==
+ hba->sq_cq_rdp) {
+ kfree(hba->sq_cq_dump);
+ hba->sq_cq_dump = hba->sq_cq_rdp = NULL;
+ return 0;
+ }
+
+ if (resi_len > SYSFS_BUF_SIZE) {
+ resi_len = SYSFS_BUF_SIZE;
+ }
+ memcpy(buf, hba->sq_cq_rdp, resi_len);
+ hba->sq_cq_rdp += resi_len;
+
+ return resi_len;
+}
+
+
+
+
+static void bnx2i_dup_cq_mem(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn, int count)
+{
+ struct cqe *cqe_s;
+ struct cqe *cqe_d;
+ int total_cnt = count;
+
+ if (conn->ep->qp.cq_cons_qe == conn->ep->qp.cq_virt)
+ cqe_s = conn->ep->qp.cq_last_qe;
+ else
+ cqe_s = conn->ep->qp.cq_cons_qe - 1;
+ cqe_d = (struct cqe *)hba->sq_cq_rdp;
+ while (count--) {
+ memcpy(cqe_d, cqe_s, sizeof(struct cqe));
+ if (cqe_s == conn->ep->qp.cq_virt) {
+ cqe_s = conn->ep->qp.cq_last_qe;
+ } else {
+ cqe_s--;
+ }
+ cqe_d++;
+ if ((cqe_d - (struct cqe *)hba->sq_cq_rdp) > total_cnt) {
+ printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
+ break;
+ }
+ }
+}
+
+
+static int bnx2i_init_cq_dump(struct bnx2i_hba *hba, u32 iscsi_cid,
+ u32 count)
+{
+ struct bnx2i_conn *conn = NULL;
+ int cq_size = 0;
+
+ conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+ if (!conn) {
+ printk(KERN_ALERT "CQ dump: cid #%x not valid\n",
+ iscsi_cid);
+ return -EPERM;
+ }
+
+ if (hba->sq_cq_dump)
+ return -EPERM;
+
+ cq_size = (conn->ep->qp.cq_last_qe - conn->ep->qp.cq_virt) + 1;
+
+ if (!count || (count > cq_size))
+ count = cq_size;
+
+ hba->sq_cq_size = count * sizeof(struct cqe);
+
+ if (!hba->sq_cq_size)
+ return -EINVAL;
+
+ hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
+ if (!hba->sq_cq_dump)
+ return -ENOMEM;
+ hba->sq_cq_rdp = hba->sq_cq_dump;
+
+ bnx2i_dup_cq_mem(hba, conn, count);
+ return 0;
+}
+
+
+
+static void bnx2i_dup_sq_mem(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn, int count)
+{
+ struct sqe *sqe_s;
+ struct sqe *sqe_d;
+ int total_cnt = count;
+
+ if (conn->ep->qp.sq_prod_qe == conn->ep->qp.sq_virt)
+ sqe_s = conn->ep->qp.sq_last_qe;
+ else
+ sqe_s = conn->ep->qp.sq_prod_qe - 1;
+ sqe_d = (struct sqe *)hba->sq_cq_rdp;
+ while (count--) {
+ memcpy(sqe_d, sqe_s, sizeof(struct sqe));
+ if (sqe_s == conn->ep->qp.sq_virt) {
+ sqe_s = conn->ep->qp.sq_last_qe;
+ } else {
+ sqe_s--;
+ }
+ sqe_d++;
+ if ((sqe_d - (struct sqe *) hba->sq_cq_rdp) >
+ total_cnt) {
+ printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
+ break;
+ }
+ }
+}
+
+
+static int bnx2i_init_sq_dump(struct bnx2i_hba *hba,
+ u32 iscsi_cid, u32 count)
+{
+ struct bnx2i_conn *conn = NULL;
+ int sq_size = 0;
+
+ conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+ if (!conn) {
+ printk(KERN_ALERT "SQ dump: cid #%x not valid\n",
+ iscsi_cid);
+ return -EINVAL;
+ }
+
+ if (hba->sq_cq_dump)
+ return -EINVAL;
+
+ sq_size = (conn->ep->qp.sq_last_qe - conn->ep->qp.sq_virt) + 1;
+
+ if (!count || (count > sq_size))
+ count = sq_size;
+
+ hba->sq_cq_size = count * sizeof(struct sqe);
+
+ if (!hba->sq_cq_size)
+ return -EINVAL;
+
+ hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
+ if (!hba->sq_cq_dump)
+ return -ENOMEM;
+ hba->sq_cq_rdp = hba->sq_cq_dump;
+
+ bnx2i_dup_sq_mem(hba, conn, count);
+ return 0;
+}
+
+static ssize_t bnx2i_setup_qp_shmem_dump(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ u32 iscsi_cid;
+ char queue[32];
+ ssize_t ret = count;
+ u32 num_count;
+
+
+ if (sscanf(buf, "%c%c,%d,%d", &queue[0], &queue[1],
+ &iscsi_cid, &num_count) > 0) {
+ if (!strncmp(queue, "SQ", 2)) {
+ ret = bnx2i_init_sq_dump(hba, iscsi_cid, num_count);
+ } else if (!strncmp(queue, "CQ", 2)) {
+ ret = bnx2i_init_cq_dump(hba, iscsi_cid, num_count);
+ } else {
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+
+static ssize_t bnx2i_read_tcp_portd_options(struct class_device *cdev,
+ char *buf)
+{
+ extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
+ return sprintf(buf, "0x%x\n", bnx2i_tcp_port_tbl.num_required);
+}
+
+static ssize_t bnx2i_write_tcp_portd_results(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
+ u32 tcp_port, bind_stat;
+
+ if (!bnx2i_tcp_port_tbl.free_q)
+ return count;
+
+ if (sscanf(buf, "%d,%d", &tcp_port, &bind_stat) > 0) {
+ if (bind_stat && tcp_port) {
+ bnx2i_tcp_port_new_entry(tcp_port);
+ }
+ }
+ return count;
+}
+
+
+static CLASS_DEVICE_ATTR (mips_info, S_IRUGO,
+ bnx2i_show_mips_status, NULL);
+static CLASS_DEVICE_ATTR (net_if_name, S_IRUGO,
+ bnx2i_show_net_if_name, NULL);
+static CLASS_DEVICE_ATTR (pci_bar, S_IRUGO,
+ bnx2i_show_pci_bar, NULL);
+static CLASS_DEVICE_ATTR (sq_size, S_IRUGO | S_IWUSR,
+ bnx2i_show_sq_info, bnx2i_set_sq_info);
+static CLASS_DEVICE_ATTR (cq_size, S_IRUGO | S_IWUSR,
+ bnx2i_show_cq_info, bnx2i_set_cq_info);
+static CLASS_DEVICE_ATTR (rq_size, S_IRUGO | S_IWUSR,
+ bnx2i_show_rq_info, bnx2i_set_rq_info);
+static CLASS_DEVICE_ATTR (num_ccell, S_IRUGO | S_IWUSR,
+ bnx2i_show_ccell_info, bnx2i_set_ccell_info);
+static CLASS_DEVICE_ATTR (pci_trigger, S_IRUGO,
+ bnx2i_read_pci_trigger_reg, NULL);
+static CLASS_DEVICE_ATTR (ctx_dump, S_IRUGO | S_IWUSR,
+ bnx2i_get_iscsi_cntx_dump,
+ bnx2i_select_iscsi_cntx_dump);
+static CLASS_DEVICE_ATTR (cid_list, S_IRUGO | S_IWUSR,
+ bnx2i_get_active_iscsi_cid_list,
+ bnx2i_set_iscsi_cid_err_poll_mode);
+static CLASS_DEVICE_ATTR (qp_shmem_dump, S_IRUGO | S_IWUSR,
+ bnx2i_get_qp_shmem_dump,
+ bnx2i_setup_qp_shmem_dump);
+static CLASS_DEVICE_ATTR (port_bind, S_IRUGO | S_IWUSR,
+ bnx2i_read_tcp_portd_options,
+ bnx2i_write_tcp_portd_results);
+
+
+static struct class_device_attribute *bnx2i_class_attributes[] = {
+ &class_device_attr_mips_info,
+ &class_device_attr_net_if_name,
+ &class_device_attr_pci_bar,
+ &class_device_attr_sq_size,
+ &class_device_attr_cq_size,
+ &class_device_attr_rq_size,
+ &class_device_attr_num_ccell,
+ &class_device_attr_pci_trigger,
+ &class_device_attr_ctx_dump,
+ &class_device_attr_cid_list,
+ &class_device_attr_qp_shmem_dump,
+};
+
+static struct class_device_attribute *tcp_port_class_attributes[] = {
+ &class_device_attr_port_bind
+};
+
+static void bnx2i_sysfs_release(struct class_device *class_dev)
+{
+}
+
+struct class_device port_class_dev;
+
+
+static struct class bnx2i_class = {
+ .name = "bnx2i",
+ .release = bnx2i_sysfs_release,
+};
+
+
+
+static int bnx2i_register_port_class_dev(struct class_device *class_dev)
+{
+ char dev_name[BUS_ID_SIZE];
+ int ret;
+ int i;
+
+ class_dev->class = &bnx2i_class;
+ class_dev->class_data = class_dev;
+ snprintf(dev_name, BUS_ID_SIZE, "%s", "tcp_portd");
+ strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
+
+ ret = class_device_register(class_dev);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(tcp_port_class_attributes); ++i) {
+ ret = class_device_create_file(class_dev,
+ tcp_port_class_attributes[i]);
+ if (ret)
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ class_device_unregister(class_dev);
+err:
+ return ret;
+}
+
+
+int bnx2i_register_sysfs(struct bnx2i_hba *hba)
+{
+ struct class_device *class_dev = &hba->class_dev;
+ char dev_name[BUS_ID_SIZE];
+ int ret;
+ int i;
+
+ class_dev->class = &bnx2i_class;
+ class_dev->class_data = hba;
+ snprintf(dev_name, BUS_ID_SIZE, "%.2x:%.2x.%.1x",
+ hba->pci_dev->bus->number,
+ PCI_SLOT(hba->pci_dev->devfn),
+ PCI_FUNC(hba->pci_dev->devfn));
+ strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
+
+ ret = class_device_register(class_dev);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(bnx2i_class_attributes); ++i) {
+ ret = class_device_create_file(class_dev,
+ bnx2i_class_attributes[i]);
+ if (ret)
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ class_device_unregister(class_dev);
+err:
+ return ret;
+}
+
+void bnx2i_unregister_sysfs(struct bnx2i_hba *hba)
+{
+ class_device_unregister(&hba->class_dev);
+}
+
+int bnx2i_sysfs_setup(void)
+{
+ int ret;
+ ret = class_register(&bnx2i_class);
+
+ bnx2i_register_port_class_dev(&port_class_dev);
+ return ret;
+}
+
+void bnx2i_sysfs_cleanup(void)
+{
+ class_device_unregister(&port_class_dev);
+ class_unregister(&bnx2i_class);
+}
^ permalink raw reply related
* Re: [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Kok, Auke @ 2007-08-04 23:04 UTC (permalink / raw)
To: Bill Fink
Cc: jeff, netdev, ayyappan.veeraiyan, akpm, arjan, hch, shemminger,
nhorman, inaky, mb, john.ronciak
In-Reply-To: <20070804150401.afb6b709.billfink@mindspring.com>
Bill Fink wrote:
> On Fri, 3 Aug 2007, Auke Kok wrote:
>> +
>> +Contents
>> +========
>> +
>> +- In This Release
>> +- Identifying Your Adapter
>> +- Command Line Parameters
>
> There is no section "Command Line Parameters" in the document.
>
> -Bill
hmm yes, I removed all of them :)
Obviously I'll update the index. Thanks
Auke
^ permalink raw reply
* Re: [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Bill Fink @ 2007-08-04 19:04 UTC (permalink / raw)
To: Auke Kok
Cc: jeff, netdev, ayyappan.veeraiyan, akpm, arjan, hch, shemminger,
nhorman, inaky, mb, john.ronciak
In-Reply-To: <20070804040502.D9133F2C0F@doppio.foo-projects.org>
On Fri, 3 Aug 2007, Auke Kok wrote:
> This patch adds support for the Intel 82598 PCI-Express 10GbE
> chipset. Devices will be available on the market soon.
>
> This version of the driver is largely the same as the last release:
>
> * Driver uses a single RX and single TX queue, each using 1 MSI-X
> irq vector.
> * Driver runs in NAPI mode only
> * Driver is largely multiqueue-ready (TM)
...
> diff --git a/Documentation/networking/ixgbe.txt b/Documentation/networking/ixgbe.txt
> new file mode 100644
> index 0000000..823d69c
> --- /dev/null
> +++ b/Documentation/networking/ixgbe.txt
> @@ -0,0 +1,72 @@
> +Linux* Base Driver for the 10 Gigabit Family of Adapters
> +================================================================
> +
> +July 09, 2007
> +
> +
> +Contents
> +========
> +
> +- In This Release
> +- Identifying Your Adapter
> +- Command Line Parameters
There is no section "Command Line Parameters" in the document.
-Bill
> +- Support
> +
> +In This Release
> +===============
> +
> +This file describes the Linux* Base Driver for the 10 Gigabit PCI Express
> +Family of Adapters. This driver supports the 2.6.x kernel. This driver
> +includes support for Itanium(R)2-based systems.
> +
> +The following features are now available in supported kernels:
> + - Native VLANs
> + - Channel Bonding (teaming)
> + - SNMP
> +
> +Channel Bonding documentation can be found in the Linux kernel source:
> +/Documentation/networking/bonding.txt
> +
> +Instructions on updating ethtool can be found in the section "Additional
> +Configurations" later in this document.
> +
> +
> +Identifying Your Adapter
> +========================
> +
> +The following Intel network adapters are compatible with the drivers in this
> +release:
> +
> +Controller Adapter Name Physical Layer
> +---------- ------------ --------------
> +82598 Intel(R) 10GbE-LR/LRM/SR
> + Server Adapters 10G Base -SR (850 nm optical fiber)
> + 10G Base -LRM (850 nm optical fiber)
> + 10G Base -LR (1310 nm optical fiber)
> +
> +For more information on how to identify your adapter, go to the Adapter &
> +Driver ID Guide at:
> +
> + http://support.intel.com/support/network/sb/CS-012904.htm
> +
> +For the latest Intel network drivers for Linux, refer to the following
> +website. In the search field, enter your adapter name or type, or use the
> +networking link on the left to search for your adapter:
> +
> + http://downloadfinder.intel.com/scripts-df/support_intel.asp
> +
> +
> +Support
> +=======
> +
> +For general information, go to the Intel support website at:
> +
> + http://support.intel.com
> +
> +or the Intel Wired Networking project hosted by Sourceforge at:
> +
> + http://sourceforge.net/projects/e1000
> +
> +If an issue is identified with the released source code on the supported
> +kernel with a supported adapter, email the specific information related
> +to the issue to e1000-devel@lists.sf.net
^ permalink raw reply
* [PATCH][RESEND] Avoid possible NULL pointer deref in 3c359 driver
From: Jesper Juhl @ 2007-08-04 18:31 UTC (permalink / raw)
To: Andrew Morton
Cc: Linux Kernel Mailing List, Mike Phillips, netdev, linux-tr,
Jesper Juhl, davem
(Resending old patch originally submitted at 1/7-2007 02:19)
In xl_freemem(), if dev_if is NULL, the line
struct xl_private *xl_priv =(struct xl_private *)dev->priv;
will cause a NULL pointer dereference. However, if we move
that assignment below the 'if' statement that tests for a NULL
'dev', then that NULL deref can never happen.
It never hurts to be safe :-)
Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com>
---
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index e22a3f5..671f4da 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -1044,15 +1044,17 @@ static void xl_freemem(struct net_device *dev)
static irqreturn_t xl_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
- struct xl_private *xl_priv =(struct xl_private *)dev->priv;
- u8 __iomem * xl_mmio = xl_priv->xl_mmio ;
- u16 intstatus, macstatus ;
+ struct xl_private *xl_priv;
+ u8 __iomem * xl_mmio;
+ u16 intstatus, macstatus;
if (!dev) {
- printk(KERN_WARNING "Device structure dead, aaahhhh !\n") ;
+ printk(KERN_WARNING "3c359: Device structure dead, aaahhhh!\n");
return IRQ_NONE;
}
+ xl_priv = (struct xl_private *)dev->priv;
+ xl_mmio = xl_priv->xl_mmio;
intstatus = readw(xl_mmio + MMIO_INTSTATUS) ;
if (!(intstatus & 1)) /* We didn't generate the interrupt */
^ permalink raw reply related
* Re: [PATCH 00/23] per device dirty throttling -v8
From: david @ 2007-08-04 17:15 UTC (permalink / raw)
To: Ray Lee
Cc: Ingo Molnar, Linus Torvalds, Peter Zijlstra, linux-mm,
linux-kernel, miklos, akpm, neilb, dgc, tomoki.sekiyama.qu,
nikita, trond.myklebust, yingchao.zhou, richard, netdev
In-Reply-To: <2c0942db0708040901x7ada0fe2mf71f37ecba51005b@mail.gmail.com>
On Sat, 4 Aug 2007, Ray Lee wrote:
> (adding netdev cc:)
>
> On 8/4/07, david@lang.hm <david@lang.hm> wrote:
>> On Sat, 4 Aug 2007, Ingo Molnar wrote:
>>
>>> * Ingo Molnar <mingo@elte.hu> wrote:
>>>
>>>> There are positive reports in the never-ending "my system crawls like
>>>> an XT when copying large files" bugzilla entry:
>>>>
>>>> http://bugzilla.kernel.org/show_bug.cgi?id=7372
>>>
>>> i forgot this entry:
>>>
>>> " We recently upgraded our office to gigabit Ethernet and got some big
>>> AMD64 / 3ware boxes for file and vmware servers... only to find them
>>> almost useless under any kind of real load. I've built some patched
>>> 2.6.21.6 kernels (using the bdi throttling patch you mentioned) to
>>> see if our various Debian Etch boxes run better. So far my testing
>>> shows a *great* improvement over the stock Debian 2.6.18 kernel on
>>> our configurations. "
>>>
>>> and bdi has been in -mm in the past i think, so we also know (to a
>>> certain degree) that it does not hurt those workloads that are fine
>>> either.
>>>
>>> [ my personal interest in this is the following regression: every time i
>>> start a large kernel build with DEBUG_INFO on a quad-core 4GB RAM box,
>>> i get up to 30 seconds complete pauses in Vim (and most other tasks),
>>> during plain editing of the source code. (which happens when Vim tries
>>> to write() to its swap/undo-file.) ]
>>
>> I have an issue that sounds like it's related.
>>
>> I've got a syslog server that's got two Opteron 246 cpu's, 16G ram, 2x140G
>> 15k rpm drives (fusion MPT hardware mirroring), 16x500G 7200rpm SATA
>> drives on 3ware 9500 cards (software raid6) running 2.6.20.3 with hz set
>> at default and preempt turned off.
>>
>> I have syslog doing buffered writes to the SCSI drives and every 5 min a
>> cron job copies the data to the raid array.
>>
>> I've found that if I do anything significant on the large raid array that
>> the system looses a significant amount of the UDP syslog traffic, even
>> though there should be pleanty of ram and cpu (and the spindles involved
>> in the writes are not being touched), even a grep can cause up to 40%
>> losses in the syslog traffic. I've experimented with nice levels (nicing
>> down the grep and nicing up the syslogd) without a noticable effect on the
>> losses.
>>
>> I've been planning to try a new kernel with hz=1000 to see if that would
>> help, and after that experiment with the various preempt settings, but it
>> sounds like the per-device queues may actually be more relavent to the
>> problem.
>>
>> what would you suggest I test, and in what order and combination?
>
> At least on a surface level, your report has some similarities to
> http://lkml.org/lkml/2007/5/21/84 . In that message, John Miller
> mentions several things he tried without effect:
>
> < - I increased the max allowed receive buffer through
> < proc/sys/net/core/rmem_max and the application calls the right
> < syscall. "netstat -su" does not show any "packet receive errors".
> <
> < - After getting "kernel: swapper: page allocation failure.
> < order:0, mode:0x20", I increased /proc/sys/vm/min_free_kbytes
> <
> < - ixgb.txt in kernel network documentation suggests to increase
> < net.core.netdev_max_backlog to 300000. This did not help.
> <
> < - I also had to increase net.core.optmem_max, because the default
> < value was too small for 700 multicast groups.
>
> As they're all pretty simple to test, it may be worthwhile to give
> them a shot just to rule things out.
I will try them later today.
I forgot to mention that the filesystems are ext2 for the mirrored high
speed disks and xfs for the 8TB array.
David Lang
^ permalink raw reply
* Re: source interface ping bug ?
From: nano bug @ 2007-08-04 17:16 UTC (permalink / raw)
To: netdev
In-Reply-To: <d39c36500708021301i6754cb6bh4ba1a2a85092ce3c@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2165 bytes --]
Hello, any news about this ?
On 8/2/07, nano bug <linnewbye@gmail.com> wrote:
> ---------- Forwarded message ----------
> From: nano bug <linnewbye@gmail.com>
> Date: Aug 2, 2007 10:56 PM
> Subject: Re: source interface ping bug ?
> To: Ben Greear <greearb@candelatech.com>
>
>
> Hello,
>
> Sorry for the late reply, I have atached the strace output of eth0 and
> eth2 on kernel 2.6.20 and 2.6.22
>
> On 7/30/07, Ben Greear <greearb@candelatech.com> wrote:
> > nano bug wrote:
> > > Can someone have a look a this and tell if it's kernel related or if I
> > > posted this in the wrong place ? Thanks.
> > >
> > Last I checked, ping did not do an SO_BINDTODEVICE even if you did -i ethX.
> > I think it just looked up the IP for that port and treated it as -i a.b.c.d.
> >
> > That said, I'm not sure why the behaviour changes for you between kernel
> > releases.
> >
> > Maybe an 'strace' of your ping command on the different kernels would help
> > figure out what the problem is?
> >
> > Ben
> >
> > --
> > Ben Greear <greearb@candelatech.com>
> > Candela Technologies Inc http://www.candelatech.com
On 8/2/07, nano bug <linnewbye@gmail.com> wrote:
> ---------- Forwarded message ----------
> From: nano bug <linnewbye@gmail.com>
> Date: Aug 2, 2007 10:58 PM
> Subject: Re: source interface ping bug ?
> To: Patrick McHardy <kaber@trash.net>
>
>
> Hello,
>
> Yes I'm running NAT, I have atached the output of the iptables -t nat
> -vxnL command and the routing tables
>
> On 7/30/07, Patrick McHardy <kaber@trash.net> wrote:
> > nano bug wrote:
> > > [...]
> > > using source interface :
> > >
> > > root@darkstar:~/iputils# ./ping -I eth2 87.248.113.14
> > > PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 eth2: 56(84) bytes of data.
> > >>From 86.106.19.75 icmp_seq=1 Destination Host Unreachable
> >
> > > root@darkstar:~# tcpdump -i eth2 -vvv -n host 87.248.113.14 and host
> > > 86.106.19.75
> > > tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 96 bytes
> > > 01:19:24.292911 arp who-has 87.248.113.14 tell 86.106.19.75
> >
> >
> > Are you using (or running) NAT locally? What do your routing tables look
> > like?
> >
>
>
[-- Attachment #2: route_tables --]
[-- Type: application/octet-stream, Size: 2032 bytes --]
root@darkstar:~#
root@darkstar:~#
root@darkstar:~# ip route show
80.97.71.0/24 dev eth0 proto kernel scope link src 80.97.71.23
10.10.10.0/24 dev eth1 proto kernel scope link src 10.10.10.1
86.106.18.0/23 dev eth2 scope link
127.0.0.0/8 dev lo scope link
root@darkstar:~# ip rule show
0: from all lookup local
50: from all lookup main
100: from 80.97.71.0/24 lookup 201
101: from 86.106.18.0/23 lookup 202
102: from all lookup 222
32766: from all lookup main
32767: from all lookup default
root@darkstar:~# ip route show table 201
default via 80.97.71.1 dev eth0 proto static
prohibit default proto static metric 1
root@darkstar:~# ip route show table 202
default via 86.106.18.1 dev eth2 proto static
prohibit default proto static metric 1
root@darkstar:~# ip route show table 222
default proto static
nexthop via 80.97.71.1 dev eth0 weight 1
nexthop via 86.106.18.1 dev eth2 weight 99
prohibit default proto static metric 1
root@darkstar:~# iptables -V
iptables v1.3.8
root@darkstar:~# iptables -t nat -xvnL
Chain PREROUTING (policy ACCEPT 2120 packets, 298134 bytes)
pkts bytes target prot opt in out source destination
1 48 DNAT tcp -- eth2 * 0.0.0.0/0 0.0.0.0/0 tcp dpts:18856:18870 to:172.16.2.1
0 0 DNAT udp -- eth2 * 0.0.0.0/0 0.0.0.0/0 udp dpts:18856:18870 to:172.16.2.1
Chain POSTROUTING (policy ACCEPT 56 packets, 4568 bytes)
pkts bytes target prot opt in out source destination
2 138 MASQUERADE all -- * * 10.10.10.0/24 0.0.0.0/0
0 0 MASQUERADE all -- * * 172.16.1.0/24 0.0.0.0/0
0 0 MASQUERADE all -- * * 172.16.2.0/24 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 55 packets, 4520 bytes)
pkts bytes target prot opt in out source destination
root@darkstar:~#
[-- Attachment #3: strace_eth0_2.6.20 --]
[-- Type: application/octet-stream, Size: 13375 bytes --]
execve("./ping", ["./ping", "-I", "eth0", "87.248.113.14", "-c", "10"], [/* 28 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f2f000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f2e000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7f1b000
mmap2(0xb7f2a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7f2a000
mmap2(0xb7f2c000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f2c000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7dd9000
mmap2(0xb7f15000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7f15000
mmap2(0xb7f18000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f18000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7dd8000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7dd86c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7f15000, 4096, PROT_READ) = 0
munmap(0xb7f2f000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth0\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32778), sin_addr=inet_addr("80.97.71.23")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth0", ifr_index=1}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f37000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 2170
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083994, 673594}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfe0bb88) = -1 EINVAL (Invalid argument)
gettimeofday({1186083994, 673892}, NULL) = 0
gettimeofday({1186083994, 674001}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0k/z\10\0\1\2324\262F\321H\n\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\341\"\0\0006\1C\10W\370q\16PaG\27\0\0s/z\10\0\1"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "PING 87.248.113.14 (87.248.113.1"..., 140PING 87.248.113.14 (87.248.113.14) from 80.97.71.23 eth0: 56(84) bytes of data.
64 bytes from 87.248.113.14: icmp_seq=1 ttl=54 time=63.1 ms
) = 140
gettimeofday({1186083994, 740380}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 934) = 0
gettimeofday({1186083995, 672555}, NULL) = 0
sched_yield() = 0
recvmsg(3, 0xbfe0bdb8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083995, 675887}, NULL) = 0
gettimeofday({1186083995, 676770}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\231#z\10\0\2\2334\262F\242S\n\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\353\32\0\0006\0019\20W\370q\16PaG\27\0\0\241#z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=2 ttl=54 time=67.1 ms
) = 60
gettimeofday({1186083995, 746675}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 930) = 0
gettimeofday({1186083996, 671678}, NULL) = 0
sched_yield() = 0
recvmsg(3, 0xbfe0bdb8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083996, 675088}, NULL) = 0
gettimeofday({1186083996, 676068}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0V%z\10\0\3\2344\262F\344P\n\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\372\341\0\0006\1)IW\370q\16PaG\27\0\0^%z\10\0\3"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=3 ttl=54 time=70.6 ms
) = 60
gettimeofday({1186083996, 749536}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 926) = 0
gettimeofday({1186083997, 671550}, NULL) = 0
sched_yield() = 0
recvmsg(3, 0xbfe0bdb8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083997, 674843}, NULL) = 0
gettimeofday({1186083997, 675844}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0005%z\10\0\4\2354\262F\4P\n\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\6\340\0\0006\1\35KW\370q\16PaG\27\0\0=%z\10\0\4"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=4 ttl=54 time=63.6 ms
) = 60
gettimeofday({1186083997, 742365}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 933) = 0
gettimeofday({1186083998, 680849}, NULL) = 0
gettimeofday({1186083998, 681944}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0`\fz\10\0\5\2364\262F\330g\n\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\20\366\0\0006\1\0235W\370q\16PaG\27\0\0h\fz\10\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=5 ttl=54 time=65.4 ms
) = 60
gettimeofday({1186083998, 750410}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083999, 690220}, NULL) = 0
gettimeofday({1186083999, 691307}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\313\346z\10\0\6\2374\262Fk\214\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\33\5\0\0006\1\t&W\370q\16PaG\27\0\0\323\346z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=6 ttl=54 time=61.6 ms
) = 60
gettimeofday({1186083999, 755718}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 935) = 0
gettimeofday({1186084000, 689662}, NULL) = 0
gettimeofday({1186084000, 690769}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\344\347z\10\0\7\2404\262FQ\212\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T%0\0\0006\1\376\372W\370q\16PaG\27\0\0\354\347z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=7 ttl=54 time=65.6 ms
) = 60
gettimeofday({1186084000, 759246}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186084001, 699157}, NULL) = 0
gettimeofday({1186084001, 700258}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\322\301z\10\0\10\2414\262Fb\257\n\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T.\355\0\0006\1\365=W\370q\16PaG\27\0\0\332\301z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=8 ttl=54 time=71.1 ms
) = 60
gettimeofday({1186084001, 774234}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 925) = 0
gettimeofday({1186084002, 698648}, NULL) = 0
gettimeofday({1186084002, 699750}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\315\302z\10\0\t\2424\262Ff\255\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T9\6\0\0006\1\353$W\370q\16PaG\27\0\0\325\302z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=9 ttl=54 time=72.0 ms
) = 60
gettimeofday({1186084002, 774597}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 925) = 0
gettimeofday({1186084003, 698164}, NULL) = 0
gettimeofday({1186084003, 699284}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\236\303z\10\0\n\2434\262F\224\253\n\0\10\t\n\v\f"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={1, 0}}, NULL) = 0
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TB\362\0\0006\1\3418W\370q\16PaG\27\0\0\246\303z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6164 bytes from 87.248.113.14: icmp_seq=10 ttl=54 time=74.0 ms
) = 61
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 156--- 87.248.113.14 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9024ms
rtt min/avg/max/mdev = 61.605/67.447/74.038/4.053 ms
) = 156
exit_group(0) = ?
Process 2170 detached
[-- Attachment #4: strace_eth0_2.6.22 --]
[-- Type: application/octet-stream, Size: 13336 bytes --]
execve("./ping", ["./ping", "-I", "eth0", "87.248.113.14", "-c", "10"], [/* 29 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fd7000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fd6000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7fc3000
mmap2(0xb7fd2000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7fd2000
mmap2(0xb7fd4000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fd4000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e81000
mmap2(0xb7fbd000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7fbd000
mmap2(0xb7fc0000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fc0000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e80000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e806c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fbd000, 4096, PROT_READ) = 0
munmap(0xb7fd7000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth0\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32794), sin_addr=inet_addr("80.97.71.23")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth0", ifr_index=1}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fdf000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 3133
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083746, 626252}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbff7ecf8) = -1 EINVAL (Invalid argument)
gettimeofday({1186083746, 628890}, NULL) = 0
gettimeofday({1186083746, 630093}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0$\330=\f\0\1\2423\262FM\235\t\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\371~\0\0006\1*\254W\370q\16PaG\27\0\0,\330=\f\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "PING 87.248.113.14 (87.248.113.1"..., 140PING 87.248.113.14 (87.248.113.14) from 80.97.71.23 eth0: 56(84) bytes of data.
64 bytes from 87.248.113.14: icmp_seq=1 ttl=54 time=71.7 ms
) = 140
gettimeofday({1186083746, 705534}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR, revents=POLLIN}], 1, 924) = 1
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("80.97.71.1")}, msg_iov(1)=[{"E\0\0T\0\0@\0\377\1L\316PaG\1PaG\27\0\0\277s\324\v\0\220"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, MSG_DONTWAIT) = 84
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\10\0\0\0\300\372\4\10", 8) = 0
gettimeofday({1186083747, 576174}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 52) = 0
gettimeofday({1186083747, 633886}, NULL) = 0
gettimeofday({1186083747, 633994}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\346\307=\f\0\2\2433\262F\212\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\3J\0\0006\1 \341W\370q\16PaG\27\0\0\356\307=\f\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=2 ttl=54 time=79.0 ms
) = 60
gettimeofday({1186083747, 713584}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 921) = 0
gettimeofday({1186083748, 633914}, NULL) = 0
gettimeofday({1186083748, 634023}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\310\306=\f\0\3\2443\262F\247\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\ra\0\0006\1\26\312W\370q\16PaG\27\0\0\320\306=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=3 ttl=54 time=65.7 ms
) = 60
gettimeofday({1186083748, 700301}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 934) = 0
gettimeofday({1186083749, 633921}, NULL) = 0
gettimeofday({1186083749, 634029}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\301\305=\f\0\4\2453\262F\255\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\27\320\0\0006\1\f[W\370q\16PaG\27\0\0\311\305=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=4 ttl=54 time=68.8 ms
) = 60
gettimeofday({1186083749, 703430}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083750, 633923}, NULL) = 0
gettimeofday({1186083750, 634032}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\275\304=\f\0\5\2463\262F\260\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\"\26\0\0006\1\2\25W\370q\16PaG\27\0\0\305\304=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=5 ttl=54 time=69.3 ms
) = 60
gettimeofday({1186083750, 703966}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 930) = 0
gettimeofday({1186083751, 633940}, NULL) = 0
gettimeofday({1186083751, 634050}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\252\303=\f\0\6\2473\262F\302\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T,\272\0\0006\1\367pW\370q\16PaG\27\0\0\262\303=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=6 ttl=54 time=64.0 ms
) = 60
gettimeofday({1186083751, 698661}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 936) = 0
gettimeofday({1186083752, 633964}, NULL) = 0
gettimeofday({1186083752, 634073}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\222\302=\f\0\7\2503\262F\331\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T7>\0\0006\1\354\354W\370q\16PaG\27\0\0\232\302=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=7 ttl=54 time=64.5 ms
) = 60
gettimeofday({1186083752, 699205}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 935) = 0
gettimeofday({1186083753, 633955}, NULL) = 0
gettimeofday({1186083753, 634064}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\232\301=\f\0\10\2513\262F\320\254\t\0\10\t\n\v\f"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TA\236\0\0006\1\342\214W\370q\16PaG\27\0\0\242\301"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=8 ttl=54 time=62.7 ms
) = 60
gettimeofday({1186083753, 697399}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 937) = 0
gettimeofday({1186083754, 633961}, NULL) = 0
gettimeofday({1186083754, 634070}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\223\300=\f\0\t\2523\262F\326\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TL-\0\0006\1\327\375W\370q\16PaG\27\0\0\233\300=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=9 ttl=54 time=68.6 ms
) = 60
gettimeofday({1186083754, 703225}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083755, 633971}, NULL) = 0
gettimeofday({1186083755, 634079}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\211\277=\f\0\n\2533\262F\337\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={1, 0}}, NULL) = 0
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TVE\0\0006\1\315\345W\370q\16PaG\27\0\0\221\277=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6164 bytes from 87.248.113.14: icmp_seq=10 ttl=54 time=63.9 ms
) = 61
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 156--- 87.248.113.14 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9007ms
rtt min/avg/max/mdev = 62.799/67.886/79.054/4.651 ms
) = 156
exit_group(0) = ?
Process 3133 detached
[-- Attachment #5: strace_eth2_2.6.20 --]
[-- Type: application/octet-stream, Size: 13335 bytes --]
execve("./ping", ["./ping", "-I", "eth2", "87.248.113.14", "-c", "10"], [/* 26 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fcb000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fca000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7fb7000
mmap2(0xb7fc6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7fc6000
mmap2(0xb7fc8000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fc8000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e75000
mmap2(0xb7fb1000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7fb1000
mmap2(0xb7fb4000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fb4000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e74000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e746c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fb1000, 4096, PROT_READ) = 0
munmap(0xb7fcb000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32778), sin_addr=inet_addr("86.106.19.75")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth2", ifr_index=3}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fd3000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 2113
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083952, 706356}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfb8ce78) = -1 EINVAL (Invalid argument)
gettimeofday({1186083952, 706639}, NULL) = 0
gettimeofday({1186083952, 706742}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\350\257A\10\0\1p4\262F\266\310\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T6\371\0\0005\1\33mW\370q\16Vj\23K\0\0\360\257A"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "PING 87.248.113.14 (87.248.113.1"..., 141PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 eth2: 56(84) bytes of data.
64 bytes from 87.248.113.14: icmp_seq=1 ttl=53 time=75.1 ms
) = 141
gettimeofday({1186083952, 785127}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 922) = 0
gettimeofday({1186083953, 713300}, NULL) = 0
gettimeofday({1186083953, 714212}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\271\221A\10\0\2q4\262F\344\345\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0TAL\0\0005\1\21\32W\370q\16Vj\23K\0\0\301\221A\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=2 ttl=53 time=70.6 ms
) = 60
gettimeofday({1186083953, 787144}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 927) = 0
gettimeofday({1186083954, 712881}, NULL) = 0
gettimeofday({1186083954, 713737}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\223\222A\10\0\3r4\262F\t\344\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0TK\223\0\0005\1\6\323W\370q\16Vj\23K\0\0\233\222"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=3 ttl=53 time=71.5 ms
) = 60
gettimeofday({1186083954, 787613}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 926) = 0
gettimeofday({1186083955, 712260}, NULL) = 0
gettimeofday({1186083955, 713116}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\377\223A\10\0\4s4\262F\234\341\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0TU\254\0\0005\1\374\271W\370q\16Vj\23K\0\0\7\224"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=4 ttl=53 time=67.4 ms
) = 60
gettimeofday({1186083955, 781925}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083956, 721775}, NULL) = 0
gettimeofday({1186083956, 722662}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\264mA\10\0\5t4\262F\346\6\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T`\37\0\0005\1\362FW\370q\16Vj\23K\0\0\274mA\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=5 ttl=53 time=68.2 ms
) = 60
gettimeofday({1186083956, 793146}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 929) = 0
gettimeofday({1186083957, 721304}, NULL) = 0
gettimeofday({1186083957, 722165}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\244nA\10\0\6u4\262F\365\4\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0Tj\26\0\0005\1\350OW\370q\16Vj\23K\0\0\254nA\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=6 ttl=53 time=69.7 ms
) = 60
gettimeofday({1186083957, 792929}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 929) = 0
gettimeofday({1186083958, 720785}, NULL) = 0
gettimeofday({1186083958, 721642}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\256oA\10\0\7v4\262F\352\2\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0Tt/\0\0005\1\3366W\370q\16Vj\23K\0\0\266oA\10\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=7 ttl=53 time=71.7 ms
) = 60
gettimeofday({1186083958, 795674}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 926) = 0
gettimeofday({1186083959, 720275}, NULL) = 0
gettimeofday({1186083959, 721132}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\253pA\10\0\10w4\262F\354\0\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T}\330\0\0005\1\324\215W\370q\16Vj\23K\0\0\263p"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=8 ttl=53 time=66.7 ms
) = 60
gettimeofday({1186083959, 789802}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083960, 729784}, NULL) = 0
gettimeofday({1186083960, 730641}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\205JA\10\0\tx4\262F\21&\v\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T\207\350\0\0005\1\312}W\370q\16Vj\23K\0\0\215J"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=9 ttl=53 time=67.2 ms
) = 60
gettimeofday({1186083960, 800065}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR, revents=POLLIN}], 1, 930) = 1
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("80.97.71.1")}, msg_iov(1)=[{"E\0\0T\0\0@\0\377\1L\316PaG\1PaG\27\0\0gr\\\10\0\1y4\262"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, MSG_DONTWAIT) = 84
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\10\0\0\0\300\372\4\10", 8) = 0
gettimeofday({1186083961, 341678}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 388) = 0
gettimeofday({1186083961, 729316}, NULL) = 0
gettimeofday({1186083961, 730180}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0QKA\10\0\ny4\262FD$\v\0\10\t\n\v\f\r\16\17\20\21\22"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={1, 0}}, NULL) = 0
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T\222:\0\0005\1\300+W\370q\16Vj\23K\0\0YKA\10\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6164 bytes from 87.248.113.14: icmp_seq=10 ttl=53 time=70.3 ms
) = 61
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 156--- 87.248.113.14 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9022ms
rtt min/avg/max/mdev = 66.726/69.874/75.180/2.477 ms
) = 156
exit_group(0) = ?
Process 2113 detached
[-- Attachment #6: strace_eth2_2.6.22 --]
[-- Type: application/octet-stream, Size: 15575 bytes --]
execve("./ping", ["./ping", "-I", "eth2", "87.248.113.14", "-c", "10"], [/* 29 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7eff000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7efe000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7eeb000
mmap2(0xb7efa000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7efa000
mmap2(0xb7efc000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7efc000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7da9000
mmap2(0xb7ee5000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7ee5000
mmap2(0xb7ee8000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7ee8000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7da8000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7da86c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7ee5000, 4096, PROT_READ) = 0
munmap(0xb7eff000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32794), sin_addr=inet_addr("86.106.19.75")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth2", ifr_index=3}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f07000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 3093
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083700, 834172}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbff2ac98) = -1 EINVAL (Invalid argument)
gettimeofday({1186083700, 836688}, NULL) = 0
gettimeofday({1186083700, 837896}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\274\254\25\f\0\1t3\262F\10\311\f\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("80.97.71.1")}, msg_iov(1)=[{"E\0\0T\0\0@\0\377\1L\316PaG\1PaG\27\0\0a\243\324\v\0bu"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\10\0\0\0\300\372\4\10", 8) = 0
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083701, 583220}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 253) = 0
gettimeofday({1186083701, 843650}, NULL) = 0
gettimeofday({1186083701, 843759}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\324\224\25\f\0\2u3\262F\357\337\f\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083702, 843650}, NULL) = 0
gettimeofday({1186083702, 843766}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\314\223\25\f\0\3v3\262F\366\337\f\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("86.106.19.75")}, msg_iov(1)=[{"E\300\0pEx\0\0@\1`\353Vj\23KVj\23K\3\1\374\376\0\0\0\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 112
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\274\254\25\f\0\1", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_SOURCE_QUENCH|ICMP_REDIRECT), 4) = 0
write(1, "PING 87.248.113.14 (87.248.113.1"..., 139PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 eth2: 56(84) bytes of data.
From 86.106.19.75 icmp_seq=1 Destination Host Unreachable
) = 139
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\324\224\25\f\0\2", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=2 Des"..., 58From 86.106.19.75 icmp_seq=2 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\314\223\25\f\0\3", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=3 Des"..., 58From 86.106.19.75 icmp_seq=3 Destination Host Unreachable
) = 58
gettimeofday({1186083703, 846127}, NULL) = 0
gettimeofday({1186083703, 846230}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0+\211\25\f\0\4w3\262F\226\351\f\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("86.106.19.75")}, msg_iov(1)=[{"E\300\0pEy\0\0@\1`\352Vj\23KVj\23K\3\1\374\376\0\0\0\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 112
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("86.106.19.75")}, msg_iov(1)=[{"E\300\0pEz\0\0@\1`\351Vj\23KVj\23K\3\1\374\376\0\0\0\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, MSG_DONTWAIT) = 112
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083703, 847416}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 999) = 0
gettimeofday({1186083704, 843581}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 10) = 0
gettimeofday({1186083704, 853609}, NULL) = 0
gettimeofday({1186083704, 853719}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\351j\25\f\0\5x3\262F\327\6\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083705, 853658}, NULL) = 0
gettimeofday({1186083705, 853772}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\263i\25\f\0\6y3\262F\f\7\r\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083706, 853912}, NULL) = 0
gettimeofday({1186083706, 854026}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\264g\25\f\0\7z3\262F\n\10\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0+\211\25\f\0\4", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=4 Des"..., 58From 86.106.19.75 icmp_seq=4 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\351j\25\f\0\5", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=5 Des"..., 58From 86.106.19.75 icmp_seq=5 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\263i\25\f\0\6", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=6 Des"..., 58From 86.106.19.75 icmp_seq=6 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083706, 856631}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 998) = 0
gettimeofday({1186083707, 853868}, NULL) = 0
gettimeofday({1186083707, 853975}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\346f\25\f\0\10{3\262F\327\7\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083708, 853620}, NULL) = 0
gettimeofday({1186083708, 853735}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\325f\25\f\0\t|3\262F\347\6\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083709, 853616}, NULL) = 0
gettimeofday({1186083709, 853729}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\332e\25\f\0\n}3\262F\341\6\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0
recvmsg(3, 0xbff2aec8, 0) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\346f\25\f\0\10", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=8 Des"..., 58From 86.106.19.75 icmp_seq=8 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\325f\25\f\0\t", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=9 Des"..., 58From 86.106.19.75 icmp_seq=9 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\332e\25\f\0\n", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=10 De"..., 59From 86.106.19.75 icmp_seq=10 Destination Host Unreachable
) = 59
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
--- SIGALRM (Alarm clock) @ 0 (0) ---
sigreturn() = ? (mask now [])
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 124--- 87.248.113.14 ping statistics ---
10 packets transmitted, 0 received, +9 errors, 100% packet loss, time 9019ms
, pipe 4
) = 124
exit_group(1) = ?
Process 3093 detached
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-04 17:03 UTC (permalink / raw)
To: Manu Abraham; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <1a297b360708022204u4fc7603pb6baebe2bdf28618@mail.gmail.com>
On Fri, Aug 03, 2007 at 09:04:51AM +0400, Manu Abraham (abraham.manu@gmail.com) wrote:
> On 7/31/07, Evgeniy Polyakov <johnpol@2ka.mipt.ru> wrote:
>
> > TODO list currently includes following main items:
> > * redundancy algorithm (drop me a request of your own, but it is highly
> > unlikley that Reed-Solomon based will ever be used - it is too slow
> > for distributed RAID, I consider WEAVER codes)
>
>
> LDPC codes[1][2] have been replacing Turbo code[3] with regards to
> communication links and we have been seeing that transition. (maybe
> helpful, came to mind seeing the mention of Turbo code) Don't know how
> weaver compares to LDPC, though found some comparisons [4][5] But
> looking at fault tolerance figures, i guess Weaver is much better.
>
> [1] http://www.ldpc-codes.com/
> [2] http://portal.acm.org/citation.cfm?id=1240497
> [3] http://en.wikipedia.org/wiki/Turbo_code
> [4] http://domino.research.ibm.com/library/cyberdig.nsf/papers/BD559022A190D41C85257212006CEC11/$File/rj10391.pdf
> [5] http://hplabs.hp.com/personal/Jay_Wylie/publications/wylie_dsn2007.pdf
LDPC codes require to solve N order matrix over finite field - exactly
the reason I do not want to use Reed-Solomon codes even with optimized
non-Vandermonde matrix. I will investigate LDPC further though.
Turbo codes are like flow cipher compared to RS codes being block
ciphers. Transport media is reliable in data storages, otherwise they
would not even exist.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: [RFC 0/2][BNX2]: Add iSCSI support to BNX2 devices.
From: Michael Chan @ 2007-08-04 17:02 UTC (permalink / raw)
To: Jeff Garzik
Cc: davem, mchristi, netdev, open-iscsi, Anil Veerabhadrappa,
Tal Moyal, Robert Lusinsky, Uri Elzur
In-Reply-To: <46B43D2D.2090800@garzik.org>
Jeff Garzik wrote:
> Michael Chan wrote:
> > [BNX2]: Add iSCSI support to BNX2 devices.
> >
> > Modify bnx2 and add a cnic driver to support some offload functions
> > needed by iSCSI.
> >
> > Add a new open-iscsi driver to support iSCSI offload on
> bnx2 devices.
> >
> > Signed-off-by: Anil Veerabhadrappa <anilgv@broadcom.com>
> > Signed-off-by: Michael Chan <mchan@broadcom.com>
> >
> > --
> >
> > The complete patch is in:
> >
> >
> >
ftp://Net_sys_anon@ftp1.broadcom.com/0001-BNX2-Add-iSCSI-support-to-BNX2
-devices.patch
>
> > I broke this into 2 patches and omitted the firmware blob in the
next 2
> > emails for review.
> patch #2/2 did not make it (to me personally nor to
> http://marc.info/?l=linux-netdev)
Probably too big. The complete patch is available from FTP above.
I'll try to break it up some more and resend later.
^ permalink raw reply
* Re: strange tcp behavior
From: Evgeniy Polyakov @ 2007-08-04 16:51 UTC (permalink / raw)
To: David Miller; +Cc: simon, john, netdev
In-Reply-To: <20070803.141717.36925406.davem@davemloft.net>
On Fri, Aug 03, 2007 at 02:17:17PM -0700, David Miller (davem@davemloft.net) wrote:
> From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> Date: Fri, 3 Aug 2007 12:22:42 +0400
>
> > Maybe recvmsg should be changed too for symmetry?
>
> I took a look at this, and it's not %100 trivial.
>
> Let's do this later, and only sendmsg for now in order to
> fix the bug in the stable branches.
I've tested your patch, besides there was an offset in one of hooks,
it works perfectly ok.
Feel free to add my ack, tested-by or whatever is needed for this :)
Your patch fixes the problem.
Actually inet_sendmsg() can be renamed to something less misleading,
since it is not used by TCP now.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: strange tcp behavior
From: Evgeniy Polyakov @ 2007-08-04 16:49 UTC (permalink / raw)
To: David Miller; +Cc: simon, john, netdev
In-Reply-To: <20070803.130451.63127177.davem@davemloft.net>
On Fri, Aug 03, 2007 at 01:04:51PM -0700, David Miller (davem@davemloft.net) wrote:
> From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> Date: Fri, 3 Aug 2007 12:22:42 +0400
>
> > On Thu, Aug 02, 2007 at 07:21:34PM -0700, David Miller (davem@davemloft.net) wrote:
> > > What in the world are we doing allowing stream sockets to autobind?
> > > That is totally bogus. Even if we autobind, that won't make a connect
> > > happen.
> >
> > For accepted socket it is perfectly valid assumption - we could autobind
> > it during the first send. Or may bind it during accept. Its a matter of
> > taste I think. Autobinding during first sending can end up being a
> > protection against DoS in some obscure rare case...
>
> accept()ed socket is by definition fully bound and already in
> established state.
That what I meant - it binds during accept (well it can not be called
real binding), but could be autobound during first send to needed port.
Maybe that was one of intentions, don't know.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-04 16:44 UTC (permalink / raw)
To: Daniel Phillips; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <200708031741.18404.phillips@phunq.net>
Hi Daniel.
> On Tuesday 31 July 2007 10:13, Evgeniy Polyakov wrote:
> > * storage can be formed on top of remote nodes and be exported
> > simultaneously (iSCSI is peer-to-peer only, NBD requires device
> > mapper and is synchronous)
>
> In fact, NBD has nothing to do with device mapper. I use it as a
> physical target underneath ddraid (a device mapper plugin) just like I
> would use your DST if it proves out.
I meant to create a storage on top of several nodes one needs to have
device mapper or something like that on top of NBD itself. To further
export resulted device one needs another userspace NDB application and
so on. DST simplifies that greatly.
DST original code worked as device mapper plugin too, but its two
additional allocations (io and clone) per block request ended up for me
as a show stopper.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-04 16:37 UTC (permalink / raw)
To: Daniel Phillips; +Cc: netdev, linux-kernel, linux-fsdevel, Peter Zijlstra
In-Reply-To: <200708031819.17039.phillips@phunq.net>
On Fri, Aug 03, 2007 at 06:19:16PM -0700, Daniel Phillips (phillips@phunq.net) wrote:
> It depends on the characteristics of the physical and virtual block
> devices involved. Slow block devices can produce surprising effects.
> Ddsnap still qualifies as "slow" under certain circumstances (big
> linear write immediately following a new snapshot). Before we added
> throttling we would see as many as 800,000 bios in flight. Nice to
Mmm, sounds tasty to work with such a system :)
> know the system can actually survive this... mostly. But memory
> deadlock is a clear and present danger under those conditions and we
> did hit it (not to mention that read latency sucked beyond belief).
>
> Anyway, we added a simple counting semaphore to throttle the bio traffic
> to a reasonable number and behavior became much nicer, but most
> importantly, this satisfies one of the primary requirements for
> avoiding block device memory deadlock: a strictly bounded amount of bio
> traffic in flight. In fact, we allow some bounded number of
> non-memalloc bios *plus* however much traffic the mm wants to throw at
> us in memalloc mode, on the assumption that the mm knows what it is
> doing and imposes its own bound of in flight bios per device. This
> needs auditing obviously, but the mm either does that or is buggy. In
> practice, with this throttling in place we never saw more than 2,000 in
> flight no matter how hard we hit it, which is about the number we were
> aiming at. Since we draw our reserve from the main memalloc pool, we
> can easily handle 2,000 bios in flight, even under extreme conditions.
>
> See:
> http://zumastor.googlecode.com/svn/trunk/ddsnap/kernel/dm-ddsnap.c
> down(&info->throttle_sem);
>
> To be sure, I am not very proud of this throttling mechanism for various
> reasons, but the thing is, _any_ throttling mechanism no matter how
> sucky solves the deadlock problem. Over time I want to move the
make_request_fn is always called in process context, we can wait in it
for memory in mempool. Although that means we already in trouble.
I agree, any kind of high-boundary leveling must be implemented in
device itself, since block layer does not know what device is at the end
and what it will need to process given block request.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Linksys Gigabit USB2.0 adapter (asix) regression
From: Erik Slagter @ 2007-08-04 16:24 UTC (permalink / raw)
To: David Hollis; +Cc: netdev
In-Reply-To: <1185820074.8086.4.camel@dhollis-lnx.sunera.com>
[-- Attachment #1.1: Type: text/plain, Size: 1103 bytes --]
David Hollis wrote:
>> They are either garbled are they are not passed on the wire. The
>> transmitted packets are shown by tshark, but a tshark run on "the other
>> end of the line" does not show them.
>>
>> Platform is indeed x86, to be precise: fedora 7, kernel 2.6.22-rc6, cpu
>> pentium M, dell laptop inspiron 9300, ICH6.
>>
>> If you want me to test something please yell, it's no trouble at all to
>> change a few lines in the driver's source and recompile the module.
>>
> Could you send me a complete dmesg dump when the driver is compiled with
> DEBUG enabled (at least from then usb logs that the device was inserted
> to the end). I'll need to see what it reports the values of the
> registers.
Please see attachment.
> Have you tried using the F7 2.6.22 kernel? I know that has worked fine
> for me on my system.
I tried vanilla 2.6.22 and now 2.6.23-rc1
>> Please note I cannot send mail to you: "(conversation with
>> dhollis.dyndns.org[71.251.104.159] timed out while sending MAIL FROM)"
>
> I've fixed that issue so my mail delivery isn't sporadic.
I still got it last week.
[-- Attachment #1.2: dmesg.txt --]
[-- Type: text/plain, Size: 13801 bytes --]
usb 1-8: new high speed USB device using ehci_hcd and address 11
usb 1-8: configuration #1 chosen from 1 choice
usb%d: asix_read_cmd() cmd=0x1e value=0x0000 index=0x0000 size=1
drivers/net/usb/asix.c: GPIO Status: 0x0002
usb%d: asix_write_cmd() cmd=0x0d value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x0b value=0x0017 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0e value=0x0000 index=0x0000 size=0
drivers/net/usb/asix.c: EEPROM index 0x17 is 0x0580
drivers/net/usb/asix.c: GPIO0: 0, PhyMode: 0
usb%d: asix_write_gpio() - value = 0x008c
usb%d: asix_write_cmd() cmd=0x1f value=0x008c index=0x0000 size=0
usb%d: asix_write_gpio() - value = 0x003c
usb%d: asix_write_cmd() cmd=0x1f value=0x003c index=0x0000 size=0
usb%d: asix_write_gpio() - value = 0x001c
usb%d: asix_write_cmd() cmd=0x1f value=0x001c index=0x0000 size=0
usb%d: asix_write_gpio() - value = 0x003c
usb%d: asix_write_cmd() cmd=0x1f value=0x003c index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x20 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x20 value=0x0048 index=0x0000 size=0
usb%d: asix_write_rx_ctl() - mode = 0x0000
usb%d: asix_write_cmd() cmd=0x10 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x13 value=0x0000 index=0x0000 size=6
usb%d: asix_get_phy_addr()
usb%d: asix_read_cmd() cmd=0x19 value=0x0000 index=0x0000 size=2
usb%d: asix_get_phy_addr() returning 0x18e0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0002 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x02, returns=0x0141
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0003 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x03, returns=0x0cc2
drivers/net/usb/asix.c: PHYID=0x01410cc2
usb%d: marvell_phy_init()
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x001b size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x1b, returns=0x848f
usb%d: MII_MARVELL_STATUS = 0x848f
usb%d: asix_mdio_write() phy_id=0x18, loc=0x14, val=0x0082
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0014 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0018 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x18, returns=0x4100
usb%d: MII_MARVELL_LED_CTRL (1) = 0x4100
usb%d: asix_mdio_write() phy_id=0x18, loc=0x18, val=0x4101
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0018 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0018 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x18, returns=0x4101
usb%d: MII_MARVELL_LED_CTRL (2) = 0x4101
usb%d: asix_mdio_write() phy_id=0x18, loc=0x00, val=0x9000
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_write() phy_id=0x18, loc=0x04, val=0x05e1
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0004 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_write() phy_id=0x18, loc=0x09, val=0x0200
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0009 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x00, returns=0x1000
usb%d: asix_mdio_write() phy_id=0x18, loc=0x00, val=0x1200
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_medium_mode() - mode = 0x0376
usb%d: asix_write_cmd() cmd=0x1b value=0x0376 index=0x0000 size=0
usb%d: asix_write_rx_ctl() - mode = 0x0088
usb%d: asix_write_cmd() cmd=0x10 value=0x0088 index=0x0000 size=0
eth2: register 'asix' at usb-0000:00:1d.7-8, ASIX AX88178 USB 2.0 Ethernet, 00:12:17:f2:1a:17
usbcore: registered new interface driver asix
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: ax88178_link_reset()
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: link down
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0004 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x04, returns=0x05e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0000 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x00, returns=0x1000
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0005 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x05, returns=0x0000
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x000a size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x0a, returns=0x0000
eth2: ax88178_link_reset() speed: 10 duplex: 0 setting mode to 0x0174
eth2: asix_write_medium_mode() - mode = 0x0174
eth2: asix_write_cmd() cmd=0x1b value=0x0174 index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x19, returns=0x0000
eth2: marvell_led_status() read 0x0000
eth2: marvell_led_status() writing 0x02f0
eth2: asix_mdio_write() phy_id=0x18, loc=0x19, val=0x02f0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x10 value=0x0088 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
r8169: eth1: link down
eth2: Link Status is: 1
eth2: ax88178_link_reset()
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x796d
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x796d
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0004 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x04, returns=0x05e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0005 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x05, returns=0xc5e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x000a size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x0a, returns=0x3800
eth2: link up, 1000Mbps, full-duplex, lpa 0xC5E1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0004 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x04, returns=0x05e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0000 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x00, returns=0x1000
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0005 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x05, returns=0xc5e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x000a size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x0a, returns=0x3800
eth2: ax88178_link_reset() speed: 1000 duplex: 1 setting mode to 0x037f
eth2: asix_write_medium_mode() - mode = 0x037f
eth2: asix_write_cmd() cmd=0x1b value=0x037f index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x19, returns=0x02f0
eth2: marvell_led_status() read 0x02f0
eth2: marvell_led_status() writing 0x03e0
eth2: asix_mdio_write() phy_id=0x18, loc=0x19, val=0x03e0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
device eth2 entered promiscuous mode
eth2: asix_write_cmd_async() cmd=0x10 value=0x0089 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x10 value=0x0089 index=0x0000 size=0
device eth2 left promiscuous mode
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
device eth2 entered promiscuous mode
eth2: asix_write_cmd_async() cmd=0x10 value=0x0089 index=0x0000 size=0
device eth2 left promiscuous mode
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/x-pkcs7-signature, Size: 3315 bytes --]
^ permalink raw reply
* Re: strange tcp behavior
From: Evgeniy Polyakov @ 2007-08-04 16:03 UTC (permalink / raw)
To: Simon Arlott; +Cc: john, netdev, David Miller
In-Reply-To: <46B37426.20500@simon.arlott.org.uk>
On Fri, Aug 03, 2007 at 07:29:58PM +0100, Simon Arlott (simon@fire.lp0.eu) wrote:
> On 03/08/07 18:39, Evgeniy Polyakov wrote:
> > On Fri, Aug 03, 2007 at 05:51:42PM +0100, Simon Arlott (simon@fire.lp0.eu) wrote:
> >
> >> 17:38:03.533589 IP 192.168.7.4.50550 > 192.168.7.8.2500: R 82517592:82517592(0) win 1500 (raw)
> >> vs
> >> 17:37:38.383085 IP 192.168.7.8.2500 > 192.168.7.4.50550: R 4259643274:4259643274(0) ack 1171836829 win 14360
> >> What happened there ?
>
> Erm... you seem to have removed parts of my message in a way that doesn't
> make sense...
Sorry, I left line I tought were enough to understand your point.
> On Fri, Aug 03, 2007 at 05:51:42PM +0100, Simon Arlott wrote:
> > 17:38:04.536277 IP 192.168.7.8.2500 > 192.168.7.4.50550: R 1:1(0) ack 17 win 14360
> > vs
> > 17:37:38.383085 IP 192.168.7.8.2500 > 192.168.7.4.50550: R 4259643274:4259643274(0) ack 1171836829 win 14360
> > What happened there ?
>
> The first one is the RST sent when the connection is close()d without
> reading, and the second one is the same RST but after other connection
> has been made on the same ports using a different socket.
I understood it, and your question is about possibility for those
numbers to be roughly the same. Answer is 'no', it is not possible
(possible, but with extremely low probability).
If it is - this is a bug in ISN generation algo and must be fixed.
> > It is the same situation, which would happen if you will spam remote
> > side with RST packets with arbitrary sequence number in hope that it
> > will reset some connection.
>
> Isn't it still possible that the connection that got reset is left open
> (possibly for days) until another connection using the same ports is
> using roughly the same sequence numbers?
Of course it is possible, but it very unlikely. Practically it is
impossible in modern OSes - ISN generation algos are designed to prevent
this from happening.
> --
> Simon Arlott
--
Evgeniy Polyakov
^ permalink raw reply
* Re: [PATCH 00/23] per device dirty throttling -v8
From: Ray Lee @ 2007-08-04 16:01 UTC (permalink / raw)
To: david@lang.hm
Cc: Ingo Molnar, Linus Torvalds, Peter Zijlstra, linux-mm,
linux-kernel, miklos, akpm, neilb, dgc, tomoki.sekiyama.qu,
nikita, trond.myklebust, yingchao.zhou, richard, netdev
In-Reply-To: <Pine.LNX.4.64.0708040032570.6905@asgard.lang.hm>
(adding netdev cc:)
On 8/4/07, david@lang.hm <david@lang.hm> wrote:
> On Sat, 4 Aug 2007, Ingo Molnar wrote:
>
> > * Ingo Molnar <mingo@elte.hu> wrote:
> >
> >> There are positive reports in the never-ending "my system crawls like
> >> an XT when copying large files" bugzilla entry:
> >>
> >> http://bugzilla.kernel.org/show_bug.cgi?id=7372
> >
> > i forgot this entry:
> >
> > " We recently upgraded our office to gigabit Ethernet and got some big
> > AMD64 / 3ware boxes for file and vmware servers... only to find them
> > almost useless under any kind of real load. I've built some patched
> > 2.6.21.6 kernels (using the bdi throttling patch you mentioned) to
> > see if our various Debian Etch boxes run better. So far my testing
> > shows a *great* improvement over the stock Debian 2.6.18 kernel on
> > our configurations. "
> >
> > and bdi has been in -mm in the past i think, so we also know (to a
> > certain degree) that it does not hurt those workloads that are fine
> > either.
> >
> > [ my personal interest in this is the following regression: every time i
> > start a large kernel build with DEBUG_INFO on a quad-core 4GB RAM box,
> > i get up to 30 seconds complete pauses in Vim (and most other tasks),
> > during plain editing of the source code. (which happens when Vim tries
> > to write() to its swap/undo-file.) ]
>
> I have an issue that sounds like it's related.
>
> I've got a syslog server that's got two Opteron 246 cpu's, 16G ram, 2x140G
> 15k rpm drives (fusion MPT hardware mirroring), 16x500G 7200rpm SATA
> drives on 3ware 9500 cards (software raid6) running 2.6.20.3 with hz set
> at default and preempt turned off.
>
> I have syslog doing buffered writes to the SCSI drives and every 5 min a
> cron job copies the data to the raid array.
>
> I've found that if I do anything significant on the large raid array that
> the system looses a significant amount of the UDP syslog traffic, even
> though there should be pleanty of ram and cpu (and the spindles involved
> in the writes are not being touched), even a grep can cause up to 40%
> losses in the syslog traffic. I've experimented with nice levels (nicing
> down the grep and nicing up the syslogd) without a noticable effect on the
> losses.
>
> I've been planning to try a new kernel with hz=1000 to see if that would
> help, and after that experiment with the various preempt settings, but it
> sounds like the per-device queues may actually be more relavent to the
> problem.
>
> what would you suggest I test, and in what order and combination?
At least on a surface level, your report has some similarities to
http://lkml.org/lkml/2007/5/21/84 . In that message, John Miller
mentions several things he tried without effect:
< - I increased the max allowed receive buffer through
< proc/sys/net/core/rmem_max and the application calls the right
< syscall. "netstat -su" does not show any "packet receive errors".
<
< - After getting "kernel: swapper: page allocation failure.
< order:0, mode:0x20", I increased /proc/sys/vm/min_free_kbytes
<
< - ixgb.txt in kernel network documentation suggests to increase
< net.core.netdev_max_backlog to 300000. This did not help.
<
< - I also had to increase net.core.optmem_max, because the default
< value was too small for 700 multicast groups.
As they're all pretty simple to test, it may be worthwhile to give
them a shot just to rule things out.
Ray
^ 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