* [2.6.19 PATCH 6/7] ehea: eHEA Makefile
From: Jan-Bernd Themann @ 2006-09-04 10:42 UTC (permalink / raw)
To: netdev, Jeff Garzik
Cc: Thomas Klein, Jan-Bernd Themann, linux-kernel, linux-ppc,
Christoph Raisch, Marcus Eder
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/ehea/Makefile | 6 ++++++
1 file changed, 6 insertions(+)
--- linux-2.6.18-rc6-orig/drivers/net/ehea/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/Makefile 2006-09-04 11:41:18.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the eHEA ethernet device driver for IBM eServer System p
+#
+ehea-y = ehea_main.o ehea_phyp.o ehea_qmr.o ehea_ethtool.o ehea_phyp.o
+obj-$(CONFIG_EHEA) += ehea.o
+
^ permalink raw reply
* [2.6.19 PATCH 7/7] ehea: Makefile & Kconfig
From: Jan-Bernd Themann @ 2006-09-04 10:44 UTC (permalink / raw)
To: netdev, Jeff Garzik
Cc: Thomas Klein, Jan-Bernd Themann, linux-kernel, linux-ppc,
Christoph Raisch, Marcus Eder
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/Kconfig | 9 +++++++++
drivers/net/Makefile | 1 +
2 files changed, 10 insertions(+)
diff -Nurp -X dontdiff linux-2.6.18-rc6/drivers/net/Kconfig patched_kernel/drivers/net/Kconfig
--- linux-2.6.18-rc6/drivers/net/Kconfig 2006-09-04 04:19:48.000000000 +0200
+++ patched_kernel/drivers/net/Kconfig 2006-09-04 11:38:59.000000000 +0200
@@ -2318,6 +2318,15 @@ config CHELSIO_T1
To compile this driver as a module, choose M here: the module
will be called cxgb.
+config EHEA
+ tristate "eHEA Ethernet support"
+ depends on IBMEBUS
+ ---help---
+ This driver supports the IBM pSeries eHEA ethernet adapter.
+
+ To compile the driver as a module, choose M here. The module
+ will be called ehea.
+
config IXGB
tristate "Intel(R) PRO/10GbE support"
depends on PCI
diff -Nurp -X dontdiff linux-2.6.18-rc6/drivers/net/Makefile patched_kernel/drivers/net/Makefile
--- linux-2.6.18-rc6/drivers/net/Makefile 2006-09-04 04:19:48.000000000 +0200
+++ patched_kernel/drivers/net/Makefile 2006-09-04 11:39:06.000000000 +0200
@@ -10,6 +10,7 @@ obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_CHELSIO_T1) += chelsio/
+obj-$(CONFIG_EHEA) += ehea/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
^ permalink raw reply
* Problem about porting u-boot 1.1.4 to MPC8560
From: enorm @ 2006-09-04 11:08 UTC (permalink / raw)
To: linuxppc-embedded
Hi,
After prog u-boot image to 8560. We get output as follows:
U-Boot 1.1.4 (Sep 4 2006 - 17:15:31)
CPU: 8560, Version: 1.0, (0x80700010)
Core: E500, Version:
Clock Configuration:- 19:56:59)
CPU: 825 MHz, CCB: 330 MHz,, Version: 1.0, (0x80700010)
DDR: 165 MHz, LBC: 82 MHzCore: E500, Version: 1.0, (0x802
L1: D-cache 32 kB enabled
Clock Confi
I-cache 32 kB enabled
CPU: 792 MHz, CCB: 2
Board: ADS
PCI1: 32 bit, 33 MHz (compiled) MHz, LBC: 66 MHz
I2C: ready
L1: D
DRAM: Initializing
32 kB enabled
DDR: number of ranks = 1: ADS
PCI1
DDR: DDR I rank density = 0x10000000
I2C: rea
DDR: cs0_bnds = 0x0000000fzing
DDR:
DDR: cs0_config = 0x80000102
DDR: DDR I r
DDR: caslat SPD bit is 4
DDR: tCKmin = 5000 psbnds = 0x00000007
DDR: sdram_mode_2 = 0x00000000
DDR: sdram_interval = 0x05080100
DDR: sdram_cfg_2 = 0x00000000
DDR: sdram_cfg = 0xc2000000
DDR: MAS0=0x10080000
DDR: MAS1=0xc0000900
DDR: MAS2=0x00000000
DDR: MAS3=0x00000015
DDR: LAWBAR1=0x00000000
DDR: LARAR1=0x80f0001b
SDRAM: 64 MB
DDR: 256 MB
Top of RAM usable for U-Boot at: 10000000
Reserving 173k for U-Boot at: 0ffd0000
Reserving 136k for malloc() at: 0ffae000
Reserving 88 Bytes for Board Info at: 0ffadfa8
Reserving 72 Bytes for Global Data at: 0ffadf60
Stack Pointer at: 0ffadf48
then it can't go on running. we checked ,found it falled in "relocate_code"
,then we write a progam to test memory, we found test failed(say write some
thing to 0-0x8000000 then read)
How can we do to fix this problem?
^ permalink raw reply
* Re: Problem about porting u-boot 1.1.4 to MPC8560
From: Wolfgang Denk @ 2006-09-04 12:48 UTC (permalink / raw)
To: enorm; +Cc: linuxppc-embedded
In-Reply-To: <003f01c6d012$79fa8dd0$a309a8c0@cdf28e684a26482>
In message <003f01c6d012$79fa8dd0$a309a8c0@cdf28e684a26482> you wrote:
>
> After prog u-boot image to 8560. We get output as follows:
You are off topic here. Also, it is very impolite to post the same
question on several mailing lists. Your questions have already been
answered on u-boot-users.
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
Mr. Cole's Axiom:
The sum of the intelligence on the planet is a constant;
the population is growing.
^ permalink raw reply
* Re: [PATCH 4/6] Have x86_64 use add_active_range() and free_area_init_nodes
From: Mel Gorman @ 2006-09-04 15:36 UTC (permalink / raw)
To: Keith Mannthey
Cc: akpm, tony.luck, linuxppc-dev, ak, bob.picco,
Linux Kernel Mailing List, Linux Memory Management List
In-Reply-To: <a762e240608312008v3e35b63ay46c95fbb6c3f15ec@mail.gmail.com>
On (31/08/06 20:08), Keith Mannthey didst pronounce:
> >So, do you actally expect a lot of unused mem_map to be allocated with
> >struct pages that are inactive until memory is hot-added in an
> >x86_64-specific manner? The arch-independent stuff currently will not do
> >that. It sets up memmap for where memory really exists. If that is not
> >what you expect, it will hit issues at hotadd time which is not the
> >current issue but one that can be fixed.
>
> Yes. RESERVED based is a big waste of mem_map space. The add areas
> are marked as RESERVED during boot and then later onlined during add.
> It might be ok. I will play with tomorrow. I might just need to
> call add_active_range in the right spot :)
>
Following this mail should be two patches that may address the problem
with reserved memory hot-add. One assumption made by arch-independent
zone-sizing was that the only memory holes of interest were those before the
end of physical memory. Another assumption was that mem_map should only be
allocated for memory that was physically present in the machine.
With MEMORY_HOTPLUG_RESERVE on x86_64, these assumptions do not hold. This
feature expects that mem_map is allocated at boot time and later activated
on a memory hot-add event. To determine if the region is usable for hot-add
in the future, holes are calculated beyond the end of physical memory.
The following two patches fix these two assumptions. They have been boot-tested
on a range of hardware (x86, ppc64, ia64 and x86_64) so there should be no
new regressions.
I don't have access to hardware that can use MEMORY_HOTPLUG_RESERVE so I'd
appreciate hearing if the patches work. I wrote a test program that simulated
the input from the machine the problem was reported on. It registers active
memory and simulates the check made by reserve_hotadd(). push_node_boundaries()
is called to push the end of the node out by 100 pages like what SRAT would
do for reserve hot-add and it appears to do the right thing. Output is below.
mel@arnold:~/patches/brokenout/zonesizing/driver_test$ gcc driver_test.c -o
driver_test && ./driver_test | grep -v "active with" | grep -v
account_node_boundary
Stage 1: Registering active ranges
Entering add_active_range(0, 0, 152) 0 entries of 96 used
Entering add_active_range(0, 256, 524165) 1 entries of 96 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 96 used
Entering add_active_range(1, 17235968, 18219008) 3 entries of 96 used
Dumping active map
0: 0 0 -> 152
1: 0 256 -> 524165
2: 0 1048576 -> 4653056
3: 1 17235968 -> 18219008
Entering push_node_boundaries(0, 0, 4653156)
Checking reserve-hotadd
absent_pages_in_range(4653056, 17235968) == 17235968 - 4653056 == 12582912
absent_pages_in_range(18219008, 52428800) == 52428800 - 18219008 == 34209792
Stage 2: Calculating zone sizes and holes
Stage 3: Dumping zone sizes and holes
zone_size[0][0] = 4096 zone_holes[0][0] = 104
zone_size[0][1] = 1044480 zone_holes[0][1] = 524411
zone_size[0][2] = 3604580 zone_holes[0][2] = 100
zone_size[1][2] = 983040 zone_holes[1][2] = 0
Stage 4: Printing present pages
On node 0, 4128541 pages
zone 0 present_pages = 3992
zone 1 present_pages = 520069
zone 2 present_pages = 3604480
On node 1, 983040 pages
zone 2 present_pages = 983040
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
^ permalink raw reply
* Account for holes that are outside the range of physical memory
From: Mel Gorman @ 2006-09-04 15:38 UTC (permalink / raw)
To: Keith Mannthey
Cc: akpm, tony.luck, linuxppc-dev, ak, bob.picco,
Linux Kernel Mailing List, Linux Memory Management List
In-Reply-To: <20060904153613.GA14263@skynet.ie>
absent_pages_in_range() made the assumption that users of the API would
not care about holes beyound the end of physical memory. This was not the
case. This patch will account for ranges outside of physical memory as
holes correctly.
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-clean/arch/x86_64/mm/srat.c linux-2.6.18-rc4-mm3-001_account_holes_range/arch/x86_64/mm/srat.c
--- linux-2.6.18-rc4-mm3-clean/arch/x86_64/mm/srat.c 2006-08-28 15:05:28.000000000 +0100
+++ linux-2.6.18-rc4-mm3-001_account_holes_range/arch/x86_64/mm/srat.c 2006-09-01 13:29:25.000000000 +0100
@@ -240,7 +240,9 @@ static int reserve_hotadd(int node, unsi
/* This check might be a bit too strict, but I'm keeping it for now. */
if (absent_pages_in_range(s_pfn, e_pfn) != e_pfn - s_pfn) {
- printk(KERN_ERR "SRAT: Hotplug area has existing memory\n");
+ printk(KERN_ERR
+ "SRAT: Hotplug area %lu -> %lu has existing memory\n",
+ s_pfn, e_pfn);
return -1;
}
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-clean/mm/page_alloc.c linux-2.6.18-rc4-mm3-001_account_holes_range/mm/page_alloc.c
--- linux-2.6.18-rc4-mm3-clean/mm/page_alloc.c 2006-08-28 15:05:30.000000000 +0100
+++ linux-2.6.18-rc4-mm3-001_account_holes_range/mm/page_alloc.c 2006-09-01 13:29:25.000000000 +0100
@@ -2259,6 +2259,10 @@ unsigned long __init __absent_pages_in_r
if (i == -1)
return 0;
+ /* Account for ranges before physical memory on this node */
+ if (early_node_map[i].start_pfn > range_start_pfn)
+ hole_pages = early_node_map[i].start_pfn - range_start_pfn;
+
prev_end_pfn = early_node_map[i].start_pfn;
/* Find all holes for the zone within the node */
@@ -2280,6 +2284,11 @@ unsigned long __init __absent_pages_in_r
prev_end_pfn = early_node_map[i].end_pfn;
}
+ /* Account for ranges past physical memory on this node */
+ if (range_end_pfn > prev_end_pfn)
+ hole_pages = range_end_pfn -
+ max(range_start_pfn, prev_end_pfn);
+
return hole_pages;
}
@@ -2301,9 +2310,16 @@ unsigned long __init zone_absent_pages_i
unsigned long zone_type,
unsigned long *ignored)
{
- return __absent_pages_in_range(nid,
- arch_zone_lowest_possible_pfn[zone_type],
- arch_zone_highest_possible_pfn[zone_type]);
+ unsigned long node_start_pfn, node_end_pfn;
+ unsigned long zone_start_pfn, zone_end_pfn;
+
+ get_pfn_range_for_nid(nid, &node_start_pfn, &node_end_pfn);
+ zone_start_pfn = max(arch_zone_lowest_possible_pfn[zone_type],
+ node_start_pfn);
+ zone_end_pfn = min(arch_zone_highest_possible_pfn[zone_type],
+ node_end_pfn);
+
+ return __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn);
}
/* Return the zone index a PFN is in */
^ permalink raw reply
* Allow an arch to expand node boundaries
From: Mel Gorman @ 2006-09-04 15:39 UTC (permalink / raw)
To: Keith Mannthey
Cc: akpm, tony.luck, linuxppc-dev, ak, bob.picco,
Linux Kernel Mailing List, Linux Memory Management List
In-Reply-To: <20060904153613.GA14263@skynet.ie>
Arch-independent zone-sizing determines the size of a node
(pgdat->node_spanned_pages) based on the physical memory that was registered
by the architecture. However, when CONFIG_MEMORY_HOTPLUG_RESERVE is set,
the architecture expects that the spanned_pages will be much larger and that
mem_map will be allocated that is used lated on memory hot-add.
This patch allows an architecture that sets CONFIG_MEMORY_HOTPLUG_RESERVE
to call push_node_boundaries() which will set the node beginning and end to
at *least* the requested boundary.
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-001_account_holes_range/arch/x86_64/mm/srat.c linux-2.6.18-rc4-mm3-002_push_node_boundaries/arch/x86_64/mm/srat.c
--- linux-2.6.18-rc4-mm3-001_account_holes_range/arch/x86_64/mm/srat.c 2006-09-01 13:29:25.000000000 +0100
+++ linux-2.6.18-rc4-mm3-002_push_node_boundaries/arch/x86_64/mm/srat.c 2006-09-01 13:30:57.000000000 +0100
@@ -334,6 +334,8 @@ acpi_numa_memory_affinity_init(struct ac
nd->start, nd->end);
e820_register_active_regions(node, nd->start >> PAGE_SHIFT,
nd->end >> PAGE_SHIFT);
+ push_node_boundaries(node, nd->start >> PAGE_SHIFT,
+ nd->end >> PAGE_SHIFT);
if (ma->flags.hot_pluggable && !reserve_hotadd(node, start, end) < 0) {
/* Ignore hotadd region. Undo damage */
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-001_account_holes_range/include/linux/mm.h linux-2.6.18-rc4-mm3-002_push_node_boundaries/include/linux/mm.h
--- linux-2.6.18-rc4-mm3-001_account_holes_range/include/linux/mm.h 2006-08-28 15:05:30.000000000 +0100
+++ linux-2.6.18-rc4-mm3-002_push_node_boundaries/include/linux/mm.h 2006-09-01 13:30:57.000000000 +0100
@@ -1007,6 +1007,8 @@ extern void add_active_range(unsigned in
unsigned long end_pfn);
extern void shrink_active_range(unsigned int nid, unsigned long old_end_pfn,
unsigned long new_end_pfn);
+extern void push_node_boundaries(unsigned int nid, unsigned long start_pfn,
+ unsigned long end_pfn);
extern void remove_all_active_ranges(void);
extern unsigned long absent_pages_in_range(unsigned long start_pfn,
unsigned long end_pfn);
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-001_account_holes_range/mm/page_alloc.c linux-2.6.18-rc4-mm3-002_push_node_boundaries/mm/page_alloc.c
--- linux-2.6.18-rc4-mm3-001_account_holes_range/mm/page_alloc.c 2006-09-01 13:29:25.000000000 +0100
+++ linux-2.6.18-rc4-mm3-002_push_node_boundaries/mm/page_alloc.c 2006-09-01 13:30:57.000000000 +0100
@@ -131,6 +131,10 @@ static unsigned long __initdata dma_rese
int __initdata nr_nodemap_entries;
unsigned long __initdata arch_zone_lowest_possible_pfn[MAX_NR_ZONES];
unsigned long __initdata arch_zone_highest_possible_pfn[MAX_NR_ZONES];
+#ifdef CONFIG_MEMORY_HOTPLUG_RESERVE
+ unsigned long __initdata node_boundary_start_pfn[MAX_NUMNODES];
+ unsigned long __initdata node_boundary_end_pfn[MAX_NUMNODES];
+#endif /* CONFIG_MEMORY_HOTPLUG_RESERVE */
#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
#ifdef CONFIG_DEBUG_VM
@@ -2186,6 +2190,62 @@ void __init sparse_memory_present_with_a
}
/**
+ * push_node_boundaries - Push node boundaries to at least the requested boundary
+ * @nid: The nid of the node to push the boundary for
+ * @start_pfn: The start pfn of the node
+ * @end_pfn: The end pfn of the node
+ *
+ * In reserve-based hot-add, mem_map is allocated that is unused until hotadd
+ * time. Specifically, on x86_64, SRAT will report ranges that can potentially
+ * be hotplugged even though no physical memory exists. This function allows
+ * an arch to push out the node boundaries so mem_map is allocated that can
+ * be used later.
+ */
+#ifdef CONFIG_MEMORY_HOTPLUG_RESERVE
+void __init push_node_boundaries(unsigned int nid,
+ unsigned long start_pfn, unsigned long end_pfn)
+{
+ printk(KERN_DEBUG "Entering push_node_boundaries(%u, %lu, %lu)\n",
+ nid, start_pfn, end_pfn);
+
+ /* Initialise the boundary for this node if necessary */
+ if (node_boundary_end_pfn[nid] == 0)
+ node_boundary_start_pfn[nid] = -1UL;
+
+ /* Update the boundaries */
+ if (node_boundary_start_pfn[nid] > start_pfn)
+ node_boundary_start_pfn[nid] = start_pfn;
+ if (node_boundary_end_pfn[nid] < end_pfn)
+ node_boundary_end_pfn[nid] = end_pfn;
+}
+
+/* If necessary, push the node boundary out for reserve hotadd */
+static void __init account_node_boundary(unsigned int nid,
+ unsigned long *start_pfn, unsigned long *end_pfn)
+{
+ printk(KERN_DEBUG "Entering account_node_boundary(%u, %lu, %lu)\n",
+ nid, *start_pfn, *end_pfn);
+
+ /* Return if boundary information has not been provided */
+ if (node_boundary_end_pfn[nid] == 0)
+ return;
+
+ /* Check the boundaries and update if necessary */
+ if (node_boundary_start_pfn[nid] < *start_pfn)
+ *start_pfn = node_boundary_start_pfn[nid];
+ if (node_boundary_end_pfn[nid] > *end_pfn)
+ *end_pfn = node_boundary_end_pfn[nid];
+}
+#else
+void __init push_node_boundaries(unsigned int nid,
+ unsigned long start_pfn, unsigned long end_pfn) {}
+
+static void __init account_node_boundary(unsigned int nid,
+ unsigned long *start_pfn, unsigned long *end_pfn) {}
+#endif
+
+
+/**
* get_pfn_range_for_nid - Return the start and end page frames for a node
* @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned
* @start_pfn: Passed by reference. On return, it will have the node start_pfn
@@ -2212,6 +2272,9 @@ void __init get_pfn_range_for_nid(unsign
printk(KERN_WARNING "Node %u active with no memory\n", nid);
*start_pfn = 0;
}
+
+ /* Push the node boundaries out if requested */
+ account_node_boundary(nid, start_pfn, end_pfn);
}
/*
@@ -2655,6 +2718,10 @@ void __init remove_all_active_ranges()
{
memset(early_node_map, 0, sizeof(early_node_map));
nr_nodemap_entries = 0;
+#ifdef CONFIG_MEMORY_HOTPLUG_RESERVE
+ memset(node_boundary_start_pfn, 0, sizeof(node_boundary_start_pfn));
+ memset(node_boundary_end_pfn, 0, sizeof(node_boundary_end_pfn));
+#endif /* CONFIG_MEMORY_HOTPLUG_RESERVE */
}
/* Compare two active node_active_regions */
^ permalink raw reply
* Re: Broken Firewire 400/SCSI on ppc Powerbook5,8
From: Stefan Richter @ 2006-09-04 16:58 UTC (permalink / raw)
To: Wolfgang Pfeiffer; +Cc: linuxppc-dev, billfink, linux1394-devel
In-Reply-To: <20060902232644.GB2814@localhost>
Wolfgang Pfeiffer wrote on :
...
> # /sbin/hdparm -tT /dev/sda
>
> /dev/sda:
> Timing cached reads: 1244 MB in 2.00 seconds = 620.49 MB/sec
> Timing buffered disk reads: 34 MB in 3.02 seconds = 11.24 MB/sec
...
> Timing buffered disk reads: 34 MB in 3.04 seconds = 11.17 MB/sec
...
> Timing buffered disk reads: 36 MB in 3.07 seconds = 11.72 MB/sec
...
Looks quite slow. I get 18 MB/s from a 20GB 2.5" HDD in an 1394a
enclosure, on an 1394a adapter or same on an 1394b adapter. The HDD was
actually taken out of a TiBook some time ago.
But I have no reference values of PowerBooks in target disk mode. This
mode is probably not optimized for performance.
--
Stefan Richter
-=====-=-==- =--= --=--
http://arcgraph.de/sr/
^ permalink raw reply
* Re: [PATCH] Gunzip call fix for PPC kernel images >4MB
From: Tom Rini @ 2006-09-04 18:36 UTC (permalink / raw)
To: Benjamin Heyne; +Cc: linuxppc-dev, Paul Mackerras, Milton Miller
In-Reply-To: <20060818101024.46a77c6b@bob.dt.e-technik.uni-dortmund.de>
Sorry about the late reply, this got burried in my inbox.
On Fri, Aug 18, 2006 at 10:10:24AM +0200, Benjamin Heyne wrote:
> And so it came to pass, that on Wed, 16 Aug 2006 22:04:08 -0700
> Tom Rini <trini@kernel.crashing.org> wrote as thus:
>
> > On Wed, Aug 16, 2006 at 11:31:37AM +0200, Benjamin Heyne wrote:
> >
> > > If Kernel images for PPC grow >4MB inflating of the kernel fails.
> > > Increasing the link/load address doesn't help. Problem is
> > > solved by replacing the fixed address of the gunzip() call in
> > > misc-embedded.c with CONFIG_BOOT_LOAD.
> >
> > The arch/ppc/boot code makes some assumptions such as that 4MB-8MB (except
> > on 40x which is end of the wrapper code to 8MB, and BOOT_LOAD defaults
> > to 4MB here) is free to use for malloc()'ing for the inflate routine.
>
> Well, I just could find this assumptions hardcoded in misc - Not in misc-embedded.
> But I am relatively new to kernel programming, so maybe I have missed something.
>
> But despite of that: Shouldn't it be possible to shift this range without
> having to change fixed addresses at several places?
Maybe, just maybe. The code is quite a bit complex in parts and I'd
really rather not try and make changes here as it's also on its way out.
> > It also assumes that the vmlinux.gz will live at BOOT_LOAD + a bit, when
> > the wrapper is set to kick off linux.
>
> As I understand it, the lowest address at which reasonable data starts is
> BOOT_LOAD. The space below is used for inflating, right? At least this
> seems to be the case for my target (Virtex, ML300) - Do other targets
> behave differently?
At various points in the cycle, all targets make assumptions about what
can and can't live where. Relocation is one of the big issues here as
we have to accept and work when we're loaded above, or below, BOOT_LOAD
initially and move ourself and anything useful we've been passed in,
around.
> > Finally, in arch/ppc/boot/ (for both OF and !OF cases) gunzip looks
> > like:
> > void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
>
> Ok - The dstlen parameter just tells zlib_inflate() something about the
> available space. If the inflated image grows larger, an error occurs.
> Therefore, BOOT_LOAD should be a reasonable border because board data
> starts beyond - not before.
Yes, I'm saying it's possible that some of that range is not available
space, for uncompressing as it's used for buffers in zlib_inflate.
> > So I see 2 problems here.
> >
> > First, there are other calls to gunzip with dstlen hard-coded to 4MB, so
> > the problem would still exist.
>
> Well, the other one exists in misc.c - But this one doesn't apply to the
> embedded target, right?
You can't really change one without changing the rest as there's similar
issues in all.
> > Second, and more complexly we want to make sure that we don't tell
> > gunzip that the destination size grows into the area it would also be
> > using to malloc buffers, unless someone can (and please do if possible)
> > explain that it's really OK to uncompress into our zalloc space (there
> > is no zfree so maybe it's going to be OK, but I don't know that the
> > algorithm wouldn't try and reuse buffers which would be bad).
>
> Well, as I said I am new to kernel programming - So I don't
> know exactly where the malloc space starts. I just assumed it would be
> beyond BOOT_START because it seemed to be reasonable (If you have a kernel
> which inflated size is exactly 4MB, inflation should also work...does it?).
I'm sorry to say you didn't pick the easiest spot to cut your teeth on,
so to speak. The malloc space always beyond BOOT_LOAD because we don't
know how high we can go. We know that from 0 to BOOT_LOAD is valid (or
we would have failed by now). We have to assume that BOOT_LOAD to the
end ourself is also valid, or we'd have blown up (and hopefully in a way
that the user would figure out they need to tweak BOOT_LOAD or make the
image smaller). But beyond that we don't always have a way to know how
much memory we have.
--
Tom Rini
^ permalink raw reply
* [PATCH] Unwire set/get_robust_list
From: Andreas Schwab @ 2006-09-04 19:27 UTC (permalink / raw)
To: linuxppc-dev
The syscalls set/get_robust_list must not be wired up until
futex_atomic_cmpxchg_inatomic is implemented. Otherwise the kernel will
hang in handle_futex_death.
Signed-off-by: Andreas Schwab <schwab@suse.de>
Index: linux-2.6.18-rc6/include/asm-powerpc/systbl.h
===================================================================
--- linux-2.6.18-rc6.orig/include/asm-powerpc/systbl.h 2006-09-04 18:12:43.000000000 +0200
+++ linux-2.6.18-rc6/include/asm-powerpc/systbl.h 2006-09-04 20:54:42.000000000 +0200
@@ -302,5 +302,5 @@ SYSCALL_SPU(symlinkat)
SYSCALL_SPU(readlinkat)
SYSCALL_SPU(fchmodat)
SYSCALL_SPU(faccessat)
-COMPAT_SYS_SPU(get_robust_list)
-COMPAT_SYS_SPU(set_robust_list)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
Index: linux-2.6.18-rc6/include/asm-powerpc/unistd.h
===================================================================
--- linux-2.6.18-rc6.orig/include/asm-powerpc/unistd.h 2006-09-04 18:12:43.000000000 +0200
+++ linux-2.6.18-rc6/include/asm-powerpc/unistd.h 2006-09-04 20:55:30.000000000 +0200
@@ -321,8 +321,8 @@
#define __NR_readlinkat 296
#define __NR_fchmodat 297
#define __NR_faccessat 298
-#define __NR_get_robust_list 299
-#define __NR_set_robust_list 300
+/* Number 299 reserved for get_robust_list */
+/* Number 300 reserved for set_robust_list */
#ifdef __KERNEL__
Andreas.
--
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply
* Re: [2.6.19 PATCH 1/7] ehea: interface to network stack
From: Francois Romieu @ 2006-09-04 20:16 UTC (permalink / raw)
To: Jan-Bernd Themann
Cc: Thomas Klein, Jeff Garzik, Jan-Bernd Themann, netdev,
linux-kernel, linux-ppc, Christoph Raisch, Marcus Eder
In-Reply-To: <200609041237.46528.ossthema@de.ibm.com>
Jan-Bernd Themann <ossthema@de.ibm.com> :
> Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
>
>
> drivers/net/ehea/ehea_main.c | 2694 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 2694 insertions(+)
>
>
>
> --- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_main.c 1970-01-01 01:00:00.000000000 +0100
> +++ kernel/drivers/net/ehea/ehea_main.c 2006-09-04 11:41:17.000000000 +0200
> @@ -0,0 +1,2694 @@
> +/*
> + * linux/drivers/net/ehea/ehea_main.c
> + *
> + * eHEA ethernet device driver for IBM eServer System p
> + *
> + * (C) Copyright IBM Corp. 2006
> + *
> + * Authors:
> + * Christoph Raisch <raisch@de.ibm.com>
> + * Jan-Bernd Themann <themann@de.ibm.com>
> + * Thomas Klein <tklein@de.ibm.com>
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2, or (at your option)
> + * any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/tcp.h>
> +#include <linux/udp.h>
> +#include <linux/if.h>
> +#include <linux/list.h>
> +#include <linux/if_ether.h>
> +#include <net/ip.h>
> +
> +#include "ehea.h"
> +#include "ehea_qmr.h"
> +#include "ehea_phyp.h"
Afaik none of those is included in this patch nor in my 2.6.18-git tree.
Happy bissect in sight.
> +
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
> +MODULE_DESCRIPTION("IBM eServer HEA Driver");
> +MODULE_VERSION(DRV_VERSION);
> +
> +
> +static int msg_level = -1;
> +static int rq1_entries = EHEA_DEF_ENTRIES_RQ1;
> +static int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
> +static int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
> +static int sq_entries = EHEA_DEF_ENTRIES_SQ;
> +
> +module_param(msg_level, int, 0);
> +module_param(rq1_entries, int, 0);
> +module_param(rq2_entries, int, 0);
> +module_param(rq3_entries, int, 0);
> +module_param(sq_entries, int, 0);
> +
> +MODULE_PARM_DESC(msg_level, "msg_level");
> +MODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 "
> + "[2^x - 1], x = [6..14]. Default = "
> + __MODULE_STRING(EHEA_DEF_ENTRIES_RQ3) ")");
> +MODULE_PARM_DESC(rq2_entries, "Number of entries for Receive Queue 2 "
> + "[2^x - 1], x = [6..14]. Default = "
> + __MODULE_STRING(EHEA_DEF_ENTRIES_RQ2) ")");
> +MODULE_PARM_DESC(rq1_entries, "Number of entries for Receive Queue 1 "
> + "[2^x - 1], x = [6..14]. Default = "
> + __MODULE_STRING(EHEA_DEF_ENTRIES_RQ1) ")");
> +MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue "
> + "[2^x - 1], x = [6..14]. Default = "
> + __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
> +
> +void ehea_dump(void *adr, int len, char *msg) {
> + int x;
> + unsigned char *deb = adr;
> + for (x = 0; x < len; x += 16) {
> + printk(DRV_NAME "%s adr=%p ofs=%04x %016lx %016lx\n", msg,
> + deb, x, *((u64 *)&deb[0]), *((u64 *)&deb[8]));
> + deb += 16;
> + }
> +}
> +
> +static struct net_device_stats *ehea_get_stats(struct net_device *dev)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct net_device_stats *stats = &port->stats;
> + struct hcp_ehea_port_cb2 *cb2;
> + u64 hret, rx_packets;
> + int i;
unsigned int ?
> +
> + memset(stats, 0, sizeof(*stats));
> +
> + cb2 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb2) {
> + ehea_error("no mem for cb2");
> + goto kzalloc_failed;
It's better when the label tell what it does than where it comes from.
If it's numbered too, one can check them without going back and forth.
> + }
> +
> + hret = ehea_h_query_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB2, H_PORT_CB2_ALL, cb2);
> + if (hret != H_SUCCESS) {
> + ehea_error("query_ehea_port failed");
> + goto hcall_failed;
> + }
> +
> + if (netif_msg_hw(port))
> + ehea_dump(cb2, sizeof(*cb2), "net_device_stats");
> +
> + rx_packets = 0;
Could be initialized when it is declared.
> + for (i = 0; i < port->num_def_qps; i++)
> + rx_packets += port->port_res[i].rx_packets;
> +
> + stats->tx_packets = cb2->txucp + cb2->txmcp + cb2->txbcp;
> + stats->multicast = cb2->rxmcp;
> + stats->rx_errors = cb2->rxuerr;
> + stats->rx_bytes = cb2->rxo;
> + stats->tx_bytes = cb2->txo;
> + stats->rx_packets = rx_packets;
> +
> +hcall_failed:
> + kfree(cb2);
Tab was turned into spaces.
> +
> +kzalloc_failed:
> + return stats;
> +}
> +
> +static inline int ehea_refill_rq1(struct ehea_port_res *pr, int index,
Avoid inline ?
> + int nr_of_wqes)
> +{
> + int ret = 0;
> + struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
> + int max_index_mask = pr->rq1_skba.len - 1;
> + int i;
unsigned int ?
> +
> + if (!nr_of_wqes)
> + return 0;
> +
> + for (i = 0; i < nr_of_wqes; i++) {
> + if (!skb_arr_rq1[index]) {
> + skb_arr_rq1[index] = dev_alloc_skb(EHEA_LL_PKT_SIZE);
netdev_alloc_skb ?
> +
> + if (!skb_arr_rq1[index]) {
> + ehea_error("no mem for skb/%d wqes filled", i);
> + ret = -ENOMEM;
The caller does not check the returned value.
> + break;
> + }
> + }
> + index--;
> + index &= max_index_mask;
> + }
> + /* Ring doorbell */
> + ehea_update_rq1a(pr->qp, i);
> +
> + return ret;
> +}
> +
> +static int ehea_init_fill_rq1(struct ehea_port_res *pr, int nr_rq1a)
> +{
> + int ret = 0;
> + struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
> + int i;
unsigned int ?
> +
> + for (i = 0; i < pr->rq1_skba.len; i++) {
> + skb_arr_rq1[i] = dev_alloc_skb(EHEA_LL_PKT_SIZE);
netdev_alloc_skb ?
> + if (!skb_arr_rq1[i]) {
> + ehea_error("no mem for skb/%d skbs filled.", i);
> + ret = -ENOMEM;
> + goto exit0;
s/exit0/out/
> + }
> + }
> + /* Ring doorbell */
> + ehea_update_rq1a(pr->qp, nr_rq1a);
> +exit0:
> + return ret;
> +}
> +
> +static int ehea_refill_rq_def(struct ehea_port_res *pr,
> + struct ehea_q_skb_arr *q_skba, int rq_nr,
> + int num_wqes, int wqe_type, int packet_size)
> +{
> + struct ehea_qp *qp = pr->qp;
> + struct sk_buff **skb_arr = q_skba->arr;
> + struct ehea_rwqe *rwqe;
> + int i, index, max_index_mask, fill_wqes;
unsigned int ?
> + int ret = 0;
> +
> + fill_wqes = q_skba->os_skbs + num_wqes;
> +
> + if (!fill_wqes)
> + return ret;
> +
> + index = q_skba->index;
> + max_index_mask = q_skba->len - 1;
> + for (i = 0; i < fill_wqes; i++) {
> + struct sk_buff *skb = dev_alloc_skb(packet_size);
> + if (!skb) {
> + ehea_error("no mem for skb/%d wqes filled", i);
> + q_skba->os_skbs = fill_wqes - i;
> + ret = -ENOMEM;
> + break;
> + }
> + skb_reserve(skb, NET_IP_ALIGN);
> +
> + skb_arr[index] = skb;
> +
> + rwqe = ehea_get_next_rwqe(qp, rq_nr);
> + rwqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, wqe_type)
> + | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, index);
> + rwqe->sg_list[0].l_key = pr->recv_mr.lkey;
> + rwqe->sg_list[0].vaddr = (u64)skb->data;
> + rwqe->sg_list[0].len = packet_size;
> + rwqe->data_segments = 1;
> +
> + index++;
> + index &= max_index_mask;
> + }
> + q_skba->index = index;
> +
> + /* Ring doorbell */
> + iosync();
> + if (rq_nr == 2)
> + ehea_update_rq2a(pr->qp, i);
> + else
> + ehea_update_rq3a(pr->qp, i);
> +
> + return ret;
> +}
> +
> +
> +static inline int ehea_refill_rq2(struct ehea_port_res *pr, int nr_of_wqes)
> +{
> + return ehea_refill_rq_def(pr, &pr->rq2_skba, 2,
> + nr_of_wqes, EHEA_RWQE2_TYPE,
> + EHEA_RQ2_PKT_SIZE + NET_IP_ALIGN);
> +}
> +
> +
> +static inline int ehea_refill_rq3(struct ehea_port_res *pr, int nr_of_wqes)
> +{
> + return ehea_refill_rq_def(pr, &pr->rq3_skba, 3,
> + nr_of_wqes, EHEA_RWQE3_TYPE,
> + EHEA_MAX_PACKET_SIZE + NET_IP_ALIGN);
> +}
> +
> +static inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num)
> +{
> + *rq_num = (cqe->type & EHEA_CQE_TYPE_RQ) >> 5;
> + if ((cqe->status & EHEA_CQE_STAT_ERR_MASK) == 0)
> + return 0;
> + if (((cqe->status & EHEA_CQE_STAT_ERR_TCP) != 0)
> + && (cqe->header_length == 0))
&& on the previous line please.
> + return 0;
> + return -EINVAL;
> +}
> +
> +static inline void ehea_fill_skb(struct net_device *dev,
> + struct sk_buff *skb, struct ehea_cqe *cqe)
> +{
> + int length = cqe->num_bytes_transfered - 4; /*remove CRC */
> +
> + skb_put(skb, length);
> + skb->dev = dev;
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + skb->protocol = eth_type_trans(skb, dev);
> +}
> +
> +static inline struct sk_buff *get_skb_by_index(struct sk_buff **skb_array,
> + int arr_len,
> + struct ehea_cqe *cqe)
> +{
> + int skb_index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
> + struct sk_buff *skb;
> + void *pref;
> + int x;
> +
> + x = skb_index + 1;
> + x &= (arr_len - 1);
> +
> + pref = (void*)skb_array[x];
Useless cast.
> + prefetchw(pref);
> + prefetchw(pref + EHEA_CACHE_LINE);
> +
> + pref = (void*)(skb_array[x]->data);
Useless cast.
> + prefetch(pref);
> + prefetch(pref + EHEA_CACHE_LINE);
> + prefetch(pref + EHEA_CACHE_LINE * 2);
> + prefetch(pref + EHEA_CACHE_LINE * 3);
> + skb = skb_array[skb_index];
> + skb_array[skb_index] = NULL;
> + return skb;
> +}
> +
> +static inline struct sk_buff *get_skb_by_index_ll(struct sk_buff **skb_array,
> + int arr_len, int wqe_index)
> +{
> + struct sk_buff *skb;
> + void *pref;
> + int x;
> +
> + x = wqe_index + 1;
> + x &= (arr_len - 1);
> +
> + pref = (void*)skb_array[x];
Useless cast.
> + prefetchw(pref);
> + prefetchw(pref + EHEA_CACHE_LINE);
> +
> + pref = (void*)(skb_array[x]->data);
Useless cast.
> + prefetchw(pref);
> + prefetchw(pref + EHEA_CACHE_LINE);
> +
> + skb = skb_array[wqe_index];
> + skb_array[wqe_index] = NULL;
> + return skb;
> +}
> +
> +static int ehea_treat_poll_error(struct ehea_port_res *pr, int rq,
> + struct ehea_cqe *cqe, int *processed_rq2,
> + int *processed_rq3)
> +{
> + struct sk_buff *skb;
> +
> + if (netif_msg_rx_err(pr->port)) {
> + ehea_error("CQE Error for QP %d", pr->qp->init_attr.qp_nr);
> + ehea_dump(cqe, sizeof(*cqe), "CQE");
> + }
> +
> + if (rq == 2) {
> + *processed_rq2 += 1;
> + skb = get_skb_by_index(pr->rq2_skba.arr, pr->rq2_skba.len, cqe);
> + dev_kfree_skb(skb);
> + } else if (rq == 3) {
> + *processed_rq3 += 1;
> + skb = get_skb_by_index(pr->rq3_skba.arr, pr->rq3_skba.len, cqe);
> + dev_kfree_skb(skb);
> + }
> +
> + if (cqe->status & EHEA_CQE_STAT_FAT_ERR_MASK) {
> + ehea_error("Critical receive error. Resetting port.");
> + queue_work(pr->port->adapter->ehea_wq, &pr->port->reset_task);
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +static int ehea_poll(struct net_device *dev, int *budget)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct ehea_port_res *pr = &port->port_res[0];
> + struct ehea_qp *qp = pr->qp;
> + struct ehea_cqe *cqe;
> + struct sk_buff *skb;
> + struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
> + struct sk_buff **skb_arr_rq2 = pr->rq2_skba.arr;
> + struct sk_buff **skb_arr_rq3 = pr->rq3_skba.arr;
> + int skb_arr_rq1_len = pr->rq1_skba.len;
> + int skb_arr_rq2_len = pr->rq2_skba.len;
> + int skb_arr_rq3_len = pr->rq3_skba.len;
> + int processed, processed_rq1, processed_rq2, processed_rq3;
> + int wqe_index, last_wqe_index, rq, intreq, my_quota, port_reset;
> +
> + processed = processed_rq1 = processed_rq2 = processed_rq3 = 0;
> + last_wqe_index = 0;
> + my_quota = min(*budget, dev->quota);
> + my_quota = min(my_quota, EHEA_POLL_MAX_RWQE);
> +
> + /* rq0 is low latency RQ */
> + cqe = ehea_poll_rq1(qp, &wqe_index);
> + while ((my_quota > 0) && cqe) {
> + ehea_inc_rq1(qp);
> + processed_rq1++;
> + processed++;
> + my_quota--;
> + if (netif_msg_rx_status(port))
> + ehea_dump(cqe, sizeof(*cqe), "CQE");
> +
> + last_wqe_index = wqe_index;
> + rmb();
> + if (!ehea_check_cqe(cqe, &rq)) {
> + if (rq == 1) { /* LL RQ1 */
> + skb = get_skb_by_index_ll(skb_arr_rq1,
> + skb_arr_rq1_len,
> + wqe_index);
> + if (unlikely(!skb)) {
> + if (netif_msg_rx_err(port))
> + ehea_error("LL rq1: skb=NULL");
> + skb = dev_alloc_skb(EHEA_LL_PKT_SIZE);
Tab/space
> + if (!skb)
> + break;
> + }
> + memcpy(skb->data, ((char*)cqe) + 64,
> + cqe->num_bytes_transfered - 4);
> + ehea_fill_skb(dev, skb, cqe);
> + } else if (rq == 2) { /* RQ2 */
> + skb = get_skb_by_index(skb_arr_rq2,
> + skb_arr_rq2_len, cqe);
> + if (unlikely(!skb)) {
> + if (netif_msg_rx_err(port))
> + ehea_error("rq2: skb=NULL");
> + break;
> + }
> + ehea_fill_skb(dev, skb, cqe);
> + processed_rq2++;
> + } else { /* RQ3 */
> + skb = get_skb_by_index(skb_arr_rq3,
> + skb_arr_rq3_len, cqe);
> + if (unlikely(!skb)) {
> + if (netif_msg_rx_err(port))
> + ehea_error("rq3: skb=NULL");
> + break;
> + }
> + ehea_fill_skb(dev, skb, cqe);
> + processed_rq3++;
> + }
> +
> + if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
> + vlan_hwaccel_receive_skb(skb, port->vgrp,
> + cqe->vlan_tag);
> + else
> + netif_receive_skb(skb);
> +
> + } else { /* Error occured */
> + pr->p_state.poll_receive_errors++;
> + port_reset = ehea_treat_poll_error(pr, rq, cqe,
> + &processed_rq2,
> + &processed_rq3);
> + if (port_reset)
> + break;
> + }
> + cqe = ehea_poll_rq1(qp, &wqe_index);
> + }
> +
> + dev->quota -= processed;
> + *budget -= processed;
> +
> + pr->p_state.ehea_poll += 1;
> + pr->rx_packets += processed;
> +
> + ehea_refill_rq1(pr, last_wqe_index, processed_rq1);
> + ehea_refill_rq2(pr, processed_rq2);
> + ehea_refill_rq3(pr, processed_rq3);
> +
> + intreq = ((pr->p_state.ehea_poll & 0xF) == 0xF);
Arguable parenthesis.
> +
> + if (!cqe || intreq) {
> + netif_rx_complete(dev);
> + ehea_reset_cq_ep(pr->recv_cq);
> + ehea_reset_cq_n1(pr->recv_cq);
> + cqe = hw_qeit_get_valid(&qp->hw_rqueue1);
> + if (!cqe || intreq)
> + return 0;
> + if (!netif_rx_reschedule(dev, my_quota))
> + return 0;
> + }
> + return 1;
> +}
> +
> +void free_sent_skbs(struct ehea_cqe *cqe, struct ehea_port_res *pr)
> +{
> + struct sk_buff *skb;
> + int index, max_index_mask, i;
> +
> + index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
> + max_index_mask = pr->sq_skba.len - 1;
> + for (i = 0; i < EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id); i++) {
> + skb = pr->sq_skba.arr[index];
> + if (likely(skb)) {
> + dev_kfree_skb(skb);
> + pr->sq_skba.arr[index] = NULL;
> + } else {
> + ehea_error("skb=NULL, wr_id=%lX, loop=%d, index=%d",
> + cqe->wr_id, i, index);
> + }
> + index--;
> + index &= max_index_mask;
> + }
> +}
> +
> +#define MAX_SENDCOMP_QUOTA 400
> +void ehea_send_irq_tasklet(unsigned long data)
> +{
> + struct ehea_port_res *pr = (struct ehea_port_res*)data;
> + struct ehea_cq *send_cq = pr->send_cq;
> + struct ehea_cqe *cqe;
> + int quota = MAX_SENDCOMP_QUOTA;
> + int cqe_counter = 0;
> + int swqe_av = 0;
> + unsigned long flags;
> +
> + do {
> + cqe = ehea_poll_cq(send_cq);
> + if (!cqe) {
> + ehea_reset_cq_ep(send_cq);
> + ehea_reset_cq_n1(send_cq);
> + cqe = ehea_poll_cq(send_cq);
> + if (!cqe)
> + break;
> + }
> + cqe_counter++;
> + rmb();
> + if (cqe->status & EHEA_CQE_STAT_ERR_MASK) {
> + ehea_error("Send Completion Error: Resetting port");
> + if (netif_msg_tx_err(pr->port))
> + ehea_dump(cqe, sizeof(*cqe), "Send CQE");
> + queue_work(pr->port->adapter->ehea_wq,
> + &pr->port->reset_task);
> + break;
> + }
> +
> + if (netif_msg_tx_done(pr->port))
> + ehea_dump(cqe, sizeof(*cqe), "CQE");
> +
> + if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id)
> + == EHEA_SWQE2_TYPE))
> + free_sent_skbs(cqe, pr);
> +
> + swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id);
> + quota--;
> + } while (quota > 0);
> +
> + ehea_update_feca(send_cq, cqe_counter);
> + atomic_add(swqe_av, &pr->swqe_avail);
> +
> + spin_lock_irqsave(&pr->netif_queue, flags);
> + if (pr->queue_stopped && (atomic_read(&pr->swqe_avail)
> + >= EHEA_SWQE_REFILL_TH)) {
> + netif_wake_queue(pr->port->netdev);
> + pr->queue_stopped = 0;
> + }
> + spin_unlock_irqrestore(&pr->netif_queue, flags);
> +
> + if (unlikely(cqe))
> + tasklet_hi_schedule(&pr->send_comp_task);
> +}
> +
> +irqreturn_t ehea_send_irq_handler(int irq, void *param, struct pt_regs *regs)
> +{
> + struct ehea_port_res *pr = (struct ehea_port_res*)param;
Useless cast.
> + tasklet_hi_schedule(&pr->send_comp_task);
> + return IRQ_HANDLED;
> +}
> +
> +irqreturn_t ehea_recv_irq_handler(int irq, void *param, struct pt_regs * regs)
> +{
> + struct ehea_port_res *pr = (struct ehea_port_res*)param;
Useless cast.
> + struct ehea_port *port = pr->port;
> + netif_rx_schedule(port->netdev);
Please insert a blank line.
> + return IRQ_HANDLED;
> +}
> +
> +irqreturn_t ehea_qp_aff_irq_handler(int irq, void *param, struct pt_regs * regs)
static ?
> +{
> + struct ehea_port *port = (struct ehea_port*)param;
Useless cast.
> + struct ehea_eqe *eqe;
> + u32 qp_token;
> +
> + eqe = ehea_poll_eq(port->qp_eq);
> + ehea_debug("eqe=%p", eqe);
> + while (eqe) {
> + ehea_debug("*eqe=%lx", *(u64*)eqe);
> + eqe = ehea_poll_eq(port->qp_eq);
> + qp_token = EHEA_BMASK_GET(EHEA_EQE_QP_TOKEN, eqe->entry);
> + ehea_debug("next eqe=%p", eqe);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static struct ehea_port *ehea_get_port(struct ehea_adapter *adapter,
> + int logical_port)
> +{
> + int i;
unsigned int ?
> +
> + for (i = 0; i < adapter->num_ports; i++)
> + if (adapter->port[i]->logical_port_id == logical_port)
> + return adapter->port[i];
> + return NULL;
> +}
> +
> +int ehea_sense_port_attr(struct ehea_port *port)
static ?
> +{
> + int ret;
> + u64 hret;
> + struct hcp_ehea_port_cb0 *cb0;
> +
> + cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb0) {
> + ehea_error("no mem for cb0");
> + ret = -ENOMEM;
> + goto exit0;
s/exit0/out_0/
> + }
> +
> + hret = ehea_h_query_ehea_port(port->adapter->handle,
> + port->logical_port_id, H_PORT_CB0,
> + EHEA_BMASK_SET(H_PORT_CB0_ALL, 0xFFFF),
> + cb0);
> + if (hret != H_SUCCESS) {
> + ret = -EIO;
> + goto exit1;
s/exit1/out_kfree_1/
> + }
> +
> + /* MAC address */
> + port->mac_addr = cb0->port_mac_addr << 16;
> +
> + if (!is_valid_ether_addr((u8*)&port->mac_addr)) {
> + ret = -EADDRNOTAVAIL;
> + goto exit1;
s/exit1/out_kfree_1/
> + }
> +
> + /* Port speed */
> + switch (cb0->port_speed) {
> + case H_SPEED_10M_H:
> + port->port_speed = EHEA_SPEED_10M;
> + port->full_duplex = 0;
> + break;
> + case H_SPEED_10M_F:
> + port->port_speed = EHEA_SPEED_10M;
> + port->full_duplex = 1;
> + break;
> + case H_SPEED_100M_H:
> + port->port_speed = EHEA_SPEED_100M;
> + port->full_duplex = 0;
> + break;
> + case H_SPEED_100M_F:
> + port->port_speed = EHEA_SPEED_100M;
> + port->full_duplex = 1;
> + break;
> + case H_SPEED_1G_F:
> + port->port_speed = EHEA_SPEED_1G;
> + port->full_duplex = 1;
> + break;
> + case H_SPEED_10G_F:
> + port->port_speed = EHEA_SPEED_10G;
> + port->full_duplex = 1;
> + break;
> + default:
> + port->port_speed = 0;
> + port->full_duplex = 0;
> + break;
> + }
> +
> + /* Number of default QPs */
> + port->num_def_qps = cb0->num_default_qps;
> +
> + if (!port->num_def_qps) {
> + ret = -EINVAL;
> + goto exit1;
s/exit1/out_kfree_1/
> + }
> +
> + if (port->num_def_qps >= EHEA_NUM_TX_QP)
> + port->num_add_tx_qps = 0;
> + else
> + port->num_add_tx_qps = EHEA_NUM_TX_QP - port->num_def_qps;
> +
> + ret = 0;
> +exit1:
> + if (ret || netif_msg_probe(port))
> + ehea_dump(cb0, sizeof(*cb0), "ehea_sense_port_attr");
> + kfree(cb0);
> +exit0:
> + return ret;
> +}
> +
> +int ehea_set_portspeed(struct ehea_port *port, u32 port_speed)
> +{
> + struct hcp_ehea_port_cb4 *cb4;
> + u64 hret;
> + int ret = 0;
> +
> + cb4 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb4) {
> + ehea_error("no mem for cb4");
> + ret = -ENOMEM;
> + goto exit0;
s/exit0/out/
> + }
> +
> + cb4->port_speed = port_speed;
> +
> + netif_carrier_off(port->netdev);
> +
> + hret = ehea_h_modify_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB4, H_PORT_CB4_SPEED, cb4);
> + if (hret == H_SUCCESS) {
> + port->autoneg = port_speed == EHEA_SPEED_AUTONEG ? 1 : 0;
> +
> + hret = ehea_h_query_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB4, H_PORT_CB4_SPEED,
> + cb4);
> + if (hret == H_SUCCESS) {
> + switch (cb4->port_speed) {
> + case H_SPEED_10M_H:
> + port->port_speed = EHEA_SPEED_10M;
> + port->full_duplex = 0;
> + break;
> + case H_SPEED_10M_F:
> + port->port_speed = EHEA_SPEED_10M;
> + port->full_duplex = 1;
> + break;
> + case H_SPEED_100M_H:
> + port->port_speed = EHEA_SPEED_100M;
> + port->full_duplex = 0;
> + break;
> + case H_SPEED_100M_F:
> + port->port_speed = EHEA_SPEED_100M;
> + port->full_duplex = 1;
> + break;
> + case H_SPEED_1G_F:
> + port->port_speed = EHEA_SPEED_1G;
> + port->full_duplex = 1;
> + break;
> + case H_SPEED_10G_F:
> + port->port_speed = EHEA_SPEED_10G;
> + port->full_duplex = 1;
> + break;
> + default:
> + port->port_speed = 0;
> + port->full_duplex = 0;
> + break;
> + }
> + } else {
> + ehea_error("Failed sensing port speed");
> + ret = -EIO;
> + }
> + } else {
> + if (hret == H_AUTHORITY)
> + {
Misplaced curly brace.
> + ehea_info("Hypervisor denied setting port speed. Either"
> + " this partition is not authorized to set "
> + "port speed or another partition has modified"
> + " port speed first.");
> + ret = -EPERM;
> + } else
> + {
Misplaced curly brace.
> + ret = -EIO;
> + ehea_error("Failed setting port speed");
> + }
> + }
> + netif_carrier_on(port->netdev);
> +exit0:
> + kfree(cb4);
cb4 is NULL. Not wrong per se but I'd rather move the label one line down.
> +
> + return ret;
> +}
> +
> +static void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe)
> +{
> + int ret;
> + u8 ec;
> + u8 portnum;
> + struct ehea_port *port;
> +
> + ec = EHEA_BMASK_GET(NEQE_EVENT_CODE, eqe);
> +
> + switch (ec) {
> + case EHEA_EC_PORTSTATE_CHG: /* port state change */
> + portnum = EHEA_BMASK_GET(NEQE_PORTNUM, eqe);
> + port = ehea_get_port(adapter, portnum);
> +
> + if (!port) {
> + ehea_error("unknown portnum %x", portnum);
> + break;
> + }
> +
> + if (EHEA_BMASK_GET(NEQE_PORT_UP, eqe)) {
> + if (!netif_carrier_ok(port->netdev)) {
> + ret = ehea_sense_port_attr(
> + adapter->port[portnum]);
> + if (ret) {
> + ehea_error("failed resensing port "
> + "attributes");
> + break;
> + }
> +
> + if (netif_msg_link(port))
> + ehea_info("%s: Logical port up: %dMbps "
> + "%s Duplex",
> + port->netdev->name,
> + port->port_speed,
> + port->full_duplex ==
> + 1 ? "Full" : "Half");
> +
> + netif_carrier_on(port->netdev);
> + netif_wake_queue(port->netdev);
> + }
> + } else
> + if (netif_carrier_ok(port->netdev)) {
> + if (netif_msg_link(port))
> + ehea_info("%s: Logical port down",
> + port->netdev->name);
> + netif_carrier_off(port->netdev);
> + netif_stop_queue(port->netdev);
> + }
> +
> + if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PORT_UP, eqe)) {
> + if (netif_msg_link(port))
> + ehea_info("%s: Physical port up",
> + port->netdev->name);
> + } else {
> + if (netif_msg_link(port))
> + ehea_info("%s: Physical port down",
> + port->netdev->name);
> + }
> +
> + if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PRIMARY, eqe))
> + ehea_info("Externel switch port is primary port");
> + else
> + ehea_info("Externel switch port is backup port");
> +
> + break;
> + case EHEA_EC_ADAPTER_MALFUNC:
> + ehea_error("Adapter malfunction");
> + break;
> + case EHEA_EC_PORT_MALFUNC:
> + ehea_info("Port malfunction");
> + break;
> + default:
> + ehea_error("unknown event code %x", ec);
> + break;
> + }
> +}
> +
> +void ehea_neq_tasklet(unsigned long data)
static ?
> +{
> + struct ehea_adapter *adapter = (struct ehea_adapter*)data;
> + struct ehea_eqe *eqe;
> + u64 event_mask;
> +
> + eqe = ehea_poll_eq(adapter->neq);
> + ehea_debug("eqe=%p", eqe);
> +
> + while (eqe) {
> + ehea_debug("*eqe=%lx", eqe->entry);
> + ehea_parse_eqe(adapter, eqe->entry);
> + eqe = ehea_poll_eq(adapter->neq);
> + ehea_debug("next eqe=%p", eqe);
> + }
> +
> + event_mask = EHEA_BMASK_SET(NELR_PORTSTATE_CHG, 1)
> + | EHEA_BMASK_SET(NELR_ADAPTER_MALFUNC, 1)
> + | EHEA_BMASK_SET(NELR_PORT_MALFUNC, 1);
> +
> + ehea_h_reset_events(adapter->handle,
> + adapter->neq->fw_handle, event_mask);
> +}
> +
> +irqreturn_t ehea_interrupt_neq(int irq, void *param, struct pt_regs *regs)
static ?
> +{
> + struct ehea_adapter *adapter = (struct ehea_adapter*)param;
Useless cast.
> + tasklet_hi_schedule(&adapter->neq_tasklet);
> + return IRQ_HANDLED;
> +}
> +
> +
> +static int ehea_fill_port_res(struct ehea_port_res *pr)
> +{
> + int ret;
> + struct ehea_qp_init_attr *init_attr = &pr->qp->init_attr;
> +
> + /* RQ 1 */
> + ret = ehea_init_fill_rq1(pr, init_attr->act_nr_rwqes_rq1
> + - init_attr->act_nr_rwqes_rq2
> + - init_attr->act_nr_rwqes_rq3 - 1);
> + /* RQ 2 */
Useless comment.
> + ret |= ehea_refill_rq2(pr, init_attr->act_nr_rwqes_rq2 - 1);
> +
> + /* RQ 3 */
> + ret |= ehea_refill_rq3(pr, init_attr->act_nr_rwqes_rq3 - 1);
> +
> + return ret;
> +}
> +
> +static int ehea_reg_interrupts(struct net_device *dev)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct ehea_port_res *pr;
> + int i, k, ret;
unsigned int i ?
> +
> + for (i = 0; i < port->num_def_qps; i++) {
> + pr = &port->port_res[i];
> + snprintf(pr->int_recv_name, EHEA_IRQ_NAME_SIZE - 1
> + , "%s-recv%d", dev->name, i);
> + ret = ibmebus_request_irq(NULL, pr->recv_eq->attr.ist1,
> + ehea_recv_irq_handler,
> + SA_INTERRUPT, pr->int_recv_name, pr);
> + if (ret) {
> + ehea_error("failed registering irq for ehea_recv_int:"
> + "port_res_nr:%d, ist=%X", i,
> + pr->recv_eq->attr.ist1);
> + for (k = 0; k < i; k++) {
> + u32 ist = port->port_res[k].recv_eq->attr.ist1;
> + ibmebus_free_irq(NULL, ist, &port->port_res[k]);
> + }
> + goto failure;
Poor label (and bloaty release practice too: remove k, reuse "i" below
and more importantly release the things in allocation-reversed order).
> + }
> + if (netif_msg_ifup(port))
> + ehea_info("irq_handle 0x%X for funct ehea_recv_int %d "
> + "registered", pr->recv_eq->attr.ist1, i);
> + }
> +
> + snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1,
> + "%s-aff", dev->name);
> + ret = ibmebus_request_irq(NULL, port->qp_eq->attr.ist1,
> + ehea_qp_aff_irq_handler,
> + SA_INTERRUPT, port->int_aff_name, port);
> + if (ret) {
> + ehea_error("failed registering irq for qp_aff_irq_handler:"
> + " ist=%X", port->qp_eq->attr.ist1);
> + goto failure2;
> + }
> + if (netif_msg_ifup(port))
> + ehea_info("irq_handle 0x%X for function qp_aff_irq_handler "
> + "registered", port->qp_eq->attr.ist1);
> +
> + for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
> + pr = &port->port_res[i];
> + snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
> + "%s-send%d", dev->name, i);
> + ret = ibmebus_request_irq(NULL, pr->send_eq->attr.ist1,
> + ehea_send_irq_handler,
> + SA_INTERRUPT, pr->int_send_name,
> + pr);
> + if (ret) {
> + ehea_error("failed registering irq for ehea_send"
> + " port_res_nr:%d, ist=%X", i,
> + pr->send_eq->attr.ist1);
> + for (k = 0; k < i; k++) {
> + u32 ist = port->port_res[k].send_eq->attr.ist1;
> + ibmebus_free_irq(NULL, ist, &port->port_res[i]);
> + }
> + goto failure3;
Sic.
> + }
> + if (netif_msg_ifup(port))
> + ehea_info("irq_handle 0x%X for function ehea_send_int "
> + "%d registered", pr->send_eq->attr.ist1, i);
> + }
> + return ret;
> +failure3:
> + for (i = 0; i < port->num_def_qps; i++)
> + ibmebus_free_irq(NULL, port->port_res[i].recv_eq->attr.ist1,
> + &port->port_res[i]);
Compare with:
u32 ist = port->port_res[k].recv_eq->attr.ist1;
ibmebus_free_irq(NULL, ist, &port->port_res[k]);
It was the first loop above. :o/
> +failure2:
> + ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port);
> +failure:
> + return ret;
> +}
> +
> +static void ehea_free_interrupts(struct net_device *dev)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + int i;
unsinged int
> +
> + /* send */
> + for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
> + ibmebus_free_irq(NULL, port->port_res[i].send_eq->attr.ist1,
> + &port->port_res[i]);
Please add a local 'struct shnortz *foo = port->port_res + i;'
> + if (netif_msg_intr(port))
> + ehea_info("free send irq for res %d with handle 0x%X",
> + i, port->port_res[i].send_eq->attr.ist1);
> + }
> +
> + /* receive */
> + for (i = 0; i < port->num_def_qps; i++) {
> + ibmebus_free_irq(NULL, port->port_res[i].recv_eq->attr.ist1,
> + &port->port_res[i]);
Sic.
> + if (netif_msg_intr(port))
> + ehea_info("free recv irq for res %d with handle 0x%X",
> + i, port->port_res[i].recv_eq->attr.ist1);
> + }
> +
> + /* associated events */
> + ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port);
> + if (netif_msg_intr(port))
> + ehea_info("associated event interrupt for handle 0x%X freed",
> + port->qp_eq->attr.ist1);
> +}
> +
> +static int ehea_configure_port(struct ehea_port *port)
> +{
> + int ret, i;
unsigned int ?
> + u64 hret, mask;
> + struct hcp_ehea_port_cb0 *cb0;
> +
> + cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb0) {
> + ret = -ENOMEM;
> + goto kzalloc_failed;
goto out;
> + }
> +
> + cb0->port_rc = EHEA_BMASK_SET(PXLY_RC_VALID, 1)
> + | EHEA_BMASK_SET(PXLY_RC_IP_CHKSUM, 1)
> + | EHEA_BMASK_SET(PXLY_RC_TCP_UDP_CHKSUM, 1)
> + | EHEA_BMASK_SET(PXLY_RC_VLAN_XTRACT, 1)
Tab/space
> + | EHEA_BMASK_SET(PXLY_RC_VLAN_TAG_FILTER,
> + PXLY_RC_VLAN_FILTER)
> + | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1);
> +
> + for (i = 0; i < port->num_def_qps; i++) {
> + cb0->default_qpn_array[i] =
> + port->port_res[i].qp->init_attr.qp_nr;
> + }
> +
> + if (netif_msg_ifup(port))
> + ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port");
> +
> + mask = EHEA_BMASK_SET(H_PORT_CB0_PRC, 1)
> + | EHEA_BMASK_SET(H_PORT_CB0_DEFQPNARRAY, 1);
> +
> + hret = ehea_h_modify_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB0, mask, cb0);
> + if (hret != H_SUCCESS) {
> + ret = -EIO;
Why can't ehea_xyz return -EIO/0 directly ?
> + goto modify_ehea_port_failed;
> + }
> +
> + ret = 0;
> +
> +modify_ehea_port_failed:
> + kfree(cb0);
> +
> +kzalloc_failed:
> + return ret;
> +}
> +
> +static int ehea_gen_smrs(struct ehea_port_res *pr)
> +{
> + u64 hret;
> + struct ehea_adapter *adapter = pr->port->adapter;
> +
> + hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle,
> + adapter->mr.vaddr, EHEA_MR_ACC_CTRL,
> + adapter->pd, &pr->send_mr);
> + if (hret != H_SUCCESS)
> + goto ehea_gen_smrs_err1;
> +
> + hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle,
> + adapter->mr.vaddr, EHEA_MR_ACC_CTRL,
> + adapter->pd, &pr->recv_mr);
> + if (hret != H_SUCCESS)
> + goto ehea_gen_smrs_err2;
> +
> + return 0;
> +
> +ehea_gen_smrs_err2:
> + hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle);
> + if (hret != H_SUCCESS)
> + ehea_error("failed freeing SMR");
> +ehea_gen_smrs_err1:
> + return -EIO;
> +}
> +
> +static int ehea_rem_smrs(struct ehea_port_res *pr)
> +{
> + struct ehea_adapter *adapter = pr->port->adapter;
> + int ret = 0;
> + u64 hret;
> +
> + hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle);
> + if (hret != H_SUCCESS) {
> + ret = -EIO;
> + ehea_error("failed freeing send SMR for pr=%p", pr);
> + }
> +
> + hret = ehea_h_free_resource(adapter->handle, pr->recv_mr.handle);
> + if (hret != H_SUCCESS) {
> + ret = -EIO;
> + ehea_error("failed freeing recv SMR for pr=%p", pr);
> + }
> +
> + return ret;
> +}
> +
> +static int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries)
> +{
> + int arr_size = sizeof(void*) * max_q_entries;
> +
> + q_skba->arr = vmalloc(arr_size);
> + if (!q_skba->arr)
> + return -ENOMEM;
> +
> + memset(q_skba->arr, 0, arr_size);
> +
> + q_skba->len = max_q_entries;
> + q_skba->index = 0;
> + q_skba->os_skbs = 0;
> +
> + return 0;
> +}
> +
> +static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr,
> + struct port_res_cfg *pr_cfg, int queue_token)
> +{
> + struct ehea_adapter *adapter = port->adapter;
> + struct ehea_qp_init_attr *init_attr = NULL;
Useless initialization.
> + enum ehea_eq_type eq_type = EHEA_EQ;
> + int ret;
> +
> + memset(pr, 0, sizeof(struct ehea_port_res));
> +
> + pr->port = port;
> + spin_lock_init(&pr->send_lock);
> + spin_lock_init(&pr->recv_lock);
> + spin_lock_init(&pr->xmit_lock);
> + spin_lock_init(&pr->netif_queue);
> +
> + pr->recv_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
> + if (!pr->recv_eq) {
> + ehea_error("create_eq failed (recv_eq)");
> + ret = -EIO;
> + goto ehea_init_port_res_err;
> + }
> +
> + pr->send_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
> + if (!pr->send_eq) {
> + ehea_error("create_eq failed (send_eq)");
> + ret = -EIO;
> + goto ehea_init_port_res_err;
Should factor 'ret = -EIO' before the sequence.
> + }
> +
> + pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq,
> + pr->recv_eq->fw_handle,
> + port->logical_port_id);
> + if (!pr->recv_cq) {
> + ehea_error("create_cq failed (cq_recv)");
> + ret = -EIO;
> + goto ehea_init_port_res_err;
Unwind path are boring, aren't they ? :o(
> + }
> +
> + pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq,
> + pr->send_eq->fw_handle,
> + port->logical_port_id);
> + if (!pr->send_cq) {
> + ehea_error("create_cq failed (cq_send)");
> + ret = -EIO;
> + goto ehea_init_port_res_err;
> + }
> +
> + if (netif_msg_ifup(port))
> + ehea_info("Send CQ: act_nr_cqes=%d, Recv CQ: act_nr_cqes=%d",
> + pr->send_cq->attr.act_nr_of_cqes,
> + pr->recv_cq->attr.act_nr_of_cqes);
> +
> + init_attr = kzalloc(sizeof(*init_attr), GFP_KERNEL);
> + if (!init_attr) {
> + ret = -ENOMEM;
> + ehea_error("no mem for ehea_qp_init_attr");
> + goto ehea_init_port_res_err;
> + }
> +
> + init_attr->low_lat_rq1 = 1;
> + init_attr->signalingtype = 1; /* generate CQE if specified in WQE */
> + init_attr->rq_count = 3;
> + init_attr->qp_token = queue_token;
> + init_attr->max_nr_send_wqes = pr_cfg->max_entries_sq;
> + init_attr->max_nr_rwqes_rq1 = pr_cfg->max_entries_rq1;
> + init_attr->max_nr_rwqes_rq2 = pr_cfg->max_entries_rq2;
> + init_attr->max_nr_rwqes_rq3 = pr_cfg->max_entries_rq3;
> + init_attr->wqe_size_enc_sq = EHEA_SG_SQ;
> + init_attr->wqe_size_enc_rq1 = EHEA_SG_RQ1;
> + init_attr->wqe_size_enc_rq2 = EHEA_SG_RQ2;
> + init_attr->wqe_size_enc_rq3 = EHEA_SG_RQ3;
> + init_attr->rq2_threshold = EHEA_RQ2_THRESHOLD;
> + init_attr->rq3_threshold = EHEA_RQ3_THRESHOLD;
> + init_attr->port_nr = port->logical_port_id;
> + init_attr->send_cq_handle = pr->send_cq->fw_handle;
> + init_attr->recv_cq_handle = pr->recv_cq->fw_handle;
> + init_attr->aff_eq_handle = port->qp_eq->fw_handle;
> +
> + pr->qp = ehea_create_qp(adapter, adapter->pd, init_attr);
> + if (!pr->qp) {
> + ehea_error("create_qp failed");
> + ret = -EIO;
> + goto ehea_init_port_res_err;
> + }
> +
> + if (netif_msg_ifup(port))
> + ehea_info("QP: qp_nr=%d\n act_nr_snd_wqe=%d\n nr_rwqe_rq1=%d\n "
> + "nr_rwqe_rq2=%d\n nr_rwqe_rq3=%d", init_attr->qp_nr,
> + init_attr->act_nr_send_wqes,
> + init_attr->act_nr_rwqes_rq1,
> + init_attr->act_nr_rwqes_rq2,
> + init_attr->act_nr_rwqes_rq3);
> +
> + ret = ehea_init_q_skba(&pr->sq_skba, init_attr->act_nr_send_wqes + 1);
> + ret |= ehea_init_q_skba(&pr->rq1_skba, init_attr->act_nr_rwqes_rq1 + 1);
> + ret |= ehea_init_q_skba(&pr->rq2_skba, init_attr->act_nr_rwqes_rq2 + 1);
> + ret |= ehea_init_q_skba(&pr->rq3_skba, init_attr->act_nr_rwqes_rq3 + 1);
> + if (ret)
> + goto ehea_init_port_res_err;
> +
> + if (ehea_gen_smrs(pr) != 0) {
> + ret = -EIO;
> + goto ehea_init_port_res_err;
> + }
> + tasklet_init(&pr->send_comp_task, ehea_send_irq_tasklet,
> + (unsigned long)pr);
> + atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1);
> +
> + kfree(init_attr);
> + ret = 0;
> + goto done;
> +
> +ehea_init_port_res_err:
> + vfree(pr->sq_skba.arr);
> + vfree(pr->rq1_skba.arr);
> + vfree(pr->rq2_skba.arr);
> + vfree(pr->rq3_skba.arr);
> + ehea_destroy_qp(pr->qp);
> + kfree(init_attr);
> + ehea_destroy_cq(pr->send_cq);
> + ehea_destroy_cq(pr->recv_cq);
> + ehea_destroy_eq(pr->send_eq);
> + ehea_destroy_eq(pr->recv_eq);
Sigh.
> +done:
> + return ret;
> +}
> +
> +static int ehea_clean_port_res(struct ehea_port *port, struct ehea_port_res *pr)
> +{
> + int ret, i;
> +
> + ret = ehea_destroy_qp(pr->qp);
> +
> + if (!ret) {
> + ehea_destroy_cq(pr->send_cq);
> + ehea_destroy_cq(pr->recv_cq);
> + ehea_destroy_eq(pr->send_eq);
> + ehea_destroy_eq(pr->recv_eq);
> +
> + for (i = 0; i < pr->rq1_skba.len; i++)
> + if (pr->rq1_skba.arr[i])
> + dev_kfree_skb(pr->rq1_skba.arr[i]);
> +
> + for (i = 0; i < pr->rq2_skba.len; i++)
> + if (pr->rq2_skba.arr[i])
> + dev_kfree_skb(pr->rq2_skba.arr[i]);
> +
> + for (i = 0; i < pr->rq3_skba.len; i++)
> + if (pr->rq3_skba.arr[i])
> + dev_kfree_skb(pr->rq3_skba.arr[i]);
> +
> + for (i = 0; i < pr->sq_skba.len; i++)
> + if (pr->sq_skba.arr[i])
> + dev_kfree_skb(pr->sq_skba.arr[i]);
Feels like a 0..4 loop is missing above.
> +
> + vfree(pr->rq1_skba.arr);
> + vfree(pr->rq2_skba.arr);
> + vfree(pr->rq3_skba.arr);
> + vfree(pr->sq_skba.arr);
> + ret = ehea_rem_smrs(pr);
> + }
> + return ret;
> +}
> +
> +/*
> + * The write_* functions store information in swqe which is used by
> + * the hardware to calculate the ip/tcp/udp checksum
> + */
> +
> +static inline void write_ip_start_end(struct ehea_swqe *swqe,
> + const struct sk_buff *skb)
> +{
> + swqe->ip_start = (u8)(((u64)skb->nh.iph) - ((u64)skb->data));
> + swqe->ip_end = (u8)(swqe->ip_start + skb->nh.iph->ihl * 4 - 1);
> +}
> +
> +static inline void write_tcp_offset_end(struct ehea_swqe *swqe,
> + const struct sk_buff *skb)
> +{
> + swqe->tcp_offset =
> + (u8)(swqe->ip_end + 1 + offsetof(struct tcphdr, check));
> +
> + swqe->tcp_end = (u16)skb->len - 1;
> +}
> +
> +static inline void write_udp_offset_end(struct ehea_swqe *swqe,
> + const struct sk_buff *skb)
> +{
> + swqe->tcp_offset =
> + (u8)(swqe->ip_end + 1 + offsetof(struct udphdr, check));
> +
> + swqe->tcp_end = (u16)skb->len - 1;
> +}
> +
> +
> +static inline void write_swqe2_TSO(struct sk_buff *skb,
> + struct ehea_swqe *swqe, u32 lkey)
> +{
> + struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
> + u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
> + int skb_data_size = skb->len - skb->data_len;
> + int headersize;
> + u64 tmp_addr;
> +
> + /* Packet is TCP with TSO enabled */
> + swqe->tx_control |= EHEA_SWQE_TSO;
> + swqe->mss = skb_shinfo(skb)->gso_size;
> + /* copy only eth/ip/tcp headers to immediate data and
> + * the rest of skb->data to sg1entry
> + */
> + headersize = ETH_HLEN + (skb->nh.iph->ihl * 4) + (skb->h.th->doff * 4);
> +
> + skb_data_size = skb->len - skb->data_len;
> +
> + if (skb_data_size >= headersize) {
> + /* copy immediate data */
> + memcpy(imm_data, skb->data, headersize);
> + swqe->immediate_data_length = headersize;
> +
> + if (skb_data_size > headersize) {
> + /* set sg1entry data */
> + sg1entry->l_key = lkey;
> + sg1entry->len = skb_data_size - headersize;
> +
> + tmp_addr = (u64)(skb->data + headersize);
> + sg1entry->vaddr = tmp_addr;
> + swqe->descriptors++;
> + }
> + } else
> + ehea_error("cannot handle fragmented headers");
> +}
> +
> +static inline void write_swqe2_nonTSO(struct sk_buff *skb,
> + struct ehea_swqe *swqe, u32 lkey)
> +{
> + int skb_data_size = skb->len - skb->data_len;
> + u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
> + struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
> + u64 tmp_addr;
> +
> + /* Packet is any nonTSO type
> + *
> + * Copy as much as possible skb->data to immediate data and
> + * the rest to sg1entry
> + */
> + if (skb_data_size >= SWQE2_MAX_IMM) {
> + /* copy immediate data */
> + memcpy(imm_data, skb->data, SWQE2_MAX_IMM);
> +
> + swqe->immediate_data_length = SWQE2_MAX_IMM;
> +
> + if (skb_data_size > SWQE2_MAX_IMM) {
> + /* copy sg1entry data */
> + sg1entry->l_key = lkey;
> + sg1entry->len = skb_data_size - SWQE2_MAX_IMM;
> + tmp_addr = (u64)(skb->data + SWQE2_MAX_IMM);
> + sg1entry->vaddr = tmp_addr;
> + swqe->descriptors++;
> + }
> + } else {
> + memcpy(imm_data, skb->data, skb_data_size);
> + swqe->immediate_data_length = skb_data_size;
> + }
> +}
> +
> +static inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
> + struct ehea_swqe *swqe, u32 lkey)
> +{
> + struct ehea_vsgentry *sg_list, *sg1entry, *sgentry;
> + skb_frag_t *frag;
> + int nfrags, sg1entry_contains_frag_data, i;
> + u64 tmp_addr;
> +
> + nfrags = skb_shinfo(skb)->nr_frags;
> + sg1entry = &swqe->u.immdata_desc.sg_entry;
> + sg_list = (struct ehea_vsgentry*)&swqe->u.immdata_desc.sg_list;
> + swqe->descriptors = 0;
> + sg1entry_contains_frag_data = 0;
> +
> + if ((dev->features & NETIF_F_TSO) && skb_shinfo(skb)->gso_size)
> + write_swqe2_TSO(skb, swqe, lkey);
> + else
> + write_swqe2_nonTSO(skb, swqe, lkey);
> +
> + /* write descriptors */
> + if (nfrags > 0) {
> + if (swqe->descriptors == 0) {
> + /* sg1entry not yet used */
> + frag = &skb_shinfo(skb)->frags[0];
> +
> + /* copy sg1entry data */
> + sg1entry->l_key = lkey;
> + sg1entry->len = frag->size;
> + tmp_addr = (u64)(page_address(frag->page)
> + + frag->page_offset);
> + sg1entry->vaddr = tmp_addr;
> + swqe->descriptors++;
> + sg1entry_contains_frag_data = 1;
> + }
> +
> + for (i = sg1entry_contains_frag_data; i < nfrags; i++) {
> +
> + frag = &skb_shinfo(skb)->frags[i];
> + sgentry = &sg_list[i - sg1entry_contains_frag_data];
> +
> + sgentry->l_key = lkey;
> + sgentry->len = frag->size;
> +
> + tmp_addr = (u64)(page_address(frag->page)
> + + frag->page_offset);
> + sgentry->vaddr = tmp_addr;
> + swqe->descriptors++;
> + }
> + }
> +}
> +
> +static int ehea_broadcast_reg_helper(struct ehea_port *port, u32 hcallid)
> +{
> + int ret = 0;
> + u64 hret;
> + u8 reg_type;
> +
> + /* De/Register untagged packets */
> + reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_UNTAGGED;
> + hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
> + port->logical_port_id,
> + reg_type, port->mac_addr, 0, hcallid);
> + if (hret != H_SUCCESS) {
> + ehea_error("reg_dereg_bcmc failed (tagged)");
> + ret = -EIO;
> + goto hcall_failed;
> + }
> +
> + /* De/Register VLAN packets */
> + reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_VLANID_ALL;
> + hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
> + port->logical_port_id,
> + reg_type, port->mac_addr, 0, hcallid);
> + if (hret != H_SUCCESS) {
> + ehea_error("reg_dereg_bcmc failed (vlan)");
> + ret = -EIO;
> + }
> +hcall_failed:
> + return ret;
> +}
> +
> +static int ehea_set_mac_addr(struct net_device *dev, void *sa)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct sockaddr *mac_addr = (struct sockaddr*)sa;
> + struct hcp_ehea_port_cb0 *cb0;
> + int ret;
> + u64 hret;
> +
> + if (!is_valid_ether_addr(mac_addr->sa_data)) {
> + ret = -EADDRNOTAVAIL;
> + goto exit0;
> + }
> +
> + cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb0) {
> + ehea_error("no mem for cb0");
> + ret = -ENOMEM;
> + goto exit0;
> + }
> +
> + memcpy(&(cb0->port_mac_addr), &(mac_addr->sa_data[0]), ETH_ALEN);
> +
> + cb0->port_mac_addr = cb0->port_mac_addr >> 16;
> +
> + hret = ehea_h_modify_ehea_port(port->adapter->handle,
> + port->logical_port_id, H_PORT_CB0,
> + EHEA_BMASK_SET(H_PORT_CB0_MAC, 1), cb0);
> + if (hret != H_SUCCESS) {
> + ret = -EIO;
> + goto exit1;
> + }
> +
> + memcpy(dev->dev_addr, mac_addr->sa_data, dev->addr_len);
> +
> + /* Deregister old MAC in pHYP */
> + ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
> + if (ret)
> + goto exit1;
> +
> + port->mac_addr = cb0->port_mac_addr << 16;
> +
> + /* Register new MAC in pHYP */
> + ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
> + if (ret)
> + goto exit1;
> +
> + ret = 0;
> +exit1:
> + kfree(cb0);
> +exit0:
> + return ret;
> +}
> +
> +static void ehea_promiscuous_error(u64 hret, int enable)
> +{
> + ehea_info("Hypervisor denied %sabling promiscuous mode.%s",
> + enable == 1 ? "en" : "dis",
> + hret != H_AUTHORITY ? "" : " Another partition owning a "
> + "logical port on the same physical port might have altered "
> + "promiscuous mode first.");
> +}
> +
> +static void ehea_promiscuous(struct net_device *dev, int enable)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct hcp_ehea_port_cb5 *cb5;
> + struct hcp_ehea_port_cb7 *cb7;
> + u64 hret;
> +
> + if ((enable && port->promisc) || (!enable && !port->promisc))
> + return;
> +
> + cb5 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + cb7 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb5 || !cb7) {
> + ehea_error("no mem for cb5/cb7");
> + goto error;
> + }
> +
> + /* Modify Pxs_RCU in CB5 */
> + hret = ehea_h_query_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB5, H_PORT_CB5_RCU, cb5);
> + if (hret) {
> + ehea_promiscuous_error(hret, enable);
> + goto error;
> + }
> +
> + if (enable)
> + cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0xFFFF);
> + else
> + cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0);
> +
> + hret = ehea_h_modify_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB5, H_PORT_CB5_RCU, cb5);
> + if (hret) {
> + ehea_promiscuous_error(hret, enable);
> + goto error;
> + }
> +
> + /* Modify Pxs_DUCQPN in CB7 */
> + cb7->def_uc_qpn = enable == 1 ? port->port_res[0].qp->fw_handle : 0;
> +
> + hret = ehea_h_modify_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB7, H_PORT_CB7_DUCQPN, cb7);
> + if (hret) {
> + /* Rollback */
> + if (enable)
> + cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0);
> + else
> + cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0xFFFF);
> +
> + ehea_h_modify_ehea_port(port->adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB5, H_PORT_CB5_RCU, cb5);
> +
> + ehea_promiscuous_error(hret, enable);
> + goto error;
> + }
> +
> + port->promisc = enable;
> +error:
> + kfree(cb5);
> + kfree(cb7);
> + return;
> +}
> +
> +static u64 ehea_multicast_reg_helper(struct ehea_port *port, u64 mc_mac_addr,
> + u32 hcallid)
> +{
> + u64 hret;
> + u8 reg_type;
> +
> + reg_type = EHEA_BCMC_SCOPE_ALL | EHEA_BCMC_MULTICAST
> + | EHEA_BCMC_UNTAGGED;
> +
> + hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
> + port->logical_port_id,
> + reg_type, mc_mac_addr, 0, hcallid);
> + if (hret)
> + goto hcall_failed;
> +
> + reg_type = EHEA_BCMC_SCOPE_ALL | EHEA_BCMC_MULTICAST
> + | EHEA_BCMC_VLANID_ALL;
> +
> + hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
> + port->logical_port_id,
> + reg_type, mc_mac_addr, 0, hcallid);
> +hcall_failed:
> + return hret;
> +}
> +
> +static int ehea_drop_multicast_list(struct net_device *dev)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct ehea_mc_list *mc_entry = port->mc_list;
> + struct list_head *pos;
> + struct list_head *temp;
> + int ret = 0;
> + u64 hret;
> +
> + list_for_each_safe(pos, temp, &(port->mc_list->list)) {
> + mc_entry = list_entry(pos, struct ehea_mc_list, list);
> +
> + hret = ehea_multicast_reg_helper(port, mc_entry->macaddr,
> + H_DEREG_BCMC);
> + if (hret) {
> + ehea_error("failed deregistering mcast MAC");
> + ret = -EIO;
> + }
> +
> + list_del(pos);
> + kfree(mc_entry);
> + }
> + return ret;
> +}
> +
> +static void ehea_allmulti(struct net_device *dev, int enable)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + u64 hret;
> +
> + if (!port->allmulti) {
> + if (enable) {
> + /* Enable ALLMULTI */
> + ehea_drop_multicast_list(dev);
> + hret = ehea_multicast_reg_helper(port, 0, H_REG_BCMC);
> + if (!hret)
> + port->allmulti = 1;
> + else
> + ehea_error("failed enabling IFF_ALLMULTI");
> + }
> + } else
> + if (!enable) {
> + /* Disable ALLMULTI */
> + hret = ehea_multicast_reg_helper(port, 0, H_DEREG_BCMC);
> + if (!hret)
> + port->allmulti = 0;
> + else
> + ehea_error("failed disabling IFF_ALLMULTI");
> + }
> +}
> +
> +static void ehea_add_multicast_entry(struct ehea_port* port, u8* mc_mac_addr)
> +{
> + struct ehea_mc_list *ehea_mcl_entry;
> + u64 hret;
> +
> + ehea_mcl_entry = kzalloc(sizeof(*ehea_mcl_entry), GFP_KERNEL);
> + if (!ehea_mcl_entry) {
> + ehea_error("no mem for mcl_entry");
> + return;
> + }
> +
> + INIT_LIST_HEAD(&ehea_mcl_entry->list);
> +
> + memcpy(&ehea_mcl_entry->macaddr, mc_mac_addr, ETH_ALEN);
> +
> + hret = ehea_multicast_reg_helper(port, ehea_mcl_entry->macaddr,
> + H_REG_BCMC);
> + if (!hret)
> + list_add(&ehea_mcl_entry->list, &port->mc_list->list);
> + else {
> + ehea_error("failed registering mcast MAC");
> + kfree(ehea_mcl_entry);
> + }
> +}
> +
> +static void ehea_set_multicast_list(struct net_device *dev)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct dev_mc_list *k_mcl_entry;
> + int ret, i;
> +
> + if (dev->flags & IFF_PROMISC) {
> + ehea_promiscuous(dev, 1);
> + return;
> + }
> + ehea_promiscuous(dev, 0);
> +
> + if (dev->flags & IFF_ALLMULTI) {
> + ehea_allmulti(dev, 1);
> + return;
> + }
> + ehea_allmulti(dev, 0);
> +
> + if (dev->mc_count) {
> + ret = ehea_drop_multicast_list(dev);
> + if (ret) {
> + /* Dropping the current multicast list failed.
> + * Enabling ALL_MULTI is the best we can do.
> + */
> + ehea_allmulti(dev, 1);
> + }
> +
> + if (dev->mc_count > port->adapter->max_mc_mac) {
> + ehea_info("Mcast registration limit reached (0x%lx). "
> + "Use ALLMULTI!",
> + port->adapter->max_mc_mac);
> + goto escape;
> + }
> +
> + for (i = 0, k_mcl_entry = dev->mc_list;
> + i < dev->mc_count;
> + i++, k_mcl_entry = k_mcl_entry->next) {
> + ehea_add_multicast_entry(port, k_mcl_entry->dmi_addr);
> + }
> + }
> +escape:
> + return;
> +}
> +
> +static int ehea_change_mtu(struct net_device *dev, int new_mtu)
> +{
> + if ((new_mtu < 68) || (new_mtu > EHEA_MAX_PACKET_SIZE))
> + return -EINVAL;
> + dev->mtu = new_mtu;
> + return 0;
> +}
> +
> +static inline void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
> + struct ehea_swqe *swqe, u32 lkey)
> +{
> + if (skb->protocol == htons(ETH_P_IP)) {
> + /* IPv4 */
> + swqe->tx_control |= EHEA_SWQE_CRC
> + | EHEA_SWQE_IP_CHECKSUM
> + | EHEA_SWQE_TCP_CHECKSUM
> + | EHEA_SWQE_IMM_DATA_PRESENT
> + | EHEA_SWQE_DESCRIPTORS_PRESENT;
> +
> + write_ip_start_end(swqe, skb);
> +
> + if (skb->nh.iph->protocol == IPPROTO_UDP) {
> + if ((skb->nh.iph->frag_off & IP_MF)
> + || (skb->nh.iph->frag_off & IP_OFFSET))
> + /* IP fragment, so don't change cs */
> + swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM;
> + else
> + write_udp_offset_end(swqe, skb);
> +
> + } else if (skb->nh.iph->protocol == IPPROTO_TCP) {
> + write_tcp_offset_end(swqe, skb);
> + }
> +
> + /* icmp (big data) and ip segmentation packets (all other ip
> + packets) do not require any special handling */
> +
> + } else {
> + /* Other Ethernet Protocol */
> + swqe->tx_control |= EHEA_SWQE_CRC
> + | EHEA_SWQE_IMM_DATA_PRESENT
> + | EHEA_SWQE_DESCRIPTORS_PRESENT;
> + }
> +
> + write_swqe2_data(skb, dev, swqe, lkey);
> +}
> +
> +static inline void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
> + struct ehea_swqe *swqe)
> +{
> + int nfrags = skb_shinfo(skb)->nr_frags;
> + u8 *imm_data = &swqe->u.immdata_nodesc.immediate_data[0];
> + skb_frag_t *frag;
> + int i;
> +
> + if (likely(skb->protocol == htons(ETH_P_IP))) {
> + /* IPv4 */
> + write_ip_start_end(swqe, skb);
> +
> + if (skb->nh.iph->protocol == IPPROTO_TCP) {
> + swqe->tx_control |= EHEA_SWQE_CRC
> + | EHEA_SWQE_IP_CHECKSUM
> + | EHEA_SWQE_TCP_CHECKSUM
> + | EHEA_SWQE_IMM_DATA_PRESENT;
> +
> + write_tcp_offset_end(swqe, skb);
> +
> + } else if (skb->nh.iph->protocol == IPPROTO_UDP) {
> + if ((skb->nh.iph->frag_off & IP_MF)
> + || (skb->nh.iph->frag_off & IP_OFFSET))
> + /* IP fragment, so don't change cs */
> + swqe->tx_control |= EHEA_SWQE_CRC
> + | EHEA_SWQE_IMM_DATA_PRESENT;
> + else {
> + swqe->tx_control |= EHEA_SWQE_CRC
> + | EHEA_SWQE_IP_CHECKSUM
> + | EHEA_SWQE_TCP_CHECKSUM
> + | EHEA_SWQE_IMM_DATA_PRESENT;
> +
> + write_udp_offset_end(swqe, skb);
> + }
> + } else {
> + /* icmp (big data) and
> + ip segmentation packets (all other ip packets) */
> + swqe->tx_control |= EHEA_SWQE_CRC
> + | EHEA_SWQE_IP_CHECKSUM
> + | EHEA_SWQE_IMM_DATA_PRESENT;
> + }
> + } else {
> + /* Other Ethernet Protocol */
> + swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IMM_DATA_PRESENT;
> + }
> + /* copy (immediate) data */
> + if (nfrags == 0) {
> + /* data is in a single piece */
> + memcpy(imm_data, skb->data, skb->len);
> + } else {
> + /* first copy data from the skb->data buffer ... */
> + memcpy(imm_data, skb->data, skb->len - skb->data_len);
> + imm_data += skb->len - skb->data_len;
> +
> + /* ... then copy data from the fragments */
> + for (i = 0; i < nfrags; i++) {
> + frag = &skb_shinfo(skb)->frags[i];
> + memcpy(imm_data,
> + page_address(frag->page) + frag->page_offset,
> + frag->size);
> + imm_data += frag->size;
> + }
> + }
> + swqe->immediate_data_length = skb->len;
> + dev_kfree_skb(skb);
> +}
> +
> +static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct ehea_port_res *pr;
> + struct ehea_swqe *swqe;
> + unsigned long flags;
> + u32 lkey;
> + int swqe_index;
> +
> + pr = &port->port_res[0];
Initialization and declaration can happen at the same time.
> +
> + if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
> + spin_lock_irqsave(&pr->netif_queue, flags);
> + if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
> + netif_stop_queue(dev);
> + pr->queue_stopped = 1;
> + spin_unlock_irqrestore(&pr->netif_queue, flags);
> + return NETDEV_TX_BUSY;
1 - this is considered a severe bug. You should stop queueing before it
happens.
2 - don't mix spinlocked sections and stealth return.
> + }
> + spin_unlock_irqrestore(&pr->netif_queue, flags);
> + }
> + atomic_dec(&pr->swqe_avail);
> +
> + spin_lock(&pr->xmit_lock);
> +
> + swqe = ehea_get_swqe(pr->qp, &swqe_index);
> + memset(swqe, 0, SWQE_HEADER_SIZE);
> +
> + if (skb->len <= SWQE3_MAX_IMM) {
> + u32 sig_iv = port->sig_comp_iv;
> + u32 swqe_num = pr->swqe_id_counter;
> + ehea_xmit3(skb, dev, swqe);
> + swqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE3_TYPE)
> + | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, swqe_num);
> + if (pr->swqe_ll_count >= (sig_iv - 1)) {
> + swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
> + sig_iv);
> + swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
> + pr->swqe_ll_count = 0;
> + } else
> + pr->swqe_ll_count += 1;
> + } else {
> + swqe->wr_id =
> + EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE)
> + | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter)
> + | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index);
> + pr->sq_skba.arr[pr->sq_skba.index] = skb;
> +
> + pr->sq_skba.index++;
> + pr->sq_skba.index &= (pr->sq_skba.len - 1);
> +
> + lkey = pr->send_mr.lkey;
> + ehea_xmit2(skb, dev, swqe, lkey);
> +
> + if (pr->swqe_count >= (EHEA_SIG_IV_LONG - 1)) {
> + swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
> + EHEA_SIG_IV_LONG);
> + swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
> + pr->swqe_count = 0;
> + } else
> + pr->swqe_count += 1;
> + }
> + pr->swqe_id_counter += 1;
> +
> + if (port->vgrp && vlan_tx_tag_present(skb)) {
> + swqe->tx_control |= EHEA_SWQE_VLAN_INSERT;
> + swqe->vlan_tag = vlan_tx_tag_get(skb);
> + }
> +
> + if (netif_msg_tx_queued(port)) {
> + ehea_info("post swqe on QP %d", pr->qp->init_attr.qp_nr);
> + ehea_dump(swqe, sizeof(*swqe), "swqe");
> + }
> +
> + ehea_post_swqe(pr->qp, swqe);
> + pr->tx_packets++;
> + spin_unlock(&pr->xmit_lock);
> +
> + return NETDEV_TX_OK;
> +}
> +
> +static void ehea_vlan_rx_register(struct net_device *dev,
> + struct vlan_group *grp)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct ehea_adapter *adapter = port->adapter;
> + struct hcp_ehea_port_cb1 *cb1;
> + u64 hret;
> +
> + port->vgrp = grp;
> +
> + cb1 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb1) {
> + ehea_error("no mem for cb1");
> + goto exit0;
> + }
> +
> + if (grp)
> + memset(cb1->vlan_filter, 0, sizeof(cb1->vlan_filter));
> + else
> + memset(cb1->vlan_filter, 0xFF, sizeof(cb1->vlan_filter));
> +
> + hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
> + H_PORT_CB1, H_PORT_CB1_ALL, cb1);
> + if (hret != H_SUCCESS)
> + ehea_error("modify_ehea_port failed");
> +
> + kfree(cb1);
> +exit0:
> + return;
> +}
> +
> +static void ehea_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct ehea_adapter *adapter = port->adapter;
> + struct hcp_ehea_port_cb1 *cb1;
> + int index;
> + u64 hret;
> +
> + cb1 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb1) {
> + ehea_error("no mem for cb1");
> + goto vlan_kill_exit;
> + }
> +
> + hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
> + H_PORT_CB1, H_PORT_CB1_ALL, cb1);
> + if (hret != H_SUCCESS) {
> + ehea_error("query_ehea_port failed");
> + goto vlan_kill_exit;
> + }
> +
> + index = (vid / 64);
> + cb1->vlan_filter[index] |= ((u64)(1 << (vid & 0x3F)));
> +
> + hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
> + H_PORT_CB1, H_PORT_CB1_ALL, cb1);
> + if (hret != H_SUCCESS)
> + ehea_error("modify_ehea_port failed");
> +
> + kfree(cb1);
> +
> +vlan_kill_exit:
> + return;
> +
> +}
> +
> +static void ehea_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> + struct ehea_adapter *adapter = port->adapter;
> + struct hcp_ehea_port_cb1 *cb1;
> + int index;
> + u64 hret;
> +
> + if (port->vgrp)
> + port->vgrp->vlan_devices[vid] = NULL;
> +
> + cb1 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb1) {
> + ehea_error("no mem for cb1");
> + goto exit0;
> + }
> +
> + hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
> + H_PORT_CB1, H_PORT_CB1_ALL, cb1);
> + if (hret != H_SUCCESS) {
> + ehea_error("query_ehea_port failed");
> + goto exit0;
> + }
> +
> + index = (vid / 64);
> + cb1->vlan_filter[index] &= ~((u64)(1 << (vid & 0x3F)));
> +
> + hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
> + H_PORT_CB1, H_PORT_CB1_ALL, cb1);
> + if (hret != H_SUCCESS)
> + ehea_error("modify_ehea_port failed");
> +exit0:
> + kfree(cb1);
> + return;
> +}
> +
> +int ehea_activate_qp(struct ehea_adapter *adapter, struct ehea_qp *qp)
> +{
> + int ret = -EIO;
> + u64 hret;
> + u16 dummy16 = 0;
> + u64 dummy64 = 0;
> + struct hcp_modify_qp_cb0* cb0;
> +
> + cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb0) {
> + ret = -ENOMEM;
> + goto exit0;
> + }
> +
> + hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
> + EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
> + if (hret != H_SUCCESS) {
> + ehea_error("query_ehea_qp failed (1)");
> + goto exit0;
> + }
> +
> + cb0->qp_ctl_reg = H_QP_CR_STATE_INITIALIZED;
> + hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
> + EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
> + &dummy64, &dummy64, &dummy16, &dummy16);
> + if (hret != H_SUCCESS) {
> + ehea_error("modify_ehea_qp failed (1)");
> + goto exit0;
> + }
> +
> + hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
> + EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
> + if (hret != H_SUCCESS) {
> + ehea_error("query_ehea_qp failed (2)");
> + goto exit0;
> + }
> +
> + cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_INITIALIZED;
> + hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
> + EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
> + &dummy64, &dummy64, &dummy16, &dummy16);
> + if (hret != H_SUCCESS) {
> + ehea_error("modify_ehea_qp failed (2)");
> + goto exit0;
> + }
> +
> + hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
> + EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
> + if (hret != H_SUCCESS) {
> + ehea_error("query_ehea_qp failed (3)");
> + goto exit0;
> + }
> +
> + cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_RDY2SND;
> + hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
> + EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
> + &dummy64, &dummy64, &dummy16, &dummy16);
> + if (hret != H_SUCCESS) {
> + ehea_error("modify_ehea_qp failed (3)");
> + goto exit0;
> + }
> +
> + hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
> + EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
> + if (hret != H_SUCCESS) {
> + ehea_error("query_ehea_qp failed (4)");
> + goto exit0;
> + }
> +
> + ret = 0;
> +
> +exit0:
> + kfree(cb0);
> + return ret;
> +}
> +
> +static int ehea_port_res_setup(struct ehea_port *port, int def_qps,
> + int add_tx_qps)
> +{
> + int ret, i, k;
> + struct port_res_cfg pr_cfg, pr_cfg_small_rx;
> + enum ehea_eq_type eq_type = EHEA_EQ;
> +
> + port->qp_eq = ehea_create_eq(port->adapter, eq_type,
> + EHEA_MAX_ENTRIES_EQ, 1);
> + if (!port->qp_eq) {
> + ret = -EINVAL;
> + ehea_error("ehea_create_eq failed (qp_eq)");
> + goto port_res_setup_failed2;
> + }
> +
> + pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries;
> + pr_cfg.max_entries_scq = sq_entries;
> + pr_cfg.max_entries_sq = sq_entries;
> + pr_cfg.max_entries_rq1 = rq1_entries;
> + pr_cfg.max_entries_rq2 = rq2_entries;
> + pr_cfg.max_entries_rq3 = rq3_entries;
> +
> + pr_cfg_small_rx.max_entries_rcq = 1;
> + pr_cfg_small_rx.max_entries_scq = sq_entries;
> + pr_cfg_small_rx.max_entries_sq = sq_entries;
> + pr_cfg_small_rx.max_entries_rq1 = 1;
> + pr_cfg_small_rx.max_entries_rq2 = 1;
> + pr_cfg_small_rx.max_entries_rq3 = 1;
> +
> + for (i = 0; i < def_qps; i++) {
> + ret = ehea_init_port_res(port, &port->port_res[i], &pr_cfg, i);
> + if (ret)
> + goto port_res_setup_failed;
> + }
> + for (i = def_qps; i < def_qps + add_tx_qps; i++) {
> + ret = ehea_init_port_res(port, &port->port_res[i],
> + &pr_cfg_small_rx, i);
> + if (ret)
> + goto port_res_setup_failed;
> + }
> + return 0;
> +
> +port_res_setup_failed:
> + for(k = 0; k < i; k++) {
> + ehea_clean_port_res(port, &port->port_res[k]);
Useless k ?
> + }
> +port_res_setup_failed2:
> + ehea_destroy_eq(port->qp_eq);
> + return ret;
> +}
> +
> +static int ehea_clean_all_port_res(struct ehea_port *port)
> +{
> + int ret = 0;
> + int i;
> +
> + for(i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
> + ret |= ehea_clean_port_res(port, &port->port_res[i]);
> +
> + ret |= ehea_destroy_eq(port->qp_eq);
> +
> + return ret;
> +}
> +
> +int ehea_up(struct net_device *dev)
static
> +{
> + int ret, i;
> + struct ehea_port *port = netdev_priv(dev);
> + u64 mac_addr = 0;
> +
> + if (port->state == EHEA_PORT_UP)
> + return 0;
> +
> + ret = ehea_port_res_setup(port, port->num_def_qps,
> + port->num_add_tx_qps);
> + if (ret) {
> + ehea_error("port_res_failed");
> + goto port_res_setup_failed;
> + }
> +
> + /* Set default QP for this port */
> + ret = ehea_configure_port(port);
> + if (ret) {
> + ehea_error("ehea_configure_port failed. ret:%d", ret);
> + goto reg_failed;
> + }
> +
> + ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
> + if (ret) {
> + ret = -EIO;
> + ehea_error("reg_failed");
> + goto reg_failed;
> + }
> + mac_addr = (*(u64*)dev->dev_addr) >> 16;
> +
> + ret = ehea_reg_interrupts(dev);
> + if (ret) {
> + ehea_error("irq_reg_failed");
> + goto irq_reg_failed;
> + }
> +
> + for(i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
> + ret = ehea_activate_qp(port->adapter, port->port_res[i].qp);
> + if (ret) {
> + ehea_error("activate_qp failed");
> + goto activate_qp_failed;
> + }
> + }
> +
> + for(i = 0; i < port->num_def_qps; i++) {
> + ret = ehea_fill_port_res(&port->port_res[i]);
> + if (ret) {
> + ehea_error("fill_port_res_failed");
> + goto fill_port_res_failed;
> + }
> + }
> +
> + ret = 0;
> + port->state = EHEA_PORT_UP;
> + goto done;
> +
> +fill_port_res_failed:
> +activate_qp_failed:
> + ehea_free_interrupts(dev);
> +irq_reg_failed:
> + ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
> +reg_failed:
> + ehea_clean_all_port_res(port);
> +port_res_setup_failed:
> +done:
> + return ret;
> +}
> +
> +int ehea_open(struct net_device *dev)
static
> +{
> + int ret;
> + struct ehea_port *port = netdev_priv(dev);
> +
> + down(&port->port_lock);
> +
> + if (netif_msg_ifup(port))
> + ehea_info("enabling port %s", dev->name);
> + ret = ehea_up(dev);
Broken indent.
> + if (ret == 0)
> + netif_start_queue(dev);
> + up(&port->port_lock);
> + return ret;
> +}
> +
> +static int ehea_down(struct net_device *dev)
> +{
> + int ret, i;
> + struct ehea_port *port = netdev_priv(dev);
> +
> + if (port->state == EHEA_PORT_DOWN)
> + return 0;
> + ehea_drop_multicast_list(dev);
> + ehea_free_interrupts(dev);
> +
> + for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
> + tasklet_kill(&port->port_res[i].send_comp_task);
> +
> + ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
> + ret = ehea_clean_all_port_res(port);
> + port->state = EHEA_PORT_DOWN;
> + return ret;
> +}
> +
> +static int ehea_stop(struct net_device *dev)
> +{
> + int ret;
> + struct ehea_port *port = netdev_priv(dev);
> +
> + if (netif_msg_ifdown(port))
> + ehea_info("disabling port %s", dev->name);
> + flush_workqueue(port->adapter->ehea_wq);
> + down(&port->port_lock);
> + netif_stop_queue(dev);
> + ret = ehea_down(dev);
> + up(&port->port_lock);
> + return ret;
> +}
> +
> +static void ehea_reset_port(unsigned long data)
> +{
> + int ret;
> + struct net_device *dev = (struct net_device *)data;
> + struct ehea_port *port = netdev_priv(dev);
> +
> + port->resets++;
> + down(&port->port_lock);
> + netif_stop_queue(dev);
> + netif_poll_disable(dev);
> + ret = ehea_down(dev);
> + if (ret)
> + ehea_error("ehea_down failed. not all resources are freed");
> +
> + ret = ehea_up(dev);
> + if (ret) {
> + ehea_error("Reset device %s failed: ret=%d", dev->name, ret);
> + goto done;
> + }
> + if (netif_msg_timer(port))
> + ehea_info("Device %s resetted successfully", dev->name);
> + netif_poll_enable(dev);
> + netif_wake_queue(dev);
> +done:
> + up(&port->port_lock);
> + return;
> +}
> +
> +static void ehea_tx_watchdog(struct net_device *dev)
> +{
> + struct ehea_port *port = netdev_priv(dev);
> +
> + if (netif_carrier_ok(dev))
> + queue_work(port->adapter->ehea_wq, &port->reset_task);
> +}
> +
> +int ehea_sense_adapter_attr(struct ehea_adapter *adapter)
> +{
> + struct hcp_query_ehea *cb;
> + u64 hret;
> + int ret;
> +
> + cb = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb) {
> + ret = -ENOMEM;
> + goto kzalloc_failed;
> + }
> +
> + hret = ehea_h_query_ehea(adapter->handle, cb);
> +
> + if (hret != H_SUCCESS) {
> + ret = -EIO;
> + goto query_ehea_failed;
> + }
> +
> + adapter->num_ports = cb->num_ports;
> + adapter->max_mc_mac = cb->max_mc_mac - 1;
> + ret = 0;
> +
> +query_ehea_failed:
> + kfree(cb);
> +
> +kzalloc_failed:
> + return ret;
> +}
> +
> +static int ehea_setup_single_port(struct ehea_port *port,
> + struct device_node *dn)
> +{
> + int ret;
> + u64 hret;
> + struct net_device *dev = port->netdev;
> + struct ehea_adapter *adapter = port->adapter;
> + struct hcp_ehea_port_cb4 *cb4;
> + u32 *dn_log_port_id;
> +
> + sema_init(&port->port_lock, 1);
> + port->state = EHEA_PORT_DOWN;
> + port->sig_comp_iv = sq_entries / 10;
> +
> + if (!dn) {
> + ehea_error("bad device node: dn=%p", dn);
> + ret = -EINVAL;
> + goto done;
> + }
> +
> + port->of_dev_node = dn;
> +
> + /* Determine logical port id */
> + dn_log_port_id = (u32*)get_property(dn, "ibm,hea-port-no", NULL);
> +
> + if (!dn_log_port_id) {
> + ehea_error("bad device node: dn_log_port_id=%p",
> + dn_log_port_id);
> + ret = -EINVAL;
> + goto done;
> + }
> + port->logical_port_id = *dn_log_port_id;
> +
> + port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL);
> + if (!port->mc_list) {
> + ret = -ENOMEM;
> + goto done;
> + }
> +
> + INIT_LIST_HEAD(&port->mc_list->list);
> +
> + ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
> +
> + ret = ehea_sense_port_attr(port);
> + if (ret)
> + goto done;
> +
> + /* Enable Jumbo frames */
> + cb4 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
> + if (!cb4) {
> + ehea_error("no mem for cb4");
> + } else {
> + cb4->jumbo_frame = 1;
> + hret = ehea_h_modify_ehea_port(adapter->handle,
> + port->logical_port_id,
> + H_PORT_CB4, H_PORT_CB4_JUMBO,
> + cb4);
> + if (hret != H_SUCCESS) {
> + ehea_info("Jumbo frames not activated");
> + }
> + kfree(cb4);
> + }
> +
> + /* initialize net_device structure */
> + SET_MODULE_OWNER(dev);
> +
> + memcpy(dev->dev_addr, &port->mac_addr, ETH_ALEN);
> +
> + dev->open = ehea_open;
> + dev->poll = ehea_poll;
> + dev->weight = 64;
> + dev->stop = ehea_stop;
> + dev->hard_start_xmit = ehea_start_xmit;
> + dev->get_stats = ehea_get_stats;
> + dev->set_multicast_list = ehea_set_multicast_list;
> + dev->set_mac_address = ehea_set_mac_addr;
> + dev->change_mtu = ehea_change_mtu;
> + dev->vlan_rx_register = ehea_vlan_rx_register;
> + dev->vlan_rx_add_vid = ehea_vlan_rx_add_vid;
> + dev->vlan_rx_kill_vid = ehea_vlan_rx_kill_vid;
> + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO
> + | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_TX
> + | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER
> + | NETIF_F_LLTX;
> + dev->tx_timeout = &ehea_tx_watchdog;
> + dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT;
> +
> + INIT_WORK(&port->reset_task,
> + (void (*)(void *)) ehea_reset_port, dev);
Why not modify ehea_reset_port ?
> +
> + ehea_set_ethtool_ops(dev);
This function does not appear in the current patch.
> +
> + ret = register_netdev(dev);
> + if (ret) {
> + ehea_error("register_netdev failed. ret=%d", ret);
> + goto reg_netdev_failed;
> + }
> +
> + port->netdev = dev;
> + ret = 0;
> + goto done;
> +
> +reg_netdev_failed:
> + kfree(port->mc_list);
> +done:
> + return ret;
> +}
> +
> +static int ehea_setup_ports(struct ehea_adapter *adapter)
> +{
> + int ret;
> + int port_setup_ok = 0;
> + struct ehea_port *port;
> + struct device_node *dn = NULL;
> + struct net_device *dev;
> + int i;
> +
> + /* get port properties for all ports */
> + for (i = 0; i < adapter->num_ports; i++) {
> +
> + if (adapter->port[i])
> + continue; /* port already up and running */
> +
> + /* allocate memory for the port structures */
> + dev = alloc_etherdev(sizeof(struct ehea_port));
> +
> + if (!dev) {
> + ehea_error("no mem for net_device");
> + break;
> + }
> +
> + port = netdev_priv(dev);
> + port->adapter = adapter;
> + port->netdev = dev;
> + adapter->port[i] = port;
> + port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT);
> +
> + dn = of_find_node_by_name(dn, "ethernet");
> + ret = ehea_setup_single_port(port, dn);
> + if (ret) {
> + /* Free mem for this port struct. The others will be
> + processed on rollback */
> + free_netdev(dev);
> + adapter->port[i] = NULL;
> + ehea_error("eHEA port %d setup failed, ret=%d", i, ret);
> + }
> + }
> +
> + of_node_put(dn);
> +
> + /* Check for succesfully set up ports */
> + for (i = 0; i < adapter->num_ports; i++)
> + if (adapter->port[i])
> + port_setup_ok++;
> +
> + if (port_setup_ok)
> + ret = 0; /* At least some ports are setup correctly */
> + else
> + ret = -EINVAL;
> +
> + return ret;
> +}
> +
> +static int __devinit ehea_probe(struct ibmebus_dev *dev,
> + const struct of_device_id *id)
> +{
> + struct ehea_adapter *adapter;
> + u64 *adapter_handle;
> + int ret;
> +
> + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
> + if (!adapter) {
> + ret = -ENOMEM;
> + dev_err(&dev->ofdev.dev, "no mem for ehea_adapter\n");
> + goto kzalloc_adapter_failed;
> + }
> +
> + adapter_handle = (u64*)get_property(dev->ofdev.node, "ibm,hea-handle",
> + NULL);
> + if (!adapter_handle) {
> + dev_err(&dev->ofdev.dev, "failed getting handle for adapter"
> + " '%s'\n", dev->ofdev.node->full_name);
> + ret = -ENODEV;
> + goto get_property_failed;
> + }
> +
> + adapter->handle = *adapter_handle;
> + adapter->pd = EHEA_PD_ID;
> +
> + dev->ofdev.dev.driver_data = adapter;
> +
> + ret = ehea_reg_mr_adapter(adapter);
> + if (ret) {
> + dev_err(&dev->ofdev.dev, "reg_mr_adapter failed\n");
> + goto register_mr_failed;
> + }
> +
> + /* initialize adapter and ports */
> + /* get adapter properties */
> + ret = ehea_sense_adapter_attr(adapter);
> + if (ret) {
> + dev_err(&dev->ofdev.dev, "sense_adapter_attr failed: %d", ret);
> + goto sense_adapter_failed;
> + }
> + dev_info(&dev->ofdev.dev, "%d eHEA ports found\n", adapter->num_ports);
> +
> + adapter->neq = ehea_create_eq(adapter,
> + EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1);
> + if (!adapter->neq) {
> + dev_err(&dev->ofdev.dev, "NEQ creation failed");
> + goto create_neq_failed;
> + }
> +
> + tasklet_init(&adapter->neq_tasklet, ehea_neq_tasklet,
> + (unsigned long)adapter);
> +
> + ret = ibmebus_request_irq(NULL, adapter->neq->attr.ist1,
> + ehea_interrupt_neq, SA_INTERRUPT, "ehea_neq",
> + (void*)adapter);
> + if (ret) {
> + dev_err(&dev->ofdev.dev, "requesting NEQ IRQ failed");
> + goto request_irq_failed;
> + }
> +
> + adapter->ehea_wq = create_workqueue("ehea_wq");
> + if (!adapter->ehea_wq)
> + goto create_wq_failed;
> +
> + ret = ehea_setup_ports(adapter);
> + if (ret) {
> + dev_err(&dev->ofdev.dev, "setup_ports failed");
> + goto setup_ports_failed;
> + }
> +
> + ret = 0;
> + goto done;
> +
> +setup_ports_failed:
> + destroy_workqueue(adapter->ehea_wq);
> +
> +create_wq_failed:
> + ibmebus_free_irq(NULL, adapter->neq->attr.ist1, (void*)adapter);
> +request_irq_failed:
> + ehea_destroy_eq(adapter->neq);
> +
> +create_neq_failed:
> +sense_adapter_failed:
> + ehea_h_free_resource(adapter->handle, adapter->mr.handle);
> +
> +register_mr_failed:
> +get_property_failed:
> + kfree(adapter);
> +
> +kzalloc_adapter_failed:
> +done:
> + return ret;
The labelling results are not pretty.
> +}
> +
> +static void ehea_shutdown_single_port(struct ehea_port *port)
> +{
> + unregister_netdev(port->netdev);
> + kfree(port->mc_list);
> + free_netdev(port->netdev);
> +}
> +
> +static int __devexit ehea_remove(struct ibmebus_dev *dev)
> +{
> + struct ehea_adapter *adapter = dev->ofdev.dev.driver_data;
> + u64 hret;
> + int i;
> +
> + for (i = 0; i < adapter->num_ports; i++)
> + if (adapter->port[i]) {
> + ehea_shutdown_single_port(adapter->port[i]);
> + adapter->port[i] = NULL;
> + }
> +
> + ibmebus_free_irq(NULL, adapter->neq->attr.ist1, adapter);
> +
> + ehea_destroy_eq(adapter->neq);
> +
> + hret = ehea_h_free_resource(adapter->handle, adapter->mr.handle);
> + if (hret) {
> + dev_err(&dev->ofdev.dev, "free_resource_mr failed");
> + return -EIO;
> + }
> + kfree(adapter);
> + return 0;
> +}
> +
> +int check_module_parm(void)
static
> +{
> + int ret = 0;
> +
> + if ((rq1_entries < EHEA_MIN_ENTRIES_QP)
> + || (rq1_entries > EHEA_MAX_ENTRIES_RQ1)) {
|| is misplaced.
> + ehea_info("Bad parameter: rq1_entries");
> + ret = -EINVAL;
> + }
> + if ((rq2_entries < EHEA_MIN_ENTRIES_QP)
> + || (rq2_entries > EHEA_MAX_ENTRIES_RQ2)) {
> + ehea_info("Bad parameter: rq2_entries");
> + ret = -EINVAL;
> + }
> + if ((rq3_entries < EHEA_MIN_ENTRIES_QP)
> + || (rq3_entries > EHEA_MAX_ENTRIES_RQ3)) {
> + ehea_info("Bad parameter: rq3_entries");
> + ret = -EINVAL;
> + }
> + if ((sq_entries < EHEA_MIN_ENTRIES_QP)
> + || (sq_entries > EHEA_MAX_ENTRIES_SQ)) {
> + ehea_info("Bad parameter: sq_entries");
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static struct of_device_id ehea_device_table[] = {
> + {
> + .name = "lhea",
> + .compatible = "IBM,lhea",
> + },
Indent seems strange.
> + {},
> +};
> +
> +static struct ibmebus_driver ehea_driver = {
> + .name = "ehea",
> + .id_table = ehea_device_table,
> + .probe = ehea_probe,
> + .remove = ehea_remove,
> +};
> +
> +int __init ehea_module_init(void)
> +{
> + int ret;
> +
> + printk("IBM eHEA ethernet device driver (Release %s)\n", DRV_VERSION);
Missing KERN_XYZ
--
Ueimor
^ permalink raw reply
* Re: [2.6.19 PATCH 1/7] ehea: interface to network stack
From: Arnd Bergmann @ 2006-09-04 21:11 UTC (permalink / raw)
To: Francois Romieu
Cc: Thomas Klein, Jeff Garzik, Jan-Bernd Themann, netdev,
linux-kernel, Christoph Raisch, linux-ppc, Marcus Eder
In-Reply-To: <20060904201606.GA24386@electric-eye.fr.zoreil.com>
Am Monday 04 September 2006 22:16 schrieb Francois Romieu:
> > +#include "ehea.h"
> > +#include "ehea_qmr.h"
> > +#include "ehea_phyp.h"
>
> Afaik none of those is included in this patch nor in my 2.6.18-git tree.
They are in 5, 3 and 2, respectively
> Happy bissect in sight.
The driver should get merged as a single commit anyway, even
if split diffs are posted for review. Even if it gets merged
like this, bisect will work since the Kconfig option is added
in the final patch.
> > +
> > +static struct net_device_stats *ehea_get_stats(struct net_device *dev)
> > +{
> > +=A0=A0=A0=A0=A0struct ehea_port *port =3D netdev_priv(dev);
> > +=A0=A0=A0=A0=A0struct net_device_stats *stats =3D &port->stats;
> > +=A0=A0=A0=A0=A0struct hcp_ehea_port_cb2 *cb2;
> > +=A0=A0=A0=A0=A0u64 hret, rx_packets;
> > +=A0=A0=A0=A0=A0int i;
>
> unsigned int ?
does it matter? int as a counter is pretty standard.
> > +
> > +=A0=A0=A0=A0=A0if (netif_msg_hw(port))
> > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0ehea_dump(cb2, sizeof(*cb2), "n=
et_device_stats");
> > +
> > +=A0=A0=A0=A0=A0rx_packets =3D 0;
>
> Could be initialized when it is declared.
>
> > +=A0=A0=A0=A0=A0for (i =3D 0; i < port->num_def_qps; i++)
> > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0rx_packets +=3D port->port_res[=
i].rx_packets;
In one of the previous reviews, we told them to do it this way
instead. Initializing at declaration is error-prone.
> > +
> > +=A0=A0=A0=A0=A0intreq =3D ((pr->p_state.ehea_poll & 0xF) =3D=3D 0xF);
>
> Arguable parenthesis.
>
I'd argue to keep them ;-)
> > +
> > +=A0=A0=A0=A0=A0hret =3D ehea_h_modify_ehea_port(port->adapter->handle,
> > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=
=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0 port->logical_port_id,
> > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=
=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0 H_PORT_CB0, mask, cb0);
> > +=A0=A0=A0=A0=A0if (hret !=3D H_SUCCESS) {
> > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0ret =3D -EIO;
>
> Why can't ehea_xyz return -EIO/0 directly ?
>
the lowest-level hypercall should return H_* by convention.
Then again, it should also be called plpar_modify_ehea_port()
in that case.
> > +static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
> > +{
> > +=A0=A0=A0=A0=A0struct ehea_port *port =3D netdev_priv(dev);
> > +=A0=A0=A0=A0=A0struct ehea_port_res *pr;
> > +=A0=A0=A0=A0=A0struct ehea_swqe *swqe;
> > +=A0=A0=A0=A0=A0unsigned long flags;
> > +=A0=A0=A0=A0=A0u32 lkey;
> > +=A0=A0=A0=A0=A0int swqe_index;
> > +
> > +=A0=A0=A0=A0=A0pr =3D &port->port_res[0];
>
> Initialization and declaration can happen at the same time.
it's a gray area. In general, I recommend not to combine them
at all. Initialization to NULL or 0 is always bad, this one
is harmless, but I'd still leave it this way, especially after
telling them to clean this up earlier ;-)
Arnd <><
^ permalink raw reply
* Re: booting Linux in Linux using kexec tools
From: Guennadi Liakhovetski @ 2006-09-04 21:39 UTC (permalink / raw)
To: Reddy Suneel-ASR125; +Cc: linuxppc-embedded
In-Reply-To: <405ECA8A30557F439A723E52D50838F9D829C6@ZMY16EXM66.ds.mot.com>
Hi Suneel
On Wed, 30 Aug 2006, Reddy Suneel-ASR125 wrote:
> I am trying to boot Linux (zImage.elf) in Linux using kexec tools.
>
> I am getting the folling crash
>
> sh-2.05b#
> sh-2.05b# kexec -e
> Shutting down gianfar ethernet
> Shutting down gianfar ethernet
> Shutting down gianfar ethernet
> Starting new kernel
> Bye!
> Oops: kernel access of bad area, sig: 11 [#1]
Have you got it working? Where do your kexec tools come from? Are they
based on a version from http://www.xmission.com/~ebiederm/files/kexec/
and, if yes, how did yo adjust it to your system?
Thanks
Guennadi
---
Guennadi Liakhovetski
^ permalink raw reply
* Re: [2.6.19 PATCH 1/7] ehea: interface to network stack
From: Francois Romieu @ 2006-09-04 21:49 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Thomas Klein, Jeff Garzik, Jan-Bernd Themann, netdev,
linux-kernel, Christoph Raisch, linux-ppc, Marcus Eder
In-Reply-To: <200609042311.21202.arnd@arndb.de>
Arnd Bergmann <arnd@arndb.de> :
[...]
> The driver should get merged as a single commit anyway, even
> if split diffs are posted for review. Even if it gets merged
> like this, bisect will work since the Kconfig option is added
> in the final patch.
I have seen/done worse but it's not exactly pretty.
[...]
> > > +?????int i;
> >
> > unsigned int ?
>
> does it matter? int as a counter is pretty standard.
ppc64 takes unsigned int as a little optimization. I do not know how
the target platform behaves here.
--
Ueimor
^ permalink raw reply
* Re: [PATCH] Unwire set/get_robust_list
From: David Woodhouse @ 2006-09-04 22:32 UTC (permalink / raw)
To: Andreas Schwab; +Cc: linuxppc-dev
In-Reply-To: <jeodtvfn4x.fsf@sykes.suse.de>
On Mon, 2006-09-04 at 21:27 +0200, Andreas Schwab wrote:
> The syscalls set/get_robust_list must not be wired up until
> futex_atomic_cmpxchg_inatomic is implemented. Otherwise the kernel will
> hang in handle_futex_death.
Something like this?
Untested-but-otherwise-Signed-off-by: David Woodhouse <dwmw2@infradead.org>
diff --git a/include/asm-powerpc/futex.h b/include/asm-powerpc/futex.h
index f1b3c00..e8ff12c 100644
--- a/include/asm-powerpc/futex.h
+++ b/include/asm-powerpc/futex.h
@@ -84,7 +84,30 @@ static inline int futex_atomic_op_inuser
static inline int
futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval)
{
- return -ENOSYS;
+ int prev;
+
+ __asm__ __volatile__ (
+ LWSYNC_ON_SMP
+"1: lwarx %0,0,%2 # futex_atomic_cmpxchg_inatomic\n\
+ cmpw 0,%0,%3\n\
+ bne- 3f\n"
+ PPC405_ERR77(0,%2)
+"2: stwcx. %4,0,%2\n\
+ bne- 1b\n"
+ ISYNC_ON_SMP
+"3: .section .fixup,\"ax\"\n\
+4: li %0,%5\n\
+ b 3b\n\
+ .previous\n\
+ .section __ex_table,\"a\"\n\
+ .align 3\n\
+ " PPC_LONG "1b,4b,2b,4b\n\
+ .previous" \
+ : "=&r" (prev), "+m" (*uaddr)
+ : "r" (uaddr), "r" (oldval), "r" (newval), "i" (-EFAULT)
+ : "cc", "memory");
+
+ return prev;
}
#endif /* __KERNEL__ */
--
dwmw2
^ permalink raw reply related
* MAC driver issues
From: David H. Lynch Jr. @ 2006-09-04 22:31 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <Pine.LNX.4.60.0609042336020.5921@poirot.grange>
I am trying to get a network driver working.
It sends - I can capture the output with Etherreal and it looks good.
It receives - I can dump the received packets when the come in and they
look ok to me.
BUT,
If I try to ping another host, first Linux sends and ARP broadcast,
the appropriate client sends an ARP response.
Etherreal is happy with both.
My driver gets the response - The driver says the packet lenght is
64 bytes, which includes something like 14 bytes of padding at the end.
But the actual packet looks perfect exactly like what etherreal says
it should (and what I get when I capture a received ARP packet in
another driver).
At the end of the recive interrupt the skb is passed to Linux with
the netif_rc(ndev) function. This returns SUCCESS.
However, the paket never gets handed of to the ARP protocol - I put
some debugging in there and I never see it, while if I switch to a
different NIC
driver nearly the identical ARP packet gets processed by arp inside
Linux.
I have tried to chase this down, but I can't follow what is going
on inside of all the /net/core/dev.c etc.
Has anyone seen something similar to this ?
Does anyone have a clue where I can find some info on trying to
follow something through netif_rx to see where things are going off the
rails ?
--
Dave Lynch DLA Systems
Software Development: Embedded Linux
717.627.3770 dhlii@dlasys.net http://www.dlasys.net
fax: 1.253.369.9244 Cell: 1.717.587.7774
Over 25 years' experience in platforms, languages, and technologies too numerous to list.
"Any intelligent fool can make things bigger and more complex... It takes a touch of genius - and a lot of courage to move in the opposite direction."
Albert Einstein
^ permalink raw reply
* Re: [PATCH] Unwire set/get_robust_list
From: Andreas Schwab @ 2006-09-04 23:29 UTC (permalink / raw)
To: David Woodhouse; +Cc: linuxppc-dev
In-Reply-To: <1157409148.2473.209.camel@shinybook.infradead.org>
David Woodhouse <dwmw2@infradead.org> writes:
> On Mon, 2006-09-04 at 21:27 +0200, Andreas Schwab wrote:
>> The syscalls set/get_robust_list must not be wired up until
>> futex_atomic_cmpxchg_inatomic is implemented. Otherwise the kernel will
>> hang in handle_futex_death.
>
> Something like this?
>
> Untested-but-otherwise-Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Thanks, sucessfully tested on 32bit (with the nptl testsuite of glibc).
Will do some 64bit testing tomorrow.
Andreas.
--
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply
* Re: [PATCH] Unwire set/get_robust_list
From: Paul Mackerras @ 2006-09-04 23:58 UTC (permalink / raw)
To: David Woodhouse; +Cc: linuxppc-dev
In-Reply-To: <1157409148.2473.209.camel@shinybook.infradead.org>
David Woodhouse writes:
> Something like this?
Don't we need an access_ok() test in there? Otherwise, looks fine to
me.
Thanks,
Paul.
^ permalink raw reply
* [POWERPC] merge iSeries i/o operations with the rest
From: Stephen Rothwell @ 2006-09-05 2:08 UTC (permalink / raw)
To: paulus; +Cc: ppc-dev
The low level i/o operations are now handled in iSeires by taking a
trap when we access the (inaccessible) io memory region and calling
the Hypervisor from the trap code. To accomplish this the inline
assembler code for mos of the operations is simplified to ease decoding
and emulation.
The memset_io/memcpy_(to,from)io routines just do a firmware feature
check as it will compile out in the non combined build case and in the
combined build, the extra check is a relatively small overhead.
The remainder of the routines are not used in the drivers for any
currently supported hardware on legacy iSeries, so juts use the eeh
versions (they used to BUG or were not even implemented).
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
arch/powerpc/mm/fault.c | 8 ++
arch/powerpc/platforms/iseries/pci.c | 140 +++++++++++++++++++-----------
include/asm-powerpc/io.h | 109 +++++++++++------------
include/asm-powerpc/iseries/iseries_io.h | 27 ------
include/asm-powerpc/ppc-pci.h | 11 ++
5 files changed, 159 insertions(+), 136 deletions(-)
This has been built for iSeries and pSeries and booted on iSeries. It
has a measurable performance impact on iSeries for the only real device I
have (a PCnet32 ethernet card - iperf throughput is reduced by about
4-5%). I have not booted this on pSeries much less tried to measure any
performance difference.
Comments welcome.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 78a0d59..e8add2b 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -38,6 +38,8 @@ #include <asm/uaccess.h>
#include <asm/tlbflush.h>
#include <asm/kdebug.h>
#include <asm/siginfo.h>
+#include <asm/firmware.h>
+#include <asm/ppc-pci.h>
#ifdef CONFIG_KPROBES
ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
@@ -185,8 +187,12 @@ #endif /* CONFIG_4xx || CONFIG_BOOKE */
}
/* On a kernel SLB miss we can only check for a valid exception entry */
- if (!user_mode(regs) && (address >= TASK_SIZE))
+ if (!user_mode(regs) && (address >= TASK_SIZE)) {
+ if (firmware_has_feature(FW_FEATURE_ISERIES) &&
+ iseries_handle_io_fault(regs, address))
+ return 0;
return SIGSEGV;
+ }
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
if (error_code & DSISR_DABRMATCH) {
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c
index f4d427a..04b50d9 100644
--- a/arch/powerpc/platforms/iseries/pci.c
+++ b/arch/powerpc/platforms/iseries/pci.c
@@ -26,6 +26,7 @@ #include <linux/init.h>
#include <linux/module.h>
#include <linux/ide.h>
#include <linux/pci.h>
+#include <linux/ptrace.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -38,6 +39,7 @@ #include <asm/abs_addr.h>
#include <asm/iseries/hv_call_xm.h>
#include <asm/iseries/mf.h>
#include <asm/iseries/iommu.h>
+#include <asm/iseries/iseries_io.h>
#include <asm/ppc-pci.h>
@@ -270,46 +272,6 @@ void pcibios_fixup_resources(struct pci_
}
/*
- * I/0 Memory copy MUST use mmio commands on iSeries
- * To do; For performance, include the hv call directly
- */
-void iSeries_memset_io(volatile void __iomem *dest, char c, size_t Count)
-{
- u8 ByteValue = c;
- long NumberOfBytes = Count;
-
- while (NumberOfBytes > 0) {
- iSeries_Write_Byte(ByteValue, dest++);
- -- NumberOfBytes;
- }
-}
-EXPORT_SYMBOL(iSeries_memset_io);
-
-void iSeries_memcpy_toio(volatile void __iomem *dest, void *source, size_t count)
-{
- char *src = source;
- long NumberOfBytes = count;
-
- while (NumberOfBytes > 0) {
- iSeries_Write_Byte(*src++, dest++);
- -- NumberOfBytes;
- }
-}
-EXPORT_SYMBOL(iSeries_memcpy_toio);
-
-void iSeries_memcpy_fromio(void *dest, const volatile void __iomem *src, size_t count)
-{
- char *dst = dest;
- long NumberOfBytes = count;
-
- while (NumberOfBytes > 0) {
- *dst++ = iSeries_Read_Byte(src++);
- -- NumberOfBytes;
- }
-}
-EXPORT_SYMBOL(iSeries_memcpy_fromio);
-
-/*
* Look down the chain to find the matching Device Device
*/
static struct device_node *find_Device_Node(int bus, int devfn)
@@ -491,7 +453,7 @@ static inline struct device_node *xlate_
* iSeries_Read_Word = Read Word (16 bit)
* iSeries_Read_Long = Read Long (32 bit)
*/
-u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress)
+static u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress)
{
u64 BarOffset;
u64 dsa;
@@ -518,9 +480,8 @@ u8 iSeries_Read_Byte(const volatile void
return (u8)ret.value;
}
-EXPORT_SYMBOL(iSeries_Read_Byte);
-u16 iSeries_Read_Word(const volatile void __iomem *IoAddress)
+static u16 iSeries_Read_Word(const volatile void __iomem *IoAddress)
{
u64 BarOffset;
u64 dsa;
@@ -548,9 +509,8 @@ u16 iSeries_Read_Word(const volatile voi
return swab16((u16)ret.value);
}
-EXPORT_SYMBOL(iSeries_Read_Word);
-u32 iSeries_Read_Long(const volatile void __iomem *IoAddress)
+static u32 iSeries_Read_Long(const volatile void __iomem *IoAddress)
{
u64 BarOffset;
u64 dsa;
@@ -578,7 +538,6 @@ u32 iSeries_Read_Long(const volatile voi
return swab32((u32)ret.value);
}
-EXPORT_SYMBOL(iSeries_Read_Long);
/*
* Write MM I/O Instructions for the iSeries
@@ -587,7 +546,7 @@ EXPORT_SYMBOL(iSeries_Read_Long);
* iSeries_Write_Word = Write Word(16 bit)
* iSeries_Write_Long = Write Long(32 bit)
*/
-void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress)
+static void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress)
{
u64 BarOffset;
u64 dsa;
@@ -612,9 +571,8 @@ void iSeries_Write_Byte(u8 data, volatil
rc = HvCall4(HvCallPciBarStore8, dsa, BarOffset, data, 0);
} while (CheckReturnCode("WWB", DevNode, &retry, rc) != 0);
}
-EXPORT_SYMBOL(iSeries_Write_Byte);
-void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress)
+static void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress)
{
u64 BarOffset;
u64 dsa;
@@ -639,9 +597,8 @@ void iSeries_Write_Word(u16 data, volati
rc = HvCall4(HvCallPciBarStore16, dsa, BarOffset, swab16(data), 0);
} while (CheckReturnCode("WWW", DevNode, &retry, rc) != 0);
}
-EXPORT_SYMBOL(iSeries_Write_Word);
-void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress)
+static void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress)
{
u64 BarOffset;
u64 dsa;
@@ -666,4 +623,83 @@ void iSeries_Write_Long(u32 data, volati
rc = HvCall4(HvCallPciBarStore32, dsa, BarOffset, swab32(data), 0);
} while (CheckReturnCode("WWL", DevNode, &retry, rc) != 0);
}
-EXPORT_SYMBOL(iSeries_Write_Long);
+
+/*
+ * I/0 Memory copy MUST use mmio commands on iSeries
+ * To do; For performance, include the hv call directly
+ */
+void iSeries_memset_io(volatile void __iomem *dest, char c, size_t Count)
+{
+ u8 ByteValue = c;
+ long NumberOfBytes = Count;
+
+ while (NumberOfBytes > 0) {
+ iSeries_Write_Byte(ByteValue, dest++);
+ -- NumberOfBytes;
+ }
+}
+EXPORT_SYMBOL(iSeries_memset_io);
+
+void iSeries_memcpy_toio(volatile void __iomem *dest, const void *source,
+ size_t count)
+{
+ const char *src = source;
+ long NumberOfBytes = count;
+
+ while (NumberOfBytes > 0) {
+ iSeries_Write_Byte(*src++, dest++);
+ -- NumberOfBytes;
+ }
+}
+EXPORT_SYMBOL(iSeries_memcpy_toio);
+
+void iSeries_memcpy_fromio(void *dest, const volatile void __iomem *src, size_t count)
+{
+ char *dst = dest;
+ long NumberOfBytes = count;
+
+ while (NumberOfBytes > 0) {
+ *dst++ = iSeries_Read_Byte(src++);
+ -- NumberOfBytes;
+ }
+}
+EXPORT_SYMBOL(iSeries_memcpy_fromio);
+
+int iseries_handle_io_fault(struct pt_regs *regs, unsigned long address)
+{
+ u32 instr;
+
+ /* is the address in out i/o range? */
+ if ((address < BASE_IO_MEMORY) || (address >= max_io_memory))
+ return 0;
+ /* we are only called for kernel mode faults */
+ instr = *(u32 *)regs->nip;
+ /* Is the major opcode 31 and target/source r0? */
+ if ((instr & 0xffe00000) != 0x7C000000)
+ return 0;
+
+ switch ((instr >> 1) & 0x3ff) {
+ case 87:
+ regs->gpr[0] = iSeries_Read_Byte((void __iomem *)address);
+ break;
+ case 215:
+ iSeries_Write_Byte(regs->gpr[0], (void __iomem *)address);
+ break;
+ case 790:
+ regs->gpr[0] = iSeries_Read_Word((void __iomem *)address);
+ break;
+ case 918:
+ iSeries_Write_Word(regs->gpr[0], (void __iomem *)address);
+ break;
+ case 534:
+ regs->gpr[0] = iSeries_Read_Long((void __iomem *)address);
+ break;
+ case 662:
+ iSeries_Write_Long(regs->gpr[0], (void __iomem *)address);
+ break;
+ default:
+ return 0;
+ }
+ regs->nip += 4;
+ return 1;
+}
diff --git a/include/asm-powerpc/io.h b/include/asm-powerpc/io.h
index 36c4c34..5420fac 100644
--- a/include/asm-powerpc/io.h
+++ b/include/asm-powerpc/io.h
@@ -19,11 +19,10 @@ #else
#include <linux/compiler.h>
#include <asm/page.h>
#include <asm/byteorder.h>
-#ifdef CONFIG_PPC_ISERIES
#include <asm/iseries/iseries_io.h>
-#endif
#include <asm/synch.h>
#include <asm/delay.h>
+#include <asm/firmware.h>
#include <asm-generic/iomap.h>
@@ -41,44 +40,10 @@ #define SLOW_DOWN_IO
extern unsigned long isa_io_base;
extern unsigned long pci_io_base;
-#ifdef CONFIG_PPC_ISERIES
-/* __raw_* accessors aren't supported on iSeries */
-#define __raw_readb(addr) { BUG(); 0; }
-#define __raw_readw(addr) { BUG(); 0; }
-#define __raw_readl(addr) { BUG(); 0; }
-#define __raw_readq(addr) { BUG(); 0; }
-#define __raw_writeb(v, addr) { BUG(); 0; }
-#define __raw_writew(v, addr) { BUG(); 0; }
-#define __raw_writel(v, addr) { BUG(); 0; }
-#define __raw_writeq(v, addr) { BUG(); 0; }
-#define readb(addr) iSeries_Read_Byte(addr)
-#define readw(addr) iSeries_Read_Word(addr)
-#define readl(addr) iSeries_Read_Long(addr)
-#define writeb(data, addr) iSeries_Write_Byte((data),(addr))
-#define writew(data, addr) iSeries_Write_Word((data),(addr))
-#define writel(data, addr) iSeries_Write_Long((data),(addr))
-#define memset_io(a,b,c) iSeries_memset_io((a),(b),(c))
-#define memcpy_fromio(a,b,c) iSeries_memcpy_fromio((a), (b), (c))
-#define memcpy_toio(a,b,c) iSeries_memcpy_toio((a), (b), (c))
-
-#define inb(addr) readb(((void __iomem *)(long)(addr)))
-#define inw(addr) readw(((void __iomem *)(long)(addr)))
-#define inl(addr) readl(((void __iomem *)(long)(addr)))
-#define outb(data,addr) writeb(data,((void __iomem *)(long)(addr)))
-#define outw(data,addr) writew(data,((void __iomem *)(long)(addr)))
-#define outl(data,addr) writel(data,((void __iomem *)(long)(addr)))
/*
- * The *_ns versions below don't do byte-swapping.
- * Neither do the standard versions now, these are just here
- * for older code.
+ * __raw_* accessors aren't supported on iSeries so
+ * these used to BUG() on iSeries, but that is not really necessary.
*/
-#define insb(port, buf, ns) _insb((u8 __iomem *)((port)+pci_io_base), (buf), (ns))
-#define insw(port, buf, ns) _insw_ns((u8 __iomem *)((port)+pci_io_base), (buf), (ns))
-#define insl(port, buf, nl) _insl_ns((u8 __iomem *)((port)+pci_io_base), (buf), (nl))
-#define insw_ns(port, buf, ns) _insw_ns((u16 __iomem *)((port)+pci_io_base), (buf), (ns))
-#define insl_ns(port, buf, nl) _insl_ns((u32 __iomem *)((port)+pci_io_base), (buf), (nl))
-#else
-
static inline unsigned char __raw_readb(const volatile void __iomem *addr)
{
return *(volatile unsigned char __force *)addr;
@@ -111,6 +76,7 @@ static inline void __raw_writeq(unsigned
{
*(volatile unsigned long __force *)addr = v;
}
+
#define readb(addr) eeh_readb(addr)
#define readw(addr) eeh_readw(addr)
#define readl(addr) eeh_readl(addr)
@@ -119,9 +85,6 @@ #define writeb(data, addr) eeh_writeb((d
#define writew(data, addr) eeh_writew((data), (addr))
#define writel(data, addr) eeh_writel((data), (addr))
#define writeq(data, addr) eeh_writeq((data), (addr))
-#define memset_io(a,b,c) eeh_memset_io((a),(b),(c))
-#define memcpy_fromio(a,b,c) eeh_memcpy_fromio((a),(b),(c))
-#define memcpy_toio(a,b,c) eeh_memcpy_toio((a),(b),(c))
#define inb(port) eeh_inb((unsigned long)port)
#define outb(val, port) eeh_outb(val, (unsigned long)port)
#define inw(port) eeh_inw((unsigned long)port)
@@ -140,8 +103,6 @@ #define insl(port, buf, nl) eeh_insl_ns(
#define insw_ns(port, buf, ns) eeh_insw_ns((port), (buf), (ns))
#define insl_ns(port, buf, nl) eeh_insl_ns((port), (buf), (nl))
-#endif
-
#define outsb(port, buf, ns) _outsb((u8 __iomem *)((port)+pci_io_base), (buf), (ns))
#define outsw(port, buf, ns) _outsw_ns((u16 __iomem *)((port)+pci_io_base), (buf), (ns))
#define outsl(port, buf, nl) _outsl_ns((u32 __iomem *)((port)+pci_io_base), (buf), (nl))
@@ -273,25 +234,30 @@ #define iobarrier_w() eieio()
* These routines do not perform EEH-related I/O address translation,
* and should not be used directly by device drivers. Use inb/readb
* instead.
+ *
+ * For some of these, we force the target/source register to be
+ * r0 to ease decoding on iSeries.
*/
static inline int in_8(const volatile unsigned char __iomem *addr)
{
- int ret;
+ register unsigned int ret __asm__("r0");
- __asm__ __volatile__("lbz%U1%X1 %0,%1; twi 0,%0,0; isync"
- : "=r" (ret) : "m" (*addr));
+ __asm__ __volatile__("lbzx %0,0,%1; twi 0,%0,0; isync"
+ : "=r" (ret) : "r" (addr));
return ret;
}
static inline void out_8(volatile unsigned char __iomem *addr, int val)
{
- __asm__ __volatile__("stb%U0%X0 %1,%0; sync"
- : "=m" (*addr) : "r" (val));
+ register unsigned int rval __asm__("r0") = val;
+
+ __asm__ __volatile__("stbx %0,0,%1; sync"
+ : : "r" (rval), "r" (addr));
}
static inline int in_le16(const volatile unsigned short __iomem *addr)
{
- int ret;
+ register unsigned int ret __asm__("r0");
__asm__ __volatile__("lhbrx %0,0,%1; twi 0,%0,0; isync"
: "=r" (ret) : "r" (addr), "m" (*addr));
@@ -309,8 +275,10 @@ static inline int in_be16(const volatile
static inline void out_le16(volatile unsigned short __iomem *addr, int val)
{
- __asm__ __volatile__("sthbrx %1,0,%2; sync"
- : "=m" (*addr) : "r" (val), "r" (addr));
+ register unsigned int rval __asm__("r0") = val;
+
+ __asm__ __volatile__("sthbrx %0,0,%1; sync"
+ : : "r" (rval), "r" (addr));
}
static inline void out_be16(volatile unsigned short __iomem *addr, int val)
@@ -321,7 +289,7 @@ static inline void out_be16(volatile uns
static inline unsigned in_le32(const volatile unsigned __iomem *addr)
{
- unsigned ret;
+ register unsigned int ret __asm__("r0");
__asm__ __volatile__("lwbrx %0,0,%1; twi 0,%0,0; isync"
: "=r" (ret) : "r" (addr), "m" (*addr));
@@ -339,8 +307,10 @@ static inline unsigned in_be32(const vol
static inline void out_le32(volatile unsigned __iomem *addr, int val)
{
- __asm__ __volatile__("stwbrx %1,0,%2; sync" : "=m" (*addr)
- : "r" (val), "r" (addr));
+ register unsigned int rval __asm__("r0") = val;
+
+ __asm__ __volatile__("stwbrx %1,0,%2; sync"
+ : "=m" (*addr) : "r" (rval), "r" (addr));
}
static inline void out_be32(volatile unsigned __iomem *addr, int val)
@@ -399,9 +369,34 @@ static inline void out_be64(volatile uns
__asm__ __volatile__("std%U0%X0 %1,%0; sync" : "=m" (*addr) : "r" (val));
}
-#ifndef CONFIG_PPC_ISERIES
#include <asm/eeh.h>
-#endif
+
+static inline void memset_io(volatile void __iomem *addr, int c,
+ unsigned long n)
+{
+ if (firmware_has_feature(FW_FEATURE_ISERIES))
+ iSeries_memset_io(addr, c, n);
+ else
+ eeh_memset_io(addr, c, n);
+}
+
+static inline void memcpy_fromio(void *dest, const volatile void __iomem *src,
+ unsigned long n)
+{
+ if (firmware_has_feature(FW_FEATURE_ISERIES))
+ iSeries_memcpy_fromio(dest, src, n);
+ else
+ eeh_memcpy_fromio(dest, src, n);
+}
+
+static inline void memcpy_toio(volatile void __iomem *dest, const void *src,
+ unsigned long n)
+{
+ if (firmware_has_feature(FW_FEATURE_ISERIES))
+ iSeries_memcpy_toio(dest, src, n);
+ else
+ eeh_memcpy_toio(dest, src, n);
+}
/**
* check_signature - find BIOS signatures
@@ -417,7 +412,6 @@ static inline int check_signature(const
const unsigned char *signature, int length)
{
int retval = 0;
-#ifndef CONFIG_PPC_ISERIES
do {
if (readb(io_addr) != *signature)
goto out;
@@ -427,7 +421,6 @@ #ifndef CONFIG_PPC_ISERIES
} while (length);
retval = 1;
out:
-#endif
return retval;
}
diff --git a/include/asm-powerpc/iseries/iseries_io.h b/include/asm-powerpc/iseries/iseries_io.h
index f29009b..1b61c51 100644
--- a/include/asm-powerpc/iseries/iseries_io.h
+++ b/include/asm-powerpc/iseries/iseries_io.h
@@ -1,9 +1,5 @@
#ifndef _ASM_POWERPC_ISERIES_ISERIES_IO_H
#define _ASM_POWERPC_ISERIES_ISERIES_IO_H
-
-
-#ifdef CONFIG_PPC_ISERIES
-#include <linux/types.h>
/*
* Created by Allan Trautman on Thu Dec 28 2000.
*
@@ -30,31 +26,12 @@ #include <linux/types.h>
* Created December 28, 2000
* End Change Activity
*/
-
-#ifdef CONFIG_PCI
-extern u8 iSeries_Read_Byte(const volatile void __iomem * IoAddress);
-extern u16 iSeries_Read_Word(const volatile void __iomem * IoAddress);
-extern u32 iSeries_Read_Long(const volatile void __iomem * IoAddress);
-extern void iSeries_Write_Byte(u8 IoData, volatile void __iomem * IoAddress);
-extern void iSeries_Write_Word(u16 IoData, volatile void __iomem * IoAddress);
-extern void iSeries_Write_Long(u32 IoData, volatile void __iomem * IoAddress);
+#include <linux/types.h>
extern void iSeries_memset_io(volatile void __iomem *dest, char x, size_t n);
-extern void iSeries_memcpy_toio(volatile void __iomem *dest, void *source,
+extern void iSeries_memcpy_toio(volatile void __iomem *dest, const void *source,
size_t n);
extern void iSeries_memcpy_fromio(void *dest,
const volatile void __iomem *source, size_t n);
-#else
-static inline u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress)
-{
- return 0xff;
-}
-
-static inline void iSeries_Write_Byte(u8 IoData,
- volatile void __iomem *IoAddress)
-{
-}
-#endif /* CONFIG_PCI */
-#endif /* CONFIG_PPC_ISERIES */
#endif /* _ASM_POWERPC_ISERIES_ISERIES_IO_H */
diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h
index cf79bc7..db153ab 100644
--- a/include/asm-powerpc/ppc-pci.h
+++ b/include/asm-powerpc/ppc-pci.h
@@ -51,6 +51,17 @@ extern void pSeries_irq_bus_setup(struct
extern unsigned long pci_probe_only;
+/* From platforms/iseries/pci.c */
+#if defined(CONFIG_PPC_ISERIES) && defined(CONFIG_PCI)
+extern int iseries_handle_io_fault(struct pt_regs *regs, unsigned long address);
+#else
+static inline int iseries_handle_io_fault(struct pt_regs *regs,
+ unsigned long address)
+{
+ return 0;
+}
+#endif
+
/* ---- EEH internal-use-only related routines ---- */
#ifdef CONFIG_EEH
--
1.4.1.1
^ permalink raw reply related
* Re: pci error recovery procedure
From: Zhang, Yanmin @ 2006-09-05 2:32 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: Yanmin Zhang, LKML, Rajesh Shah, linuxppc-dev, linux-pci maillist
In-Reply-To: <1157360592.22705.46.camel@localhost.localdomain>
On Mon, 2006-09-04 at 17:03, Benjamin Herrenschmidt wrote:
> > As you know, all functions of a device share the same bus number and 5 bit dev number.
> > They just have different 3 bit function number. We could deduce if functions are in the same
> > device (slot).
>
> Until you have a P2P bridge ...
> > Thanks. Now I understand why you specified mmio_enabled and slot_reset. They are just
> > to map to pSeries platform hardware operation steps. I know little about pSeries hardware,
> > but is it possible to merge such hardware steps from software point of view?
>
> One of the ideas we had when defining those steps is to be precise
> enough to let drivers who _can_ deal with those fine grained pSeries
> step implement them, but also have the fallback to slot reset whenever
> possible.
>
> Now, if in practice, after actually implementing this in a number of
> drivers, we see that slot reset is the only ever used path, then we
> might want to simplify things a bit. I didn't want to impose that
> restriction in the initial design though.
Thanks for your explanation. Now it's the time to delete mmio_enabled
and merge slot_reset with resume.
>
> It's my understanding that doing no slot reset (hardware reset) but just
> re-enabling MMIO, DMA and clearing pending error status in the PCI
> config space is, as far as the driver is concerned, almost functionally
> equivalent to a PCIe link reset. That is, the link reset might not (or
> will not) actually reset the hardware beyond the PCIe link layer.
I agree.
>
> Thus we could simplify the split between link reset / hard reset. The
> former is an attempt at recovery with only resetting the PCI path to the
> device, which on PCIe becomes a link reset, and on old PCI, just
> clearing of the various error bits along the path (and on pSeries,
> re-enabling MMIO and DMA access). However, there is still the problem
> that if you do that, on pSeries at least, you really want to 1- enable
> MMIO, 2- soft reset the card using MMIO, that is make sure all pending
> DMA is stopped, and 3- re-enable DMA. While if we collapse that into a
> single 'link reset' type of operation, we'll end up re-enabling MMIO and
> DMA before the driver has a chance to stop pending DMA's
Is it the exclusive reason to have multi-steps?
1) Here link reset and hard reset are hardware operations, not the
link_reset and slot_reset callback in pci_error_handlers.
2) Callback error_detected will notify drivers there is PCI errors. Drivers
shouldn't do any I/O in error_detected.
3) If both the link and slot are reset after all error_detected are called,
the device should go back to initial status and all DMA should be stopped
automatically. Why does the driver still need a chance to stop DMA? The
error_detected of the drivers in the latest kernel who support err handlers
always returns PCI_ERS_RESULT_NEED_RESET. They are typical examples.
> and thus
> increase the chance that we crap out due to a pending DMA on the chip.
>
> Ben.
Above discussion is only about if mmio_enabled is needed.
As for slot_reset, I think it could be merged with resume, because platforms
do nothing between calling slot_reset and resume. Any comment?
Yanmin
^ permalink raw reply
* Re: [PATCH] Unwire set/get_robust_list
From: David Woodhouse @ 2006-09-05 4:53 UTC (permalink / raw)
To: Paul Mackerras; +Cc: linuxppc-dev
In-Reply-To: <17660.48525.873588.173047@cargo.ozlabs.ibm.com>
On Tue, 2006-09-05 at 09:58 +1000, Paul Mackerras wrote:
> Don't we need an access_ok() test in there? Otherwise, looks fine to
> me.
Oops, good point. Like this then...
Do we actually need the memory clobber, btw? We're not using this for
locking in kernel space.
[PATCH] Implement PowerPC futex_atomic_cmpxchg_inatomic().
The sys_[gs]et_robust_list() syscalls were wired up on PowerPC but
didn't work correctly because futex_atomic_cmpxchg_inatomic() wasn't
implemented. Implement it, based on __cmpxchg_u32()
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
diff --git a/include/asm-powerpc/futex.h b/include/asm-powerpc/futex.h
index f1b3c00..936422e 100644
--- a/include/asm-powerpc/futex.h
+++ b/include/asm-powerpc/futex.h
@@ -84,7 +84,33 @@ static inline int futex_atomic_op_inuser
static inline int
futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval)
{
- return -ENOSYS;
+ int prev;
+
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
+ return -EFAULT;
+
+ __asm__ __volatile__ (
+ LWSYNC_ON_SMP
+"1: lwarx %0,0,%2 # futex_atomic_cmpxchg_inatomic\n\
+ cmpw 0,%0,%3\n\
+ bne- 3f\n"
+ PPC405_ERR77(0,%2)
+"2: stwcx. %4,0,%2\n\
+ bne- 1b\n"
+ ISYNC_ON_SMP
+"3: .section .fixup,\"ax\"\n\
+4: li %0,%5\n\
+ b 3b\n\
+ .previous\n\
+ .section __ex_table,\"a\"\n\
+ .align 3\n\
+ " PPC_LONG "1b,4b,2b,4b\n\
+ .previous" \
+ : "=&r" (prev), "+m" (*uaddr)
+ : "r" (uaddr), "r" (oldval), "r" (newval), "i" (-EFAULT)
+ : "cc", "memory");
+
+ return prev;
}
#endif /* __KERNEL__ */
--
dwmw2
^ permalink raw reply related
* Re: [POWERPC] merge iSeries i/o operations with the rest
From: David Woodhouse @ 2006-09-05 5:07 UTC (permalink / raw)
To: Stephen Rothwell; +Cc: ppc-dev, paulus
In-Reply-To: <20060905120817.e52857ee.sfr@canb.auug.org.au>
On Tue, 2006-09-05 at 12:08 +1000, Stephen Rothwell wrote:
> The low level i/o operations are now handled in iSeires by taking a
> trap when we access the (inaccessible) io memory region and calling
> the Hypervisor from the trap code.
There was some discussion of the possibility of using the 'alternatives'
mechanism to patch the I/O instructions at run-time when we find we've
booted on iSeries -- just turning each one into a jump to a trampoline
which does the appropriate hypervisor calls, thus avoiding the overhead
of the trap each time.
Was that approach not viable, or not noticeably less of a performance
hit on iSeries?
--
dwmw2
^ permalink raw reply
* Re: [POWERPC] merge iSeries i/o operations with the rest
From: Stephen Rothwell @ 2006-09-05 5:39 UTC (permalink / raw)
To: David Woodhouse; +Cc: ppc-dev, paulus
In-Reply-To: <1157432871.2413.9.camel@shinybook.infradead.org>
Hi David,
On Mon, 04 Sep 2006 22:07:50 -0700 David Woodhouse <dwmw2@infradead.org> wrote:
>
> On Tue, 2006-09-05 at 12:08 +1000, Stephen Rothwell wrote:
> > The low level i/o operations are now handled in iSeires by taking a
> > trap when we access the (inaccessible) io memory region and calling
> > the Hypervisor from the trap code.
>
> There was some discussion of the possibility of using the 'alternatives'
> mechanism to patch the I/O instructions at run-time when we find we've
> booted on iSeries -- just turning each one into a jump to a trampoline
> which does the appropriate hypervisor calls, thus avoiding the overhead
> of the trap each time.
>
> Was that approach not viable, or not noticeably less of a performance
> hit on iSeries?
It has not been tried, yet, as I need to hone my PPC assembler skills :-)
If the performance on iSeries is unacceptably worse (we need to get some
more testing done), then it would probably be worth while doing as you
suggested (or, equivalently, patching on first trap). This patch just
gets us along the way and may prove sufficient.
Note that the overhead of the trapping may not be as much as expected
since all i/o on iSeries has to be done with Hypervisor calls.
I have another version coming that modifies the inline assembler much
less, but may make the runtime patching approach slightly harder.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
^ permalink raw reply
* MPC5200B - FEC Error
From: Ram Prasad H L @ 2006-09-05 6:30 UTC (permalink / raw)
To: linuxppc-embedded
hi all,
this is Ramprasad, and we are using freescale's Lite5200B
using MPC5200B as core, for one of our applications.
The board is booted with u-boot-1.2.2 and linux kernel 2.4.25.
I'm finding a very peculiar problem with the board when I
run my application which extensively uses the FEC for receiving
video and audio streams from network. The application runs fine for
some time say 2-3 hrs and suddenly i'm getting the following error
and the OS starts rebooting.
The error log looks somewhat like this:
NIP: C0124ECC XER: 00000000 LR: C0125028 SP: C01B6270 REGS: c01b61c0 TRAP:
0300 Not tainted
MSR: 00001032 EE: 0 PR: 0 FP: 0 ME: 1 IR/DR: 11
DAR: 000000F0, DSISR: 20000000
TASK = c01b4470[0] 'swapper' Last syscall: 120
last math c228a000 last altivec 00000000
GPR00: 000000F0 C01B6270 C01B4470 00001032 00001032 00000006 C0C11034
C3C61CC0
GPR08: 0000003F 00000000 C01CE9F8 C01CEAD0 00000002 1012A714 03FBA000
00000000
GPR16: 00000001 00000001 FFFFFFFF 007FFF00 00001032 C3F93000 C0210000
00000000
GPR24: C3C06012 00000045 C01E0000 C0BE1320 0000054E C01CE9E0 C01CEA08
C3CA9A20
Call backtrace:
C0125528 C011AF0C C0006CD8 C0006D98 C0005C40 C00072C8 C00072E4
C0003918 C01CF5B8 000035F0
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
panic: Aiee, killing interrupt handler!
In interrupt handler - not syncing
<0>Rebooting in 180 seconds..<4>MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
MPC5xxx_FEC_IEVENT_RFIFO_ERROR
mpc5xxx_fec_reinit
Can anyone tell me what is the reason to face such
an error? and how can we avoid this error? any pointers
for overcoming it will be really helpfull as I'm stuck with
this problem since long time.
thanks and regards,
Ramprasad H L
^ permalink raw reply
* Re: MAC driver issues
From: Alex Zeffertt @ 2006-09-05 8:33 UTC (permalink / raw)
To: David H. Lynch Jr.; +Cc: linuxppc-embedded
In-Reply-To: <44FCA93E.1000009@dlasys.net>
Hi David,
I think in order to get any help from this list you need to be more specific
about how this problem relates to PowerPCs.
You also need to say exactly what "never gets handed off to the ARP protocol"
means.
FWIW, in my experience the hardware independent parts of the networking stack are
very stable and the problem is almost always with the drivers, or with the IP
configuration (e.g. two interfaces on the same subnet).
Alex
David H. Lynch Jr. wrote:
> I am trying to get a network driver working.
>
> It sends - I can capture the output with Etherreal and it looks good.
> It receives - I can dump the received packets when the come in and they
> look ok to me.
>
>
> BUT,
> If I try to ping another host, first Linux sends and ARP broadcast,
> the appropriate client sends an ARP response.
> Etherreal is happy with both.
> My driver gets the response - The driver says the packet lenght is
> 64 bytes, which includes something like 14 bytes of padding at the end.
> But the actual packet looks perfect exactly like what etherreal says
> it should (and what I get when I capture a received ARP packet in
> another driver).
> At the end of the recive interrupt the skb is passed to Linux with
> the netif_rc(ndev) function. This returns SUCCESS.
>
> However, the paket never gets handed of to the ARP protocol - I put
> some debugging in there and I never see it, while if I switch to a
> different NIC
> driver nearly the identical ARP packet gets processed by arp inside
> Linux.
>
> I have tried to chase this down, but I can't follow what is going
> on inside of all the /net/core/dev.c etc.
>
> Has anyone seen something similar to this ?
>
> Does anyone have a clue where I can find some info on trying to
> follow something through netif_rx to see where things are going off the
> rails ?
>
>
>
^ 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