kernel: remove linux 3.18 support
No targets are using it anymore Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
This commit is contained in:
parent
5742a2ba35
commit
e3ab280e47
|
@ -2,12 +2,10 @@
|
|||
|
||||
LINUX_RELEASE?=1
|
||||
|
||||
LINUX_VERSION-3.18 = .71
|
||||
LINUX_VERSION-4.4 = .121
|
||||
LINUX_VERSION-4.9 = .117
|
||||
LINUX_VERSION-4.14 = .60
|
||||
|
||||
LINUX_KERNEL_HASH-3.18.71 = 5abc9778ad44ce02ed6c8ab52ece8a21c6d20d21f6ed8a19287b4a38a50c1240
|
||||
LINUX_KERNEL_HASH-4.4.121 = 44a88268b5088dc326b30c9b9133ac35a9a200b636b7268d08f32abeae6ca729
|
||||
LINUX_KERNEL_HASH-4.9.117 = 8232ea326ac5784d71baa09c0cd7806a2674f8449c0be73e2ae341e6880279b0
|
||||
LINUX_KERNEL_HASH-4.14.60 = 6ce143258fba2e1f543ff56802e047d7d3a741f746b9c947dd088d4a2873b295
|
||||
|
|
|
@ -51,7 +51,7 @@ else
|
|||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(filter 3.18 4.4 4.9,$(KERNEL_PATCHVER)),)
|
||||
ifneq ($(filter 4.4 4.9,$(KERNEL_PATCHVER)),)
|
||||
DEFAULT_PACKAGES.router:=$(filter-out kmod-ipt-offload,$(DEFAULT_PACKAGES.router))
|
||||
endif
|
||||
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
CONFIG_ADM6996_PHY=y
|
||||
CONFIG_AR7=y
|
||||
CONFIG_AR7_TI=y
|
||||
# CONFIG_AR7_TYPE_AC49X is not set
|
||||
CONFIG_AR7_TYPE_TI=y
|
||||
CONFIG_AR7_WDT=y
|
||||
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
|
||||
CONFIG_ARCH_DISCARD_MEMBLOCK=y
|
||||
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
|
||||
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
|
||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
|
||||
CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
|
||||
CONFIG_ARCH_REQUIRE_GPIOLIB=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
|
||||
CONFIG_BOOT_ELF32=y
|
||||
CONFIG_CEVT_R4K=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_CMDLINE="rootfstype=squashfs,jffs2"
|
||||
CONFIG_CMDLINE_BOOL=y
|
||||
# CONFIG_CMDLINE_OVERRIDE is not set
|
||||
CONFIG_CPMAC=y
|
||||
CONFIG_CPU_GENERIC_DUMP_TLB=y
|
||||
CONFIG_CPU_HAS_PREFETCH=y
|
||||
CONFIG_CPU_HAS_SYNC=y
|
||||
CONFIG_CPU_LITTLE_ENDIAN=y
|
||||
CONFIG_CPU_MIPS32=y
|
||||
CONFIG_CPU_MIPS32_R1=y
|
||||
CONFIG_CPU_MIPSR1=y
|
||||
CONFIG_CPU_R4K_CACHE_TLB=y
|
||||
CONFIG_CPU_R4K_FPU=y
|
||||
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
|
||||
CONFIG_CPU_SUPPORTS_HIGHMEM=y
|
||||
CONFIG_CSRC_R4K=y
|
||||
CONFIG_DMA_NONCOHERENT=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
CONFIG_ETHERNET_PACKET_MANGLE=y
|
||||
CONFIG_FIXED_PHY=y
|
||||
CONFIG_GENERIC_ATOMIC64=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
|
||||
CONFIG_GENERIC_CMOS_UPDATE=y
|
||||
CONFIG_GENERIC_IO=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GPIOLIB=y
|
||||
CONFIG_GPIO_DEVRES=y
|
||||
CONFIG_HARDWARE_WATCHPOINTS=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT=y
|
||||
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
|
||||
CONFIG_HAVE_ARCH_JUMP_LABEL=y
|
||||
CONFIG_HAVE_ARCH_KGDB=y
|
||||
CONFIG_HAVE_ARCH_TRACEHOOK=y
|
||||
# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
|
||||
CONFIG_HAVE_CC_STACKPROTECTOR=y
|
||||
CONFIG_HAVE_CLK=y
|
||||
CONFIG_HAVE_CONTEXT_TRACKING=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_DEBUG_KMEMLEAK=y
|
||||
CONFIG_HAVE_DEBUG_STACKOVERFLOW=y
|
||||
CONFIG_HAVE_DMA_API_DEBUG=y
|
||||
CONFIG_HAVE_DMA_ATTRS=y
|
||||
CONFIG_HAVE_DYNAMIC_FTRACE=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
|
||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
||||
CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y
|
||||
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
||||
CONFIG_HAVE_IDE=y
|
||||
CONFIG_HAVE_KERNEL_BZIP2=y
|
||||
CONFIG_HAVE_KERNEL_GZIP=y
|
||||
CONFIG_HAVE_KERNEL_LZ4=y
|
||||
CONFIG_HAVE_KERNEL_LZMA=y
|
||||
CONFIG_HAVE_KERNEL_LZO=y
|
||||
CONFIG_HAVE_KERNEL_XZ=y
|
||||
CONFIG_HAVE_MEMBLOCK=y
|
||||
CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
|
||||
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
|
||||
CONFIG_HAVE_NET_DSA=y
|
||||
CONFIG_HAVE_OPROFILE=y
|
||||
CONFIG_HAVE_PERF_EVENTS=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HZ_PERIODIC=y
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_IP17XX_PHY=y
|
||||
CONFIG_IRQ_CPU=y
|
||||
CONFIG_IRQ_FORCED_THREADING=y
|
||||
CONFIG_IRQ_WORK=y
|
||||
CONFIG_KALLSYMS=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
||||
CONFIG_MDIO_BOARDINFO=y
|
||||
CONFIG_MIPS=y
|
||||
# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set
|
||||
CONFIG_MIPS_L1_CACHE_SHIFT=5
|
||||
# CONFIG_MIPS_MACHINE is not set
|
||||
CONFIG_MIPS_MT_DISABLED=y
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
# CONFIG_MTD_AC49X_PARTS is not set
|
||||
CONFIG_MTD_CFI_STAA=y
|
||||
CONFIG_MTD_PHYSMAP=y
|
||||
CONFIG_MVSWITCH_PHY=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NEED_PER_CPU_KM=y
|
||||
CONFIG_NO_EXCEPT_FILL=y
|
||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PHYLIB=y
|
||||
# CONFIG_PREEMPT_RCU is not set
|
||||
# CONFIG_RCU_STALL_COMMON is not set
|
||||
# CONFIG_SCSI_DMA is not set
|
||||
CONFIG_SWAP_IO_SPACE=y
|
||||
CONFIG_SWCONFIG=y
|
||||
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
|
||||
CONFIG_SYS_HAS_EARLY_PRINTK=y
|
||||
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
|
||||
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
|
||||
CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
|
||||
CONFIG_SYS_SUPPORTS_ZBOOT=y
|
||||
CONFIG_SYS_SUPPORTS_ZBOOT_UART16550=y
|
||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
CONFIG_VLYNQ=y
|
||||
# CONFIG_VLYNQ_DEBUG is not set
|
||||
CONFIG_ZONE_DMA_FLAG=0
|
|
@ -1,23 +0,0 @@
|
|||
From 443ab715a40881d6c9ba11b027ba154bac904cb0 Mon Sep 17 00:00:00 2001
|
||||
From: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
|
||||
Date: Sat, 10 May 2014 23:19:08 +0200
|
||||
Subject: [PATCH] MIPS/AR7: ensure that serial ports are properly set up
|
||||
|
||||
without UPF_FIXED_TYPE, the data from the PORT_AR7 uart_config entry is
|
||||
never copied, resulting in a dead port.
|
||||
|
||||
Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
|
||||
---
|
||||
arch/mips/ar7/platform.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/arch/mips/ar7/platform.c
|
||||
+++ b/arch/mips/ar7/platform.c
|
||||
@@ -581,6 +581,7 @@ static int __init ar7_register_uarts(voi
|
||||
uart_port.type = PORT_AR7;
|
||||
uart_port.uartclk = clk_get_rate(bus_clk) / 2;
|
||||
uart_port.iotype = UPIO_MEM32;
|
||||
+ uart_port.flags = UPF_FIXED_TYPE;
|
||||
uart_port.regshift = 2;
|
||||
|
||||
uart_port.line = 0;
|
|
@ -1,48 +0,0 @@
|
|||
From ee6c9d41de084b2cefd90e5e0c9f30a35f6d3967 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Sun, 29 Oct 2017 15:50:42 +0100
|
||||
Subject: [PATCH RFC 3/3] MIPS: AR7: ensure the port type's FCR value is used
|
||||
|
||||
Since commit aef9a7bd9b67 ("serial/uart/8250: Add tunable RX interrupt
|
||||
trigger I/F of FIFO buffers"), the port's default FCR value isn't used
|
||||
in serial8250_do_set_termios anymore, but copied over once in
|
||||
serial8250_config_port and then modified as needed.
|
||||
|
||||
Unfortunately, serial8250_config_port will never be called if the port
|
||||
is shared between kernel and userspace, and the port's flag doesn't have
|
||||
UPF_BOOT_AUTOCONF, which would trigger a serial8250_config_port as well.
|
||||
|
||||
This causes garbled output from userspace:
|
||||
|
||||
[ 5.220000] random: procd urandom read with 49 bits of entropy available
|
||||
ers
|
||||
[kee
|
||||
|
||||
Fix this by forcing it to be configured on boot, resulting in the
|
||||
expected output:
|
||||
|
||||
[ 5.250000] random: procd urandom read with 50 bits of entropy available
|
||||
Press the [f] key and hit [enter] to enter failsafe mode
|
||||
Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level
|
||||
|
||||
Fixes: aef9a7bd9b67 ("serial/uart/8250: Add tunable RX interrupt trigger I/F of FIFO buffers")
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
I'm not sure if this is just AR7's issue, or if this points to a general
|
||||
issue for UARTs used as kernel console and login console with the "fixed"
|
||||
commit.
|
||||
|
||||
arch/mips/ar7/platform.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/mips/ar7/platform.c
|
||||
+++ b/arch/mips/ar7/platform.c
|
||||
@@ -581,7 +581,7 @@ static int __init ar7_register_uarts(voi
|
||||
uart_port.type = PORT_AR7;
|
||||
uart_port.uartclk = clk_get_rate(bus_clk) / 2;
|
||||
uart_port.iotype = UPIO_MEM32;
|
||||
- uart_port.flags = UPF_FIXED_TYPE;
|
||||
+ uart_port.flags = UPF_FIXED_TYPE | UPF_BOOT_AUTOCONF;
|
||||
uart_port.regshift = 2;
|
||||
|
||||
uart_port.line = 0;
|
|
@ -1,11 +0,0 @@
|
|||
--- a/arch/mips/include/asm/mach-ar7/spaces.h
|
||||
+++ b/arch/mips/include/asm/mach-ar7/spaces.h
|
||||
@@ -20,6 +20,8 @@
|
||||
#define UNCAC_BASE _AC(0xb4000000, UL) /* 0xa0000000 + PHYS_OFFSET */
|
||||
#define IO_BASE UNCAC_BASE
|
||||
|
||||
+#define HIGHMEM_START _AC(0x20000000, UL)
|
||||
+
|
||||
#include <asm/mach-generic/spaces.h>
|
||||
|
||||
#endif /* __ASM_AR7_SPACES_H */
|
|
@ -1,45 +0,0 @@
|
|||
From patchwork Tue Jul 18 10:17:26 2017
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Subject: [5/9] MIPS: AR7: allow NULL clock for clk_get_rate
|
||||
X-Patchwork-Submitter: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
X-Patchwork-Id: 16775
|
||||
Message-Id: <20170718101730.2541-6-jonas.gorski@gmail.com>
|
||||
To: unlisted-recipients:; (no To-header on input)
|
||||
Cc: Ralf Baechle <ralf@linux-mips.org>,
|
||||
Paul Gortmaker <paul.gortmaker@windriver.com>,
|
||||
James Hogan <james.hogan@imgtec.com>,
|
||||
linux-mips@linux-mips.org, linux-kernel@vger.kernel.org
|
||||
Date: Tue, 18 Jul 2017 12:17:26 +0200
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
List-Id: linux-mips <linux-mips.eddie.linux-mips.org>
|
||||
|
||||
Make the behaviour of clk_get_rate consistent with common clk's
|
||||
clk_get_rate by accepting NULL clocks as parameter. Some device
|
||||
drivers rely on this, and will cause an OOPS otherwise.
|
||||
|
||||
Fixes: 780019ddf02f ("MIPS: AR7: Implement clock API")
|
||||
Cc: Ralf Baechle <ralf@linux-mips.org>
|
||||
Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
|
||||
Cc: James Hogan <james.hogan@imgtec.com>
|
||||
Cc: linux-mips@linux-mips.org
|
||||
Cc: linux-kernel@vger.kernel.org
|
||||
Reported-by: Mathias Kresin <dev@kresin.me>
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
arch/mips/ar7/clock.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/arch/mips/ar7/clock.c
|
||||
+++ b/arch/mips/ar7/clock.c
|
||||
@@ -430,6 +430,9 @@ EXPORT_SYMBOL(clk_disable);
|
||||
|
||||
unsigned long clk_get_rate(struct clk *clk)
|
||||
{
|
||||
+ if (!clk)
|
||||
+ return 0;
|
||||
+
|
||||
return clk->rate;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get_rate);
|
|
@ -1,22 +0,0 @@
|
|||
--- a/drivers/mtd/Makefile
|
||||
+++ b/drivers/mtd/Makefile
|
||||
@@ -12,7 +12,7 @@ obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
|
||||
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
|
||||
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
|
||||
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
|
||||
-obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
|
||||
+obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o titanpart.o
|
||||
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
|
||||
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
|
||||
obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
|
||||
--- a/arch/mips/ar7/platform.c
|
||||
+++ b/arch/mips/ar7/platform.c
|
||||
@@ -199,7 +199,7 @@ static struct resource physmap_flash_res
|
||||
.name = "mem",
|
||||
.flags = IORESOURCE_MEM,
|
||||
.start = 0x10000000,
|
||||
- .end = 0x107fffff,
|
||||
+ .end = 0x11ffffff,
|
||||
};
|
||||
|
||||
static const char *ar7_probe_types[] = { "ar7part", NULL };
|
|
@ -1,300 +0,0 @@
|
|||
--- a/drivers/vlynq/vlynq.c
|
||||
+++ b/drivers/vlynq/vlynq.c
|
||||
@@ -119,20 +119,40 @@ static int vlynq_linked(struct vlynq_dev
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static volatile int vlynq_delay_value_new = 0;
|
||||
+
|
||||
+static void vlynq_delay_wait(u32 count)
|
||||
+{
|
||||
+ /* Code adopted from original vlynq driver */
|
||||
+ int i = 0;
|
||||
+ volatile int *ptr = &vlynq_delay_value_new;
|
||||
+ *ptr = 0;
|
||||
+
|
||||
+ /* We are assuming that the each cycle takes about
|
||||
+ * 23 assembly instructions. */
|
||||
+ for(i = 0; i < (count + 23)/23; i++)
|
||||
+ *ptr = *ptr + 1;
|
||||
+}
|
||||
+
|
||||
static void vlynq_reset(struct vlynq_device *dev)
|
||||
{
|
||||
+ u32 rtm = readl(&dev->local->revision);
|
||||
+
|
||||
+ rtm = rtm < 0x00010205 || readl(&dev->local->status) & 0x800 == 0 ?
|
||||
+ 0 : 0x600000;
|
||||
+
|
||||
writel(readl(&dev->local->control) | VLYNQ_CTRL_RESET,
|
||||
&dev->local->control);
|
||||
|
||||
/* Wait for the devices to finish resetting */
|
||||
- msleep(5);
|
||||
+ vlynq_delay_wait(0xffffff);
|
||||
|
||||
/* Remove reset bit */
|
||||
- writel(readl(&dev->local->control) & ~VLYNQ_CTRL_RESET,
|
||||
+ writel(readl(&dev->local->control) & ~VLYNQ_CTRL_RESET | rtm,
|
||||
&dev->local->control);
|
||||
|
||||
/* Give some time for the devices to settle */
|
||||
- msleep(5);
|
||||
+ vlynq_delay_wait(0xffffff);
|
||||
}
|
||||
|
||||
static void vlynq_irq_unmask(struct irq_data *d)
|
||||
@@ -379,6 +399,61 @@ void vlynq_unregister_driver(struct vlyn
|
||||
}
|
||||
EXPORT_SYMBOL(vlynq_unregister_driver);
|
||||
|
||||
+enum vlynq_clk_src {
|
||||
+ vlynq_clk_external,
|
||||
+ vlynq_clk_local,
|
||||
+ vlynq_clk_remote,
|
||||
+ vlynq_clk_invalid,
|
||||
+};
|
||||
+
|
||||
+static int __vlynq_set_clocks(struct vlynq_device *dev,
|
||||
+ enum vlynq_clk_src clk_dir,
|
||||
+ int lclk_div, int rclk_div)
|
||||
+{
|
||||
+ u32 reg;
|
||||
+
|
||||
+ if (clk_dir == vlynq_clk_invalid) {
|
||||
+ printk(KERN_ERR "%s: attempt to set invalid clocking\n",
|
||||
+ dev_name(&dev->dev));
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ reg = readl(&dev->local->control);
|
||||
+ if (readl(&dev->local->revision) < 0x00010205) {
|
||||
+ if (clk_dir & vlynq_clk_local)
|
||||
+ reg |= VLYNQ_CTRL_CLOCK_INT;
|
||||
+ else
|
||||
+ reg &= ~VLYNQ_CTRL_CLOCK_INT;
|
||||
+ }
|
||||
+ reg &= ~VLYNQ_CTRL_CLOCK_MASK;
|
||||
+ reg |= VLYNQ_CTRL_CLOCK_DIV(lclk_div);
|
||||
+ writel(reg, &dev->local->control);
|
||||
+
|
||||
+ if (!vlynq_linked(dev))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ printk(KERN_INFO "%s: local VLYNQ protocol rev. is 0x%08x\n",
|
||||
+ dev_name(&dev->dev), readl(&dev->local->revision));
|
||||
+ printk(KERN_INFO "%s: remote VLYNQ protocol rev. is 0x%08x\n",
|
||||
+ dev_name(&dev->dev), readl(&dev->remote->revision));
|
||||
+
|
||||
+ reg = readl(&dev->remote->control);
|
||||
+ if (readl(&dev->remote->revision) < 0x00010205) {
|
||||
+ if (clk_dir & vlynq_clk_remote)
|
||||
+ reg |= VLYNQ_CTRL_CLOCK_INT;
|
||||
+ else
|
||||
+ reg &= ~VLYNQ_CTRL_CLOCK_INT;
|
||||
+ }
|
||||
+ reg &= ~VLYNQ_CTRL_CLOCK_MASK;
|
||||
+ reg |= VLYNQ_CTRL_CLOCK_DIV(rclk_div);
|
||||
+ writel(reg, &dev->remote->control);
|
||||
+
|
||||
+ if (!vlynq_linked(dev))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* A VLYNQ remote device can clock the VLYNQ bus master
|
||||
* using a dedicated clock line. In that case, both the
|
||||
@@ -392,29 +467,16 @@ static int __vlynq_try_remote(struct vly
|
||||
int i;
|
||||
|
||||
vlynq_reset(dev);
|
||||
- for (i = dev->dev_id ? vlynq_rdiv2 : vlynq_rdiv8; dev->dev_id ?
|
||||
- i <= vlynq_rdiv8 : i >= vlynq_rdiv2;
|
||||
- dev->dev_id ? i++ : i--) {
|
||||
+ for (i = 0; i <= 7; i++) {
|
||||
|
||||
if (!vlynq_linked(dev))
|
||||
break;
|
||||
|
||||
- writel((readl(&dev->remote->control) &
|
||||
- ~VLYNQ_CTRL_CLOCK_MASK) |
|
||||
- VLYNQ_CTRL_CLOCK_INT |
|
||||
- VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
|
||||
- &dev->remote->control);
|
||||
- writel((readl(&dev->local->control)
|
||||
- & ~(VLYNQ_CTRL_CLOCK_INT |
|
||||
- VLYNQ_CTRL_CLOCK_MASK)) |
|
||||
- VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
|
||||
- &dev->local->control);
|
||||
-
|
||||
- if (vlynq_linked(dev)) {
|
||||
- printk(KERN_DEBUG
|
||||
- "%s: using remote clock divisor %d\n",
|
||||
- dev_name(&dev->dev), i - vlynq_rdiv1 + 1);
|
||||
- dev->divisor = i;
|
||||
+ if (!__vlynq_set_clocks(dev, vlynq_clk_remote, i, i)) {
|
||||
+ printk(KERN_INFO
|
||||
+ "%s: using remote clock divisor %d\n",
|
||||
+ dev_name(&dev->dev), i + 1);
|
||||
+ dev->divisor = i + vlynq_rdiv1;
|
||||
return 0;
|
||||
} else {
|
||||
vlynq_reset(dev);
|
||||
@@ -433,25 +495,17 @@ static int __vlynq_try_remote(struct vly
|
||||
*/
|
||||
static int __vlynq_try_local(struct vlynq_device *dev)
|
||||
{
|
||||
- int i;
|
||||
+ int i, dir = !dev->dev_id;
|
||||
|
||||
vlynq_reset(dev);
|
||||
|
||||
- for (i = dev->dev_id ? vlynq_ldiv2 : vlynq_ldiv8; dev->dev_id ?
|
||||
- i <= vlynq_ldiv8 : i >= vlynq_ldiv2;
|
||||
- dev->dev_id ? i++ : i--) {
|
||||
-
|
||||
- writel((readl(&dev->local->control) &
|
||||
- ~VLYNQ_CTRL_CLOCK_MASK) |
|
||||
- VLYNQ_CTRL_CLOCK_INT |
|
||||
- VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1),
|
||||
- &dev->local->control);
|
||||
-
|
||||
- if (vlynq_linked(dev)) {
|
||||
- printk(KERN_DEBUG
|
||||
- "%s: using local clock divisor %d\n",
|
||||
- dev_name(&dev->dev), i - vlynq_ldiv1 + 1);
|
||||
- dev->divisor = i;
|
||||
+ for (i = dir ? 7 : 0; dir ? i >= 0 : i <= 7; dir ? i-- : i++) {
|
||||
+
|
||||
+ if (!__vlynq_set_clocks(dev, vlynq_clk_local, i, 0)) {
|
||||
+ printk(KERN_INFO
|
||||
+ "%s: using local clock divisor %d\n",
|
||||
+ dev_name(&dev->dev), i + 1);
|
||||
+ dev->divisor = i + vlynq_ldiv1;
|
||||
return 0;
|
||||
} else {
|
||||
vlynq_reset(dev);
|
||||
@@ -473,18 +527,10 @@ static int __vlynq_try_external(struct v
|
||||
if (!vlynq_linked(dev))
|
||||
return -ENODEV;
|
||||
|
||||
- writel((readl(&dev->remote->control) &
|
||||
- ~VLYNQ_CTRL_CLOCK_INT),
|
||||
- &dev->remote->control);
|
||||
-
|
||||
- writel((readl(&dev->local->control) &
|
||||
- ~VLYNQ_CTRL_CLOCK_INT),
|
||||
- &dev->local->control);
|
||||
-
|
||||
- if (vlynq_linked(dev)) {
|
||||
- printk(KERN_DEBUG "%s: using external clock\n",
|
||||
- dev_name(&dev->dev));
|
||||
- dev->divisor = vlynq_div_external;
|
||||
+ if (!__vlynq_set_clocks(dev, vlynq_clk_external, 0, 0)) {
|
||||
+ printk(KERN_INFO "%s: using external clock\n",
|
||||
+ dev_name(&dev->dev));
|
||||
+ dev->divisor = vlynq_div_external;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -501,24 +547,16 @@ static int __vlynq_enable_device(struct
|
||||
return result;
|
||||
|
||||
switch (dev->divisor) {
|
||||
- case vlynq_div_external:
|
||||
case vlynq_div_auto:
|
||||
/* When the device is brought from reset it should have clock
|
||||
* generation negotiated by hardware.
|
||||
* Check which device is generating clocks and perform setup
|
||||
* accordingly */
|
||||
- if (vlynq_linked(dev) && readl(&dev->remote->control) &
|
||||
- VLYNQ_CTRL_CLOCK_INT) {
|
||||
- if (!__vlynq_try_remote(dev) ||
|
||||
- !__vlynq_try_local(dev) ||
|
||||
- !__vlynq_try_external(dev))
|
||||
- return 0;
|
||||
- } else {
|
||||
- if (!__vlynq_try_external(dev) ||
|
||||
- !__vlynq_try_local(dev) ||
|
||||
- !__vlynq_try_remote(dev))
|
||||
- return 0;
|
||||
- }
|
||||
+ if (!__vlynq_try_remote(dev) || !__vlynq_try_local(dev))
|
||||
+ return 0;
|
||||
+ case vlynq_div_external:
|
||||
+ if (!__vlynq_try_external(dev))
|
||||
+ return 0;
|
||||
break;
|
||||
case vlynq_ldiv1:
|
||||
case vlynq_ldiv2:
|
||||
@@ -528,15 +566,12 @@ static int __vlynq_enable_device(struct
|
||||
case vlynq_ldiv6:
|
||||
case vlynq_ldiv7:
|
||||
case vlynq_ldiv8:
|
||||
- writel(VLYNQ_CTRL_CLOCK_INT |
|
||||
- VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
|
||||
- vlynq_ldiv1), &dev->local->control);
|
||||
- writel(0, &dev->remote->control);
|
||||
- if (vlynq_linked(dev)) {
|
||||
- printk(KERN_DEBUG
|
||||
- "%s: using local clock divisor %d\n",
|
||||
- dev_name(&dev->dev),
|
||||
- dev->divisor - vlynq_ldiv1 + 1);
|
||||
+ if (!__vlynq_set_clocks(dev, vlynq_clk_local, dev->divisor -
|
||||
+ vlynq_ldiv1, 0)) {
|
||||
+ printk(KERN_INFO
|
||||
+ "%s: using local clock divisor %d\n",
|
||||
+ dev_name(&dev->dev),
|
||||
+ dev->divisor - vlynq_ldiv1 + 1);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
@@ -548,20 +583,17 @@ static int __vlynq_enable_device(struct
|
||||
case vlynq_rdiv6:
|
||||
case vlynq_rdiv7:
|
||||
case vlynq_rdiv8:
|
||||
- writel(0, &dev->local->control);
|
||||
- writel(VLYNQ_CTRL_CLOCK_INT |
|
||||
- VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
|
||||
- vlynq_rdiv1), &dev->remote->control);
|
||||
- if (vlynq_linked(dev)) {
|
||||
- printk(KERN_DEBUG
|
||||
- "%s: using remote clock divisor %d\n",
|
||||
- dev_name(&dev->dev),
|
||||
- dev->divisor - vlynq_rdiv1 + 1);
|
||||
+ if (!__vlynq_set_clocks(dev, vlynq_clk_remote, 0,
|
||||
+ dev->divisor - vlynq_rdiv1)) {
|
||||
+ printk(KERN_INFO
|
||||
+ "%s: using remote clock divisor %d\n",
|
||||
+ dev_name(&dev->dev),
|
||||
+ dev->divisor - vlynq_rdiv1 + 1);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
-
|
||||
+ vlynq_reset(dev);
|
||||
ops->off(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -732,14 +764,14 @@ static int vlynq_probe(struct platform_d
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n",
|
||||
- dev_name(&dev->dev), (void *)dev->regs_start, dev->irq,
|
||||
- (void *)dev->mem_start);
|
||||
+ dev_name(&dev->dev), (void *)dev->regs_start,
|
||||
+ dev->irq, (void *)dev->mem_start);
|
||||
|
||||
dev->dev_id = 0;
|
||||
dev->divisor = vlynq_div_auto;
|
||||
- result = __vlynq_enable_device(dev);
|
||||
- if (result == 0) {
|
||||
+ if (!__vlynq_enable_device(dev)) {
|
||||
dev->dev_id = readl(&dev->remote->chip);
|
||||
+ vlynq_reset(dev);
|
||||
((struct plat_vlynq_ops *)(dev->dev.platform_data))->off(dev);
|
||||
}
|
||||
if (dev->dev_id)
|
|
@ -1,15 +0,0 @@
|
|||
--- a/arch/mips/ar7/memory.c
|
||||
+++ b/arch/mips/ar7/memory.c
|
||||
@@ -66,5 +66,11 @@ void __init prom_meminit(void)
|
||||
|
||||
void __init prom_free_prom_memory(void)
|
||||
{
|
||||
- /* Nothing to free */
|
||||
+ /* adapted from arch/mips/txx9/generic/setup.c */
|
||||
+ unsigned long saddr = PHYS_OFFSET + PAGE_SIZE;
|
||||
+ unsigned long eaddr = __pa_symbol(&_text);
|
||||
+
|
||||
+ /* free memory between prom-record and kernel _text base */
|
||||
+ if (saddr < eaddr)
|
||||
+ free_init_pages("prom memory", saddr, eaddr);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
--- a/arch/mips/ar7/Platform
|
||||
+++ b/arch/mips/ar7/Platform
|
||||
@@ -3,4 +3,9 @@
|
||||
#
|
||||
platform-$(CONFIG_AR7) += ar7/
|
||||
cflags-$(CONFIG_AR7) += -I$(srctree)/arch/mips/include/asm/mach-ar7
|
||||
-load-$(CONFIG_AR7) += 0xffffffff94100000
|
||||
+load-$(CONFIG_AR7_TI) += 0xffffffff94100000
|
||||
+
|
||||
+#
|
||||
+# AudioCodes AC49x
|
||||
+#
|
||||
+load-$(CONFIG_AR7_AC49X) += 0xffffffff945ca000
|
||||
--- a/arch/mips/ar7/setup.c
|
||||
+++ b/arch/mips/ar7/setup.c
|
||||
@@ -69,6 +69,10 @@ const char *get_system_type(void)
|
||||
return "TI AR7 (TNETV1056)";
|
||||
case TITAN_CHIP_1060:
|
||||
return "TI AR7 (TNETV1060)";
|
||||
+ case TITAN_CHIP_AC495:
|
||||
+ return "AudioCodes AC495";
|
||||
+ case TITAN_CHIP_AC496:
|
||||
+ return "AudioCodes AC496";
|
||||
}
|
||||
default:
|
||||
return "TI AR7 (unknown)";
|
||||
--- a/arch/mips/include/asm/mach-ar7/ar7.h
|
||||
+++ b/arch/mips/include/asm/mach-ar7/ar7.h
|
||||
@@ -92,6 +92,8 @@
|
||||
#define TITAN_CHIP_1055 0x0e
|
||||
#define TITAN_CHIP_1056 0x0d
|
||||
#define TITAN_CHIP_1060 0x07
|
||||
+#define TITAN_CHIP_AC495 0x00
|
||||
+#define TITAN_CHIP_AC496 0x02
|
||||
|
||||
/* Interrupts */
|
||||
#define AR7_IRQ_UART0 15
|
||||
--- a/arch/mips/Kconfig
|
||||
+++ b/arch/mips/Kconfig
|
||||
@@ -94,7 +94,7 @@ config AR7
|
||||
select HAVE_CLK
|
||||
help
|
||||
Support for the Texas Instruments AR7 System-on-a-Chip
|
||||
- family: TNETD7100, 7200 and 7300.
|
||||
+ family: TI TNETD7100, 7200, 7300 and AudioCodes AC49x.
|
||||
|
||||
config ATH79
|
||||
bool "Atheros AR71XX/AR724X/AR913X based boards"
|
||||
@@ -835,6 +835,7 @@ config MIPS_PARAVIRT
|
||||
endchoice
|
||||
|
||||
source "arch/mips/alchemy/Kconfig"
|
||||
+source "arch/mips/ar7/Kconfig"
|
||||
source "arch/mips/ath79/Kconfig"
|
||||
source "arch/mips/bcm47xx/Kconfig"
|
||||
source "arch/mips/bcm63xx/Kconfig"
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/ar7/Kconfig
|
||||
@@ -0,0 +1,26 @@
|
||||
+if AR7
|
||||
+
|
||||
+config AR7_TI
|
||||
+ bool
|
||||
+
|
||||
+config AR7_AC49X
|
||||
+ bool
|
||||
+
|
||||
+choice
|
||||
+ prompt "AR7 SoC family selection"
|
||||
+ default AR7_TYPE_TI
|
||||
+ depends on AR7
|
||||
+ help
|
||||
+ Select AR7 MIPS SoC implementation.
|
||||
+
|
||||
+ config AR7_TYPE_TI
|
||||
+ bool "Texas Instruments AR7"
|
||||
+ select AR7_TI
|
||||
+
|
||||
+ config AR7_TYPE_AC49X
|
||||
+ bool "AudioCodes AC49X"
|
||||
+ select AR7_AC49X
|
||||
+
|
||||
+endchoice
|
||||
+
|
||||
+endif
|
|
@ -1,20 +0,0 @@
|
|||
--- a/arch/mips/ar7/prom.c
|
||||
+++ b/arch/mips/ar7/prom.c
|
||||
@@ -70,6 +70,7 @@ struct psbl_rec {
|
||||
};
|
||||
|
||||
static const char psp_env_version[] __initconst = "TIENV0.8";
|
||||
+static const char psp_env_version_ac49x[] __initconst = "MaxENV0.2";
|
||||
|
||||
struct psp_env_chunk {
|
||||
u8 num;
|
||||
@@ -186,7 +187,8 @@ static void __init ar7_init_env(struct e
|
||||
struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300));
|
||||
void *psp_env = (void *)KSEG1ADDR(psbl->env_base);
|
||||
|
||||
- if (strcmp(psp_env, psp_env_version) == 0) {
|
||||
+ if (strcmp(psp_env, psp_env_version) == 0 ||
|
||||
+ strcmp(psp_env, psp_env_version_ac49x) == 0) {
|
||||
parse_psp_env(psp_env);
|
||||
} else {
|
||||
for (i = 0; i < MAX_ENTRY; i++, env++)
|
|
@ -1,35 +0,0 @@
|
|||
--- a/drivers/mtd/Kconfig
|
||||
+++ b/drivers/mtd/Kconfig
|
||||
@@ -164,6 +164,11 @@ config MTD_OF_PARTS
|
||||
the partition map from the children of the flash node,
|
||||
as described in Documentation/devicetree/booting-without-of.txt.
|
||||
|
||||
+config MTD_AC49X_PARTS
|
||||
+ tristate "AudioCodes AC49X partitioning support"
|
||||
+ ---help---
|
||||
+ AudioCodes AC49X partitioning support
|
||||
+
|
||||
config MTD_AR7_PARTS
|
||||
tristate "TI AR7 partitioning support"
|
||||
---help---
|
||||
--- a/drivers/mtd/Makefile
|
||||
+++ b/drivers/mtd/Makefile
|
||||
@@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
|
||||
obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
|
||||
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
|
||||
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
|
||||
+obj-$(CONFIG_MTD_AC49X_PARTS) += ac49xpart.o
|
||||
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
|
||||
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o titanpart.o
|
||||
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
|
||||
--- a/arch/mips/ar7/platform.c
|
||||
+++ b/arch/mips/ar7/platform.c
|
||||
@@ -202,7 +202,7 @@ static struct resource physmap_flash_res
|
||||
.end = 0x11ffffff,
|
||||
};
|
||||
|
||||
-static const char *ar7_probe_types[] = { "ar7part", NULL };
|
||||
+static const char *ar7_probe_types[] = { "ac49xpart", "ar7part", NULL };
|
||||
|
||||
static struct physmap_flash_data physmap_flash_data = {
|
||||
.width = 2,
|
|
@ -1,118 +0,0 @@
|
|||
--- a/drivers/mtd/ar7part.c
|
||||
+++ b/drivers/mtd/ar7part.c
|
||||
@@ -30,11 +30,14 @@
|
||||
|
||||
#include <uapi/linux/magic.h>
|
||||
|
||||
+#include <asm/mach-ar7/prom.h>
|
||||
+
|
||||
#define AR7_PARTS 4
|
||||
#define ROOT_OFFSET 0xe0000
|
||||
|
||||
#define LOADER_MAGIC1 le32_to_cpu(0xfeedfa42)
|
||||
#define LOADER_MAGIC2 le32_to_cpu(0xfeed1281)
|
||||
+#define LOADER_MAGIC3 le32_to_cpu(0x434d4d4c)
|
||||
|
||||
struct ar7_bin_rec {
|
||||
unsigned int checksum;
|
||||
@@ -42,12 +45,16 @@ struct ar7_bin_rec {
|
||||
unsigned int address;
|
||||
};
|
||||
|
||||
+int create_titan_partitions(struct mtd_info *master,
|
||||
+ struct mtd_partition **pparts,
|
||||
+ struct mtd_part_parser_data *data);
|
||||
+
|
||||
static int create_mtd_partitions(struct mtd_info *master,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct ar7_bin_rec header;
|
||||
- unsigned int offset;
|
||||
+ unsigned int offset, mtd_start, mtd_end;
|
||||
size_t len;
|
||||
unsigned int pre_size = master->erasesize, post_size = 0;
|
||||
unsigned int root_offset = ROOT_OFFSET;
|
||||
@@ -55,6 +62,16 @@ static int create_mtd_partitions(struct
|
||||
int retries = 10;
|
||||
struct mtd_partition *ar7_parts;
|
||||
|
||||
+ const char *prom_str = prom_getenv("ProductID");
|
||||
+ char mtd_name[] = "mtd1";
|
||||
+ if(prom_str &&
|
||||
+ (strcmp(prom_str, "CYWL")==0 ||
|
||||
+ strcmp(prom_str, "CYWM")==0 ||
|
||||
+ strcmp(prom_str, "CYLM")==0 ||
|
||||
+ strcmp(prom_str, "CYLL")==0)){
|
||||
+ return create_titan_partitions(master, pparts, data);
|
||||
+ }
|
||||
+
|
||||
ar7_parts = kzalloc(sizeof(*ar7_parts) * AR7_PARTS, GFP_KERNEL);
|
||||
if (!ar7_parts)
|
||||
return -ENOMEM;
|
||||
@@ -83,34 +100,39 @@ static int create_mtd_partitions(struct
|
||||
|
||||
pre_size = offset;
|
||||
|
||||
- if (!ar7_parts[1].offset) {
|
||||
- ar7_parts[1].offset = master->size - master->erasesize;
|
||||
- post_size = master->erasesize;
|
||||
- }
|
||||
-
|
||||
switch (header.checksum) {
|
||||
- case LOADER_MAGIC1:
|
||||
- while (header.length) {
|
||||
- offset += sizeof(header) + header.length;
|
||||
- mtd_read(master, offset, sizeof(header), &len,
|
||||
- (uint8_t *)&header);
|
||||
- }
|
||||
- root_offset = offset + sizeof(header) + 4;
|
||||
- break;
|
||||
case LOADER_MAGIC2:
|
||||
+ for (retries = 0; retries <= 9; retries++) {
|
||||
+ mtd_name[3] = '0' + retries;
|
||||
+ prom_str = prom_getenv(mtd_name);
|
||||
+ if (prom_str == NULL)
|
||||
+ continue;
|
||||
+ sscanf(prom_str, "%i,%i", &mtd_start, &mtd_end);
|
||||
+ if (pre_size == (mtd_start & 0x1ffffff)) {
|
||||
+ ar7_parts[1].offset = mtd_end &= 0x1ffffff;
|
||||
+ ar7_parts[1].size = post_size = master->size - mtd_end;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ case LOADER_MAGIC1:
|
||||
+ root_offset = (header.checksum == LOADER_MAGIC1) ? 4 : 0;
|
||||
while (header.length) {
|
||||
offset += sizeof(header) + header.length;
|
||||
mtd_read(master, offset, sizeof(header), &len,
|
||||
(uint8_t *)&header);
|
||||
}
|
||||
- root_offset = offset + sizeof(header) + 4 + 0xff;
|
||||
- root_offset &= ~(uint32_t)0xff;
|
||||
+ root_offset += offset + sizeof(header);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "Unknown magic: %08x\n", header.checksum);
|
||||
break;
|
||||
}
|
||||
|
||||
+ if (!ar7_parts[1].offset) {
|
||||
+ post_size = master->erasesize;
|
||||
+ ar7_parts[1].offset = master->size - post_size;
|
||||
+ }
|
||||
+
|
||||
mtd_read(master, root_offset, sizeof(header), &len, (u8 *)&header);
|
||||
if (header.checksum != SQUASHFS_MAGIC) {
|
||||
root_offset += master->erasesize - 1;
|
||||
--- a/drivers/mtd/titanpart.c
|
||||
+++ b/drivers/mtd/titanpart.c
|
||||
@@ -149,7 +149,7 @@ static void titan_add_partition(char * e
|
||||
}
|
||||
int create_titan_partitions(struct mtd_info *master,
|
||||
struct mtd_partition **pparts,
|
||||
- unsigned long origin)
|
||||
+ struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct nsp_img_hdr_head hdr;
|
||||
struct nsp_img_hdr_section_info sect_info;
|
|
@ -1,95 +0,0 @@
|
|||
--- a/arch/mips/ar7/platform.c
|
||||
+++ b/arch/mips/ar7/platform.c
|
||||
@@ -465,31 +465,22 @@ static struct gpio_led fb_fon_leds[] = {
|
||||
},
|
||||
};
|
||||
|
||||
-static struct gpio_led gt701_leds[] = {
|
||||
+static struct gpio_led actiontec_leds[] = {
|
||||
{
|
||||
.name = "inet:green",
|
||||
.gpio = 13,
|
||||
- .active_low = 1,
|
||||
- },
|
||||
- {
|
||||
- .name = "usb",
|
||||
- .gpio = 12,
|
||||
- .active_low = 1,
|
||||
},
|
||||
{
|
||||
.name = "inet:red",
|
||||
.gpio = 9,
|
||||
- .active_low = 1,
|
||||
},
|
||||
{
|
||||
- .name = "power:red",
|
||||
+ .name = "power:green",
|
||||
.gpio = 7,
|
||||
- .active_low = 1,
|
||||
},
|
||||
{
|
||||
- .name = "power:green",
|
||||
+ .name = "power:red",
|
||||
.gpio = 8,
|
||||
- .active_low = 1,
|
||||
.default_trigger = "default-on",
|
||||
},
|
||||
{
|
||||
@@ -497,6 +488,44 @@ static struct gpio_led gt701_leds[] = {
|
||||
.gpio = 10,
|
||||
.active_low = 1,
|
||||
},
|
||||
+ {
|
||||
+ .name = "wifi",
|
||||
+ .gpio = 6,
|
||||
+ .active_low = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "wifi:red",
|
||||
+ .gpio = 3,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "standby",
|
||||
+ .gpio = 4,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "wps",
|
||||
+ .gpio = 16,
|
||||
+ .active_low = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "usb",
|
||||
+ .gpio = 12,
|
||||
+ .active_low = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "voip",
|
||||
+ .gpio = 15,
|
||||
+ .active_low = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "line1",
|
||||
+ .gpio = 23,
|
||||
+ .active_low = 1,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "line2",
|
||||
+ .gpio = 25,
|
||||
+ .active_low = 1,
|
||||
+ },
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data ar7_led_data;
|
||||
@@ -540,9 +569,9 @@ static void __init detect_leds(void)
|
||||
} else if (strstr(prid, "CYWM") || strstr(prid, "CYWL")) {
|
||||
ar7_led_data.num_leds = ARRAY_SIZE(titan_leds);
|
||||
ar7_led_data.leds = titan_leds;
|
||||
- } else if (strstr(prid, "GT701")) {
|
||||
- ar7_led_data.num_leds = ARRAY_SIZE(gt701_leds);
|
||||
- ar7_led_data.leds = gt701_leds;
|
||||
+ } else if (strstr(prid, "GT7") || strstr(prid, "PK5000")) {
|
||||
+ ar7_led_data.num_leds = ARRAY_SIZE(actiontec_leds);
|
||||
+ ar7_led_data.leds = actiontec_leds;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
--- a/drivers/net/ethernet/ti/cpmac.c
|
||||
+++ b/drivers/net/ethernet/ti/cpmac.c
|
||||
@@ -1147,6 +1147,8 @@ static int cpmac_probe(struct platform_d
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ ar7_device_reset(pdata->reset_bit);
|
||||
+
|
||||
dev->irq = platform_get_irq_byname(pdev, "irq");
|
||||
|
||||
dev->netdev_ops = &cpmac_netdev_ops;
|
||||
@@ -1228,7 +1230,7 @@ int cpmac_init(void)
|
||||
cpmac_mii->reset = cpmac_mdio_reset;
|
||||
cpmac_mii->irq = mii_irqs;
|
||||
|
||||
- cpmac_mii->priv = ioremap(AR7_REGS_MDIO, 256);
|
||||
+ cpmac_mii->priv = ioremap(ar7_is_titan() ? TITAN_REGS_MDIO : AR7_REGS_MDIO, 256);
|
||||
|
||||
if (!cpmac_mii->priv) {
|
||||
pr_err("Can't ioremap mdio registers\n");
|
||||
@@ -1239,10 +1241,16 @@ int cpmac_init(void)
|
||||
/* FIXME: unhardcode gpio&reset bits */
|
||||
ar7_gpio_disable(26);
|
||||
ar7_gpio_disable(27);
|
||||
- ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
|
||||
- ar7_device_reset(AR7_RESET_BIT_CPMAC_HI);
|
||||
+
|
||||
+ if (!ar7_is_titan()) {
|
||||
+ ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
|
||||
+ ar7_device_reset(AR7_RESET_BIT_CPMAC_HI);
|
||||
+ }
|
||||
ar7_device_reset(AR7_RESET_BIT_EPHY);
|
||||
|
||||
+ if (ar7_is_titan())
|
||||
+ ar7_device_reset(TITAN_RESET_BIT_EPHY1);
|
||||
+
|
||||
cpmac_mii->reset(cpmac_mii);
|
||||
|
||||
for (i = 0; i < 300; i++) {
|
||||
@@ -1259,7 +1267,11 @@ int cpmac_init(void)
|
||||
mask = 0;
|
||||
}
|
||||
|
||||
- cpmac_mii->phy_mask = ~(mask | 0x80000000);
|
||||
+ if (ar7_is_titan())
|
||||
+ cpmac_mii->phy_mask = ~(mask | 0x80000000 | 0x40000000);
|
||||
+ else
|
||||
+ cpmac_mii->phy_mask = ~(mask | 0x80000000);
|
||||
+
|
||||
snprintf(cpmac_mii->id, MII_BUS_ID_SIZE, "cpmac-1");
|
||||
|
||||
res = mdiobus_register(cpmac_mii);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,142 +0,0 @@
|
|||
--- a/drivers/mtd/mtdsplit/mtdsplit_brnimage.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_brnimage.c
|
||||
@@ -27,7 +27,7 @@
|
||||
#define BRNIMAGE_MAX_OVERHEAD (BRNIMAGE_ALIGN_BYTES + BRNIMAGE_FOOTER_SIZE)
|
||||
|
||||
static int mtdsplit_parse_brnimage(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_eva.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_eva.c
|
||||
@@ -29,7 +29,7 @@ struct eva_image_header {
|
||||
};
|
||||
|
||||
static int mtdsplit_parse_eva(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_fit.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_fit.c
|
||||
@@ -45,8 +45,7 @@ struct fdt_header {
|
||||
};
|
||||
|
||||
static int
|
||||
-mtdsplit_fit_parse(struct mtd_info *mtd,
|
||||
- const struct mtd_partition **pparts,
|
||||
+mtdsplit_fit_parse(struct mtd_info *mtd, struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct fdt_header hdr;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_lzma.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_lzma.c
|
||||
@@ -28,7 +28,7 @@ struct lzma_header {
|
||||
};
|
||||
|
||||
static int mtdsplit_parse_lzma(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct lzma_header hdr;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_seama.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_seama.c
|
||||
@@ -30,7 +30,7 @@ struct seama_header {
|
||||
};
|
||||
|
||||
static int mtdsplit_parse_seama(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct seama_header hdr;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_squashfs.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_squashfs.c
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
static int
|
||||
mtdsplit_parse_squashfs(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_partition *part;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_tplink.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_tplink.c
|
||||
@@ -83,8 +83,8 @@ struct tplink_fw_header {
|
||||
};
|
||||
|
||||
static int mtdsplit_parse_tplink(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
- struct mtd_part_parser_data *data)
|
||||
+ struct mtd_partition **pparts,
|
||||
+ struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct tplink_fw_header hdr;
|
||||
size_t hdr_len, retlen, kernel_size;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_trx.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_trx.c
|
||||
@@ -56,7 +56,7 @@ read_trx_header(struct mtd_info *mtd, si
|
||||
|
||||
static int
|
||||
mtdsplit_parse_trx(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_uimage.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_uimage.c
|
||||
@@ -81,7 +81,7 @@ read_uimage_header(struct mtd_info *mtd,
|
||||
* of a valid uImage header if found
|
||||
*/
|
||||
static int __mtdsplit_parse_uimage(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data,
|
||||
ssize_t (*find_header)(u_char *buf, size_t len))
|
||||
{
|
||||
@@ -232,7 +232,7 @@ static ssize_t uimage_verify_default(u_c
|
||||
|
||||
static int
|
||||
mtdsplit_uimage_parse_generic(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
return __mtdsplit_parse_uimage(master, pparts, data,
|
||||
@@ -289,7 +289,7 @@ static ssize_t uimage_verify_wndr3700(u_
|
||||
|
||||
static int
|
||||
mtdsplit_uimage_parse_netgear(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
return __mtdsplit_parse_uimage(master, pparts, data,
|
||||
@@ -331,7 +331,7 @@ static ssize_t uimage_find_edimax(u_char
|
||||
|
||||
static int
|
||||
mtdsplit_uimage_parse_edimax(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
return __mtdsplit_parse_uimage(master, pparts, data,
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
|
||||
@@ -40,8 +40,8 @@ struct wrgg03_header {
|
||||
|
||||
|
||||
static int mtdsplit_parse_wrgg(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
- struct mtd_part_parser_data *data)
|
||||
+ struct mtd_partition **pparts,
|
||||
+ struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct wrgg03_header hdr;
|
||||
size_t hdr_len, retlen, kernel_ent_size;
|
|
@ -1,552 +0,0 @@
|
|||
--- a/drivers/net/phy/adm6996.c
|
||||
+++ b/drivers/net/phy/adm6996.c
|
||||
@@ -289,7 +289,7 @@ static u16
|
||||
adm6996_read_mii_reg(struct adm6996_priv *priv, enum admreg reg)
|
||||
{
|
||||
struct phy_device *phydev = priv->priv;
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
+ struct mii_bus *bus = phydev->bus;
|
||||
|
||||
return bus->read(bus, PHYADDR(reg));
|
||||
}
|
||||
@@ -298,7 +298,7 @@ static void
|
||||
adm6996_write_mii_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
|
||||
{
|
||||
struct phy_device *phydev = priv->priv;
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
+ struct mii_bus *bus = phydev->bus;
|
||||
|
||||
bus->write(bus, PHYADDR(reg), val);
|
||||
}
|
||||
@@ -1050,13 +1050,13 @@ static int adm6996_config_init(struct ph
|
||||
pdev->supported = ADVERTISED_100baseT_Full;
|
||||
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||
|
||||
- if (pdev->mdio.addr != 0) {
|
||||
+ if (pdev->addr != 0) {
|
||||
pr_info ("%s: PHY overlaps ADM6996, providing fixed PHY 0x%x.\n"
|
||||
- , pdev->attached_dev->name, pdev->mdio.addr);
|
||||
+ , pdev->attached_dev->name, pdev->addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
- priv = devm_kzalloc(&pdev->mdio.dev, sizeof(struct adm6996_priv), GFP_KERNEL);
|
||||
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct adm6996_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1076,7 +1076,7 @@ static int adm6996_config_init(struct ph
|
||||
}
|
||||
|
||||
/*
|
||||
- * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
|
||||
+ * Warning: phydev->priv is NULL if phydev->addr != 0
|
||||
*/
|
||||
static int adm6996_read_status(struct phy_device *phydev)
|
||||
{
|
||||
@@ -1092,7 +1092,7 @@ static int adm6996_read_status(struct ph
|
||||
}
|
||||
|
||||
/*
|
||||
- * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
|
||||
+ * Warning: phydev->priv is NULL if phydev->addr != 0
|
||||
*/
|
||||
static int adm6996_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
@@ -1101,11 +1101,11 @@ static int adm6996_config_aneg(struct ph
|
||||
|
||||
static int adm6996_fixup(struct phy_device *dev)
|
||||
{
|
||||
- struct mii_bus *bus = dev->mdio.bus;
|
||||
+ struct mii_bus *bus = dev->bus;
|
||||
u16 reg;
|
||||
|
||||
/* Our custom registers are at PHY addresses 0-10. Claim those. */
|
||||
- if (dev->mdio.addr > 10)
|
||||
+ if (dev->addr > 10)
|
||||
return 0;
|
||||
|
||||
/* look for the switch on the bus */
|
||||
@@ -1152,6 +1152,7 @@ static struct phy_driver adm6996_phy_dri
|
||||
.config_aneg = &adm6996_config_aneg,
|
||||
.read_status = &adm6996_read_status,
|
||||
.soft_reset = adm6996_soft_reset,
|
||||
+ .driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
static int adm6996_gpio_probe(struct platform_device *pdev)
|
||||
@@ -1220,7 +1221,7 @@ static int __init adm6996_init(void)
|
||||
int err;
|
||||
|
||||
phy_register_fixup_for_id(PHY_ANY_ID, adm6996_fixup);
|
||||
- err = phy_driver_register(&adm6996_phy_driver, THIS_MODULE);
|
||||
+ err = phy_driver_register(&adm6996_phy_driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
--- a/drivers/net/phy/ar8216.c
|
||||
+++ b/drivers/net/phy/ar8216.c
|
||||
@@ -177,7 +177,7 @@ ar8xxx_phy_check_aneg(struct phy_device
|
||||
if (ret & BMCR_ANENABLE)
|
||||
return 0;
|
||||
|
||||
- dev_info(&phydev->mdio.dev, "ANEG disabled, re-enabling ...\n");
|
||||
+ dev_info(&phydev->dev, "ANEG disabled, re-enabling ...\n");
|
||||
ret |= BMCR_ANENABLE | BMCR_ANRESTART;
|
||||
return phy_write(phydev, MII_BMCR, ret);
|
||||
}
|
||||
@@ -2007,7 +2007,7 @@ ar8xxx_phy_config_init(struct phy_device
|
||||
|
||||
priv->phy = phydev;
|
||||
|
||||
- if (phydev->mdio.addr != 0) {
|
||||
+ if (phydev->addr != 0) {
|
||||
if (chip_is_ar8316(priv)) {
|
||||
/* switch device has been initialized, reinit */
|
||||
priv->dev.ports = (AR8216_NUM_PORTS - 1);
|
||||
@@ -2055,7 +2055,7 @@ ar8xxx_check_link_states(struct ar8xxx_p
|
||||
/* flush ARL entries for this port if it went down*/
|
||||
if (!link_new)
|
||||
priv->chip->atu_flush_port(priv, i);
|
||||
- dev_info(&priv->phy->mdio.dev, "Port %d is %s\n",
|
||||
+ dev_info(&priv->phy->dev, "Port %d is %s\n",
|
||||
i, link_new ? "up" : "down");
|
||||
}
|
||||
|
||||
@@ -2074,10 +2074,10 @@ ar8xxx_phy_read_status(struct phy_device
|
||||
if (phydev->state == PHY_CHANGELINK)
|
||||
ar8xxx_check_link_states(priv);
|
||||
|
||||
- if (phydev->mdio.addr != 0)
|
||||
+ if (phydev->addr != 0)
|
||||
return genphy_read_status(phydev);
|
||||
|
||||
- ar8216_read_port_link(priv, phydev->mdio.addr, &link);
|
||||
+ ar8216_read_port_link(priv, phydev->addr, &link);
|
||||
phydev->link = !!link.link;
|
||||
if (!phydev->link)
|
||||
return 0;
|
||||
@@ -2107,7 +2107,7 @@ ar8xxx_phy_read_status(struct phy_device
|
||||
static int
|
||||
ar8xxx_phy_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
- if (phydev->mdio.addr == 0)
|
||||
+ if (phydev->addr == 0)
|
||||
return 0;
|
||||
|
||||
return genphy_config_aneg(phydev);
|
||||
@@ -2162,15 +2162,15 @@ ar8xxx_phy_probe(struct phy_device *phyd
|
||||
int ret;
|
||||
|
||||
/* skip PHYs at unused adresses */
|
||||
- if (phydev->mdio.addr != 0 && phydev->mdio.addr != 4)
|
||||
+ if (phydev->addr != 0 && phydev->addr != 4)
|
||||
return -ENODEV;
|
||||
|
||||
- if (!ar8xxx_is_possible(phydev->mdio.bus))
|
||||
+ if (!ar8xxx_is_possible(phydev->bus))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&ar8xxx_dev_list_lock);
|
||||
list_for_each_entry(priv, &ar8xxx_dev_list, list)
|
||||
- if (priv->mii_bus == phydev->mdio.bus)
|
||||
+ if (priv->mii_bus == phydev->bus)
|
||||
goto found;
|
||||
|
||||
priv = ar8xxx_create();
|
||||
@@ -2179,7 +2179,7 @@ ar8xxx_phy_probe(struct phy_device *phyd
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
- priv->mii_bus = phydev->mdio.bus;
|
||||
+ priv->mii_bus = phydev->bus;
|
||||
|
||||
ret = ar8xxx_probe_switch(priv);
|
||||
if (ret)
|
||||
@@ -2200,7 +2200,7 @@ ar8xxx_phy_probe(struct phy_device *phyd
|
||||
found:
|
||||
priv->use_count++;
|
||||
|
||||
- if (phydev->mdio.addr == 0) {
|
||||
+ if (phydev->addr == 0) {
|
||||
if (ar8xxx_has_gige(priv)) {
|
||||
phydev->supported = SUPPORTED_1000baseT_Full;
|
||||
phydev->advertising = ADVERTISED_1000baseT_Full;
|
||||
@@ -2288,21 +2288,33 @@ ar8xxx_phy_soft_reset(struct phy_device
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static struct phy_driver ar8xxx_phy_driver[] = {
|
||||
- {
|
||||
- .phy_id = 0x004d0000,
|
||||
- .name = "Atheros AR8216/AR8236/AR8316",
|
||||
- .phy_id_mask = 0xffff0000,
|
||||
- .features = PHY_BASIC_FEATURES,
|
||||
- .probe = ar8xxx_phy_probe,
|
||||
- .remove = ar8xxx_phy_remove,
|
||||
- .detach = ar8xxx_phy_detach,
|
||||
- .config_init = ar8xxx_phy_config_init,
|
||||
- .config_aneg = ar8xxx_phy_config_aneg,
|
||||
- .read_status = ar8xxx_phy_read_status,
|
||||
- .soft_reset = ar8xxx_phy_soft_reset,
|
||||
- }
|
||||
+static struct phy_driver ar8xxx_phy_driver = {
|
||||
+ .phy_id = 0x004d0000,
|
||||
+ .name = "Atheros AR8216/AR8236/AR8316",
|
||||
+ .phy_id_mask = 0xffff0000,
|
||||
+ .features = PHY_BASIC_FEATURES,
|
||||
+ .probe = ar8xxx_phy_probe,
|
||||
+ .remove = ar8xxx_phy_remove,
|
||||
+ .detach = ar8xxx_phy_detach,
|
||||
+ .config_init = ar8xxx_phy_config_init,
|
||||
+ .config_aneg = ar8xxx_phy_config_aneg,
|
||||
+ .read_status = ar8xxx_phy_read_status,
|
||||
+ .soft_reset = ar8xxx_phy_soft_reset,
|
||||
+ .driver = { .owner = THIS_MODULE },
|
||||
};
|
||||
|
||||
-module_phy_driver(ar8xxx_phy_driver);
|
||||
+int __init
|
||||
+ar8xxx_init(void)
|
||||
+{
|
||||
+ return phy_driver_register(&ar8xxx_phy_driver);
|
||||
+}
|
||||
+
|
||||
+void __exit
|
||||
+ar8xxx_exit(void)
|
||||
+{
|
||||
+ phy_driver_unregister(&ar8xxx_phy_driver);
|
||||
+}
|
||||
+
|
||||
+module_init(ar8xxx_init);
|
||||
+module_exit(ar8xxx_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
--- a/drivers/net/phy/ar8327.c
|
||||
+++ b/drivers/net/phy/ar8327.c
|
||||
@@ -627,11 +627,11 @@ ar8327_hw_init(struct ar8xxx_priv *priv)
|
||||
if (!priv->chip_data)
|
||||
return -ENOMEM;
|
||||
|
||||
- if (priv->phy->mdio.dev.of_node)
|
||||
- ret = ar8327_hw_config_of(priv, priv->phy->mdio.dev.of_node);
|
||||
+ if (priv->phy->dev.of_node)
|
||||
+ ret = ar8327_hw_config_of(priv, priv->phy->dev.of_node);
|
||||
else
|
||||
ret = ar8327_hw_config_pdata(priv,
|
||||
- priv->phy->mdio.dev.platform_data);
|
||||
+ priv->phy->dev.platform_data);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
--- a/drivers/net/phy/ip17xx.c
|
||||
+++ b/drivers/net/phy/ip17xx.c
|
||||
@@ -1273,7 +1273,7 @@ static int ip17xx_probe(struct phy_devic
|
||||
int err;
|
||||
|
||||
/* We only attach to PHY 0, but use all available PHYs */
|
||||
- if (pdev->mdio.addr != 0)
|
||||
+ if (pdev->addr != 0)
|
||||
return -ENODEV;
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
@@ -1283,7 +1283,7 @@ static int ip17xx_probe(struct phy_devic
|
||||
dev = &state->dev;
|
||||
|
||||
pdev->priv = state;
|
||||
- state->mii_bus = pdev->mdio.bus;
|
||||
+ state->mii_bus = pdev->bus;
|
||||
|
||||
err = get_model(state);
|
||||
if (err < 0)
|
||||
@@ -1295,7 +1295,7 @@ static int ip17xx_probe(struct phy_devic
|
||||
dev->name = state->regs->NAME;
|
||||
dev->ops = &ip17xx_ops;
|
||||
|
||||
- pr_info("IP17xx: Found %s at %s\n", dev->name, dev_name(&pdev->mdio.dev));
|
||||
+ pr_info("IP17xx: Found %s at %s\n", dev->name, dev_name(&pdev->dev));
|
||||
return 0;
|
||||
|
||||
error:
|
||||
@@ -1353,25 +1353,58 @@ static int ip17xx_read_status(struct phy
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static struct phy_driver ip17xx_driver[] = {
|
||||
- {
|
||||
- .name = "IC+ IP17xx",
|
||||
- .phy_id = 0x02430c00,
|
||||
- .phy_id_mask = 0x0ffffc00,
|
||||
- .features = PHY_BASIC_FEATURES,
|
||||
- .probe = ip17xx_probe,
|
||||
- .remove = ip17xx_remove,
|
||||
- .config_init = ip17xx_config_init,
|
||||
- .config_aneg = ip17xx_config_aneg,
|
||||
- .aneg_done = ip17xx_aneg_done,
|
||||
- .update_link = ip17xx_update_link,
|
||||
- .read_status = ip17xx_read_status,
|
||||
- }
|
||||
+static struct phy_driver ip17xx_driver = {
|
||||
+ .name = "IC+ IP17xx",
|
||||
+ .phy_id = 0x02430c00,
|
||||
+ .phy_id_mask = 0x0ffffc00,
|
||||
+ .features = PHY_BASIC_FEATURES,
|
||||
+ .probe = ip17xx_probe,
|
||||
+ .remove = ip17xx_remove,
|
||||
+ .config_init = ip17xx_config_init,
|
||||
+ .config_aneg = ip17xx_config_aneg,
|
||||
+ .aneg_done = ip17xx_aneg_done,
|
||||
+ .update_link = ip17xx_update_link,
|
||||
+ .read_status = ip17xx_read_status,
|
||||
+ .driver = { .owner = THIS_MODULE },
|
||||
};
|
||||
|
||||
-module_phy_driver(ip17xx_driver);
|
||||
+static struct phy_driver ip175a_driver = {
|
||||
+ .name = "IC+ IP175A",
|
||||
+ .phy_id = 0x02430c50,
|
||||
+ .phy_id_mask = 0x0ffffff0,
|
||||
+ .features = PHY_BASIC_FEATURES,
|
||||
+ .probe = ip17xx_probe,
|
||||
+ .remove = ip17xx_remove,
|
||||
+ .config_init = ip17xx_config_init,
|
||||
+ .config_aneg = ip17xx_config_aneg,
|
||||
+ .aneg_done = ip17xx_aneg_done,
|
||||
+ .update_link = ip17xx_update_link,
|
||||
+ .read_status = ip17xx_read_status,
|
||||
+ .driver = { .owner = THIS_MODULE },
|
||||
+};
|
||||
+
|
||||
+
|
||||
+int __init ip17xx_init(void)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = phy_driver_register(&ip175a_driver);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return phy_driver_register(&ip17xx_driver);
|
||||
+}
|
||||
+
|
||||
+void __exit ip17xx_exit(void)
|
||||
+{
|
||||
+ phy_driver_unregister(&ip17xx_driver);
|
||||
+ phy_driver_unregister(&ip175a_driver);
|
||||
+}
|
||||
|
||||
MODULE_AUTHOR("Patrick Horn <patrick.horn@gmail.com>");
|
||||
MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
|
||||
MODULE_AUTHOR("Martin Mares <mj@ucw.cz>");
|
||||
MODULE_LICENSE("GPL");
|
||||
+
|
||||
+module_init(ip17xx_init);
|
||||
+module_exit(ip17xx_exit);
|
||||
--- a/drivers/net/phy/mvswitch.c
|
||||
+++ b/drivers/net/phy/mvswitch.c
|
||||
@@ -50,17 +50,13 @@ struct mvswitch_priv {
|
||||
static inline u16
|
||||
r16(struct phy_device *phydev, int addr, int reg)
|
||||
{
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
-
|
||||
- return bus->read(bus, addr, reg);
|
||||
+ return phydev->bus->read(phydev->bus, addr, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
w16(struct phy_device *phydev, int addr, int reg, u16 val)
|
||||
{
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
-
|
||||
- bus->write(bus, addr, reg, val);
|
||||
+ phydev->bus->write(phydev->bus, addr, reg, val);
|
||||
}
|
||||
|
||||
|
||||
@@ -398,13 +394,12 @@ mvswitch_probe(struct phy_device *pdev)
|
||||
static int
|
||||
mvswitch_fixup(struct phy_device *dev)
|
||||
{
|
||||
- struct mii_bus *bus = dev->mdio.bus;
|
||||
u16 reg;
|
||||
|
||||
- if (dev->mdio.addr != 0x10)
|
||||
+ if (dev->addr != 0x10)
|
||||
return 0;
|
||||
|
||||
- reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||
+ reg = dev->bus->read(dev->bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||
if (reg != MV_IDENT_VALUE)
|
||||
return 0;
|
||||
|
||||
@@ -425,13 +420,14 @@ static struct phy_driver mvswitch_driver
|
||||
.config_aneg = &mvswitch_config_aneg,
|
||||
.aneg_done = &mvswitch_aneg_done,
|
||||
.read_status = &mvswitch_read_status,
|
||||
+ .driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
static int __init
|
||||
mvswitch_init(void)
|
||||
{
|
||||
phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup);
|
||||
- return phy_driver_register(&mvswitch_driver, THIS_MODULE);
|
||||
+ return phy_driver_register(&mvswitch_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
--- a/drivers/net/phy/psb6970.c
|
||||
+++ b/drivers/net/phy/psb6970.c
|
||||
@@ -70,16 +70,12 @@ struct psb6970_priv {
|
||||
|
||||
static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
|
||||
{
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
-
|
||||
- return bus->read(bus, PHYADDR(reg));
|
||||
+ return phydev->bus->read(phydev->bus, PHYADDR(reg));
|
||||
}
|
||||
|
||||
static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
|
||||
{
|
||||
- struct mii_bus *bus = phydev->mdio.bus;
|
||||
-
|
||||
- bus->write(bus, PHYADDR(reg), val);
|
||||
+ phydev->bus->write(phydev->bus, PHYADDR(reg), val);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -316,11 +312,11 @@ static int psb6970_config_init(struct ph
|
||||
|
||||
priv->phy = pdev;
|
||||
|
||||
- if (pdev->mdio.addr == 0)
|
||||
+ if (pdev->addr == 0)
|
||||
printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
|
||||
pdev->attached_dev->name);
|
||||
|
||||
- if (pdev->mdio.addr != 0) {
|
||||
+ if (pdev->addr != 0) {
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
@@ -388,14 +384,14 @@ static void psb6970_remove(struct phy_de
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
- if (pdev->mdio.addr == 0)
|
||||
+ if (pdev->addr == 0)
|
||||
unregister_switch(&priv->dev);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static int psb6970_fixup(struct phy_device *dev)
|
||||
{
|
||||
- struct mii_bus *bus = dev->mdio.bus;
|
||||
+ struct mii_bus *bus = dev->bus;
|
||||
u16 reg;
|
||||
|
||||
/* look for the switch on the bus */
|
||||
@@ -419,12 +415,13 @@ static struct phy_driver psb6970_driver
|
||||
.config_init = &psb6970_config_init,
|
||||
.config_aneg = &psb6970_config_aneg,
|
||||
.read_status = &psb6970_read_status,
|
||||
+ .driver = {.owner = THIS_MODULE},
|
||||
};
|
||||
|
||||
int __init psb6970_init(void)
|
||||
{
|
||||
phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
|
||||
- return phy_driver_register(&psb6970_driver, THIS_MODULE);
|
||||
+ return phy_driver_register(&psb6970_driver);
|
||||
}
|
||||
|
||||
module_init(psb6970_init);
|
||||
--- a/drivers/net/phy/rtl8306.c
|
||||
+++ b/drivers/net/phy/rtl8306.c
|
||||
@@ -877,7 +877,7 @@ rtl8306_config_init(struct phy_device *p
|
||||
int err;
|
||||
|
||||
/* Only init the switch for the primary PHY */
|
||||
- if (pdev->mdio.addr != 0)
|
||||
+ if (pdev->addr != 0)
|
||||
return 0;
|
||||
|
||||
val.value.i = 1;
|
||||
@@ -887,7 +887,7 @@ rtl8306_config_init(struct phy_device *p
|
||||
priv->dev.ops = &rtl8306_ops;
|
||||
priv->do_cpu = 0;
|
||||
priv->page = -1;
|
||||
- priv->bus = pdev->mdio.bus;
|
||||
+ priv->bus = pdev->bus;
|
||||
|
||||
chipid = rtl_get(dev, RTL_REG_CHIPID);
|
||||
chipver = rtl_get(dev, RTL_REG_CHIPVER);
|
||||
@@ -933,13 +933,13 @@ rtl8306_fixup(struct phy_device *pdev)
|
||||
u16 chipid;
|
||||
|
||||
/* Attach to primary LAN port and WAN port */
|
||||
- if (pdev->mdio.addr != 0 && pdev->mdio.addr != 4)
|
||||
+ if (pdev->addr != 0 && pdev->addr != 4)
|
||||
return 0;
|
||||
|
||||
memset(&priv, 0, sizeof(priv));
|
||||
priv.fixup = true;
|
||||
priv.page = -1;
|
||||
- priv.bus = pdev->mdio.bus;
|
||||
+ priv.bus = pdev->bus;
|
||||
chipid = rtl_get(&priv.dev, RTL_REG_CHIPID);
|
||||
if (chipid == 0x5988)
|
||||
pdev->phy_id = RTL8306_MAGIC;
|
||||
@@ -957,14 +957,14 @@ rtl8306_probe(struct phy_device *pdev)
|
||||
* share one rtl_priv instance between virtual phy
|
||||
* devices on the same bus
|
||||
*/
|
||||
- if (priv->bus == pdev->mdio.bus)
|
||||
+ if (priv->bus == pdev->bus)
|
||||
goto found;
|
||||
}
|
||||
priv = kzalloc(sizeof(struct rtl_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
- priv->bus = pdev->mdio.bus;
|
||||
+ priv->bus = pdev->bus;
|
||||
|
||||
found:
|
||||
pdev->priv = priv;
|
||||
@@ -985,7 +985,7 @@ rtl8306_config_aneg(struct phy_device *p
|
||||
struct rtl_priv *priv = pdev->priv;
|
||||
|
||||
/* Only for WAN */
|
||||
- if (pdev->mdio.addr == 0)
|
||||
+ if (pdev->addr == 0)
|
||||
return 0;
|
||||
|
||||
/* Restart autonegotiation */
|
||||
@@ -1001,7 +1001,7 @@ rtl8306_read_status(struct phy_device *p
|
||||
struct rtl_priv *priv = pdev->priv;
|
||||
struct switch_dev *dev = &priv->dev;
|
||||
|
||||
- if (pdev->mdio.addr == 4) {
|
||||
+ if (pdev->addr == 4) {
|
||||
/* WAN */
|
||||
pdev->speed = rtl_get(dev, RTL_PORT_REG(4, SPEED)) ? SPEED_100 : SPEED_10;
|
||||
pdev->duplex = rtl_get(dev, RTL_PORT_REG(4, DUPLEX)) ? DUPLEX_FULL : DUPLEX_HALF;
|
||||
@@ -1044,6 +1044,7 @@ static struct phy_driver rtl8306_driver
|
||||
.config_init = &rtl8306_config_init,
|
||||
.config_aneg = &rtl8306_config_aneg,
|
||||
.read_status = &rtl8306_read_status,
|
||||
+ .driver = { .owner = THIS_MODULE,},
|
||||
};
|
||||
|
||||
|
||||
@@ -1051,7 +1052,7 @@ static int __init
|
||||
rtl_init(void)
|
||||
{
|
||||
phy_register_fixup_for_id(PHY_ANY_ID, rtl8306_fixup);
|
||||
- return phy_driver_register(&rtl8306_driver, THIS_MODULE);
|
||||
+ return phy_driver_register(&rtl8306_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
|
@ -1,11 +0,0 @@
|
|||
--- a/drivers/mtd/myloader.c
|
||||
+++ b/drivers/mtd/myloader.c
|
||||
@@ -33,7 +33,7 @@ struct part_data {
|
||||
};
|
||||
|
||||
static int myloader_parse_partitions(struct mtd_info *master,
|
||||
- const struct mtd_partition **pparts,
|
||||
+ struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct part_data *buf;
|
|
@ -1,134 +0,0 @@
|
|||
--- a/drivers/ssb/pcihost_wrapper.c
|
||||
+++ b/drivers/ssb/pcihost_wrapper.c
|
||||
@@ -11,15 +11,17 @@
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
|
||||
+#include <linux/pm.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ssb/ssb.h>
|
||||
|
||||
|
||||
-#ifdef CONFIG_PM
|
||||
-static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
|
||||
+#ifdef CONFIG_PM_SLEEP
|
||||
+static int ssb_pcihost_suspend(struct device *d)
|
||||
{
|
||||
+ struct pci_dev *dev = to_pci_dev(d);
|
||||
struct ssb_bus *ssb = pci_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
@@ -28,17 +30,23 @@ static int ssb_pcihost_suspend(struct pc
|
||||
return err;
|
||||
pci_save_state(dev);
|
||||
pci_disable_device(dev);
|
||||
- pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
+
|
||||
+ /* if there is a wakeup enabled child device on ssb bus,
|
||||
+ enable pci wakeup posibility. */
|
||||
+ device_set_wakeup_enable(d, d->power.wakeup_path);
|
||||
+
|
||||
+ pci_prepare_to_sleep(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int ssb_pcihost_resume(struct pci_dev *dev)
|
||||
+static int ssb_pcihost_resume(struct device *d)
|
||||
{
|
||||
+ struct pci_dev *dev = to_pci_dev(d);
|
||||
struct ssb_bus *ssb = pci_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
- pci_set_power_state(dev, PCI_D0);
|
||||
+ pci_back_from_sleep(dev);
|
||||
err = pci_enable_device(dev);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -49,10 +57,12 @@ static int ssb_pcihost_resume(struct pci
|
||||
|
||||
return 0;
|
||||
}
|
||||
-#else /* CONFIG_PM */
|
||||
-# define ssb_pcihost_suspend NULL
|
||||
-# define ssb_pcihost_resume NULL
|
||||
-#endif /* CONFIG_PM */
|
||||
+
|
||||
+static const struct dev_pm_ops ssb_pcihost_pm_ops = {
|
||||
+ SET_SYSTEM_SLEEP_PM_OPS(ssb_pcihost_suspend, ssb_pcihost_resume)
|
||||
+};
|
||||
+
|
||||
+#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int ssb_pcihost_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
@@ -115,8 +125,9 @@ int ssb_pcihost_register(struct pci_driv
|
||||
{
|
||||
driver->probe = ssb_pcihost_probe;
|
||||
driver->remove = ssb_pcihost_remove;
|
||||
- driver->suspend = ssb_pcihost_suspend;
|
||||
- driver->resume = ssb_pcihost_resume;
|
||||
+#ifdef CONFIG_PM_SLEEP
|
||||
+ driver->driver.pm = &ssb_pcihost_pm_ops;
|
||||
+#endif
|
||||
|
||||
return pci_register_driver(driver);
|
||||
}
|
||||
--- a/drivers/ssb/driver_pcicore.c
|
||||
+++ b/drivers/ssb/driver_pcicore.c
|
||||
@@ -357,6 +357,16 @@ static void ssb_pcicore_init_hostmode(st
|
||||
pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
|
||||
SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
|
||||
|
||||
+ /*
|
||||
+ * Accessing PCI config without a proper delay after devices reset (not
|
||||
+ * GPIO reset) was causing reboots on WRT300N v1.0 (BCM4704).
|
||||
+ * Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it
|
||||
+ * completely. Flushing all writes was also tested but with no luck.
|
||||
+ * The same problem was reported for WRT350N v1 (BCM4705), so we just
|
||||
+ * sleep here unconditionally.
|
||||
+ */
|
||||
+ usleep_range(1000, 2000);
|
||||
+
|
||||
/* Enable PCI bridge BAR0 prefetch and burst */
|
||||
val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
|
||||
ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
|
||||
--- a/drivers/ssb/main.c
|
||||
+++ b/drivers/ssb/main.c
|
||||
@@ -90,25 +90,6 @@ found:
|
||||
}
|
||||
#endif /* CONFIG_SSB_PCMCIAHOST */
|
||||
|
||||
-#ifdef CONFIG_SSB_SDIOHOST
|
||||
-struct ssb_bus *ssb_sdio_func_to_bus(struct sdio_func *func)
|
||||
-{
|
||||
- struct ssb_bus *bus;
|
||||
-
|
||||
- ssb_buses_lock();
|
||||
- list_for_each_entry(bus, &buses, list) {
|
||||
- if (bus->bustype == SSB_BUSTYPE_SDIO &&
|
||||
- bus->host_sdio == func)
|
||||
- goto found;
|
||||
- }
|
||||
- bus = NULL;
|
||||
-found:
|
||||
- ssb_buses_unlock();
|
||||
-
|
||||
- return bus;
|
||||
-}
|
||||
-#endif /* CONFIG_SSB_SDIOHOST */
|
||||
-
|
||||
int ssb_for_each_bus_call(unsigned long data,
|
||||
int (*func)(struct ssb_bus *bus, unsigned long data))
|
||||
{
|
||||
@@ -1154,6 +1135,8 @@ static u32 ssb_tmslow_reject_bitmask(str
|
||||
case SSB_IDLOW_SSBREV_25: /* TODO - find the proper REJECT bit */
|
||||
case SSB_IDLOW_SSBREV_27: /* same here */
|
||||
return SSB_TMSLOW_REJECT; /* this is a guess */
|
||||
+ case SSB_IDLOW_SSBREV:
|
||||
+ break;
|
||||
default:
|
||||
WARN(1, KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
--- a/include/linux/ssb/ssb.h
|
||||
+++ b/include/linux/ssb/ssb.h
|
||||
@@ -29,10 +29,13 @@ struct ssb_sprom {
|
||||
u8 il0mac[6] __aligned(sizeof(u16)); /* MAC address for 802.11b/g */
|
||||
u8 et0mac[6] __aligned(sizeof(u16)); /* MAC address for Ethernet */
|
||||
u8 et1mac[6] __aligned(sizeof(u16)); /* MAC address for 802.11a */
|
||||
+ u8 et2mac[6] __aligned(sizeof(u16)); /* MAC address for extra Ethernet */
|
||||
u8 et0phyaddr; /* MII address for enet0 */
|
||||
u8 et1phyaddr; /* MII address for enet1 */
|
||||
+ u8 et2phyaddr; /* MII address for enet2 */
|
||||
u8 et0mdcport; /* MDIO for enet0 */
|
||||
u8 et1mdcport; /* MDIO for enet1 */
|
||||
+ u8 et2mdcport; /* MDIO for enet2 */
|
||||
u16 dev_id; /* Device ID overriding e.g. PCI ID */
|
||||
u16 board_rev; /* Board revision number from SPROM. */
|
||||
u16 board_num; /* Board number from SPROM. */
|
||||
@@ -88,11 +91,14 @@ struct ssb_sprom {
|
||||
u32 ofdm5glpo; /* 5.2GHz OFDM power offset */
|
||||
u32 ofdm5gpo; /* 5.3GHz OFDM power offset */
|
||||
u32 ofdm5ghpo; /* 5.8GHz OFDM power offset */
|
||||
+ u32 boardflags;
|
||||
+ u32 boardflags2;
|
||||
+ u32 boardflags3;
|
||||
+ /* TODO: Switch all drivers to new u32 fields and drop below ones */
|
||||
u16 boardflags_lo; /* Board flags (bits 0-15) */
|
||||
u16 boardflags_hi; /* Board flags (bits 16-31) */
|
||||
u16 boardflags2_lo; /* Board flags (bits 32-47) */
|
||||
u16 boardflags2_hi; /* Board flags (bits 48-63) */
|
||||
- /* TODO store board flags in a single u64 */
|
||||
|
||||
struct ssb_sprom_core_pwr_info core_pwr_info[4];
|
||||
|
|
@ -1,286 +0,0 @@
|
|||
--- a/drivers/bcma/bcma_private.h
|
||||
+++ b/drivers/bcma/bcma_private.h
|
||||
@@ -22,6 +22,7 @@ struct bcma_bus;
|
||||
/* main.c */
|
||||
bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value,
|
||||
int timeout);
|
||||
+void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core);
|
||||
int bcma_bus_register(struct bcma_bus *bus);
|
||||
void bcma_bus_unregister(struct bcma_bus *bus);
|
||||
int __init bcma_bus_early_register(struct bcma_bus *bus,
|
||||
--- a/drivers/bcma/driver_chipcommon.c
|
||||
+++ b/drivers/bcma/driver_chipcommon.c
|
||||
@@ -339,7 +339,7 @@ void bcma_chipco_serial_init(struct bcma
|
||||
return;
|
||||
}
|
||||
|
||||
- irq = bcma_core_irq(cc->core);
|
||||
+ irq = bcma_core_irq(cc->core, 0);
|
||||
|
||||
/* Determine the registers of the UARTs */
|
||||
cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
|
||||
--- a/drivers/bcma/driver_gpio.c
|
||||
+++ b/drivers/bcma/driver_gpio.c
|
||||
@@ -152,7 +152,7 @@ static int bcma_gpio_irq_domain_init(str
|
||||
handle_simple_irq);
|
||||
}
|
||||
|
||||
- hwirq = bcma_core_irq(cc->core);
|
||||
+ hwirq = bcma_core_irq(cc->core, 0);
|
||||
err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio",
|
||||
cc);
|
||||
if (err)
|
||||
@@ -183,7 +183,7 @@ static void bcma_gpio_irq_domain_exit(st
|
||||
return;
|
||||
|
||||
bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO);
|
||||
- free_irq(bcma_core_irq(cc->core), cc);
|
||||
+ free_irq(bcma_core_irq(cc->core, 0), cc);
|
||||
for (gpio = 0; gpio < chip->ngpio; gpio++) {
|
||||
int irq = irq_find_mapping(cc->irq_domain, gpio);
|
||||
|
||||
--- a/drivers/bcma/driver_mips.c
|
||||
+++ b/drivers/bcma/driver_mips.c
|
||||
@@ -115,7 +115,7 @@ static u32 bcma_core_mips_irqflag(struct
|
||||
* If disabled, 5 is returned.
|
||||
* If not supported, 6 is returned.
|
||||
*/
|
||||
-static unsigned int bcma_core_mips_irq(struct bcma_device *dev)
|
||||
+unsigned int bcma_core_mips_irq(struct bcma_device *dev)
|
||||
{
|
||||
struct bcma_device *mdev = dev->bus->drv_mips.core;
|
||||
u32 irqflag;
|
||||
@@ -133,13 +133,6 @@ static unsigned int bcma_core_mips_irq(s
|
||||
return 5;
|
||||
}
|
||||
|
||||
-unsigned int bcma_core_irq(struct bcma_device *dev)
|
||||
-{
|
||||
- unsigned int mips_irq = bcma_core_mips_irq(dev);
|
||||
- return mips_irq <= 4 ? mips_irq + 2 : 0;
|
||||
-}
|
||||
-EXPORT_SYMBOL(bcma_core_irq);
|
||||
-
|
||||
static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
|
||||
{
|
||||
unsigned int oldirq = bcma_core_mips_irq(dev);
|
||||
@@ -423,7 +416,7 @@ void bcma_core_mips_init(struct bcma_drv
|
||||
break;
|
||||
default:
|
||||
list_for_each_entry(core, &bus->cores, list) {
|
||||
- core->irq = bcma_core_irq(core);
|
||||
+ core->irq = bcma_core_irq(core, 0);
|
||||
}
|
||||
bcma_err(bus,
|
||||
"Unknown device (0x%x) found, can not configure IRQs\n",
|
||||
--- a/drivers/bcma/driver_pci_host.c
|
||||
+++ b/drivers/bcma/driver_pci_host.c
|
||||
@@ -593,7 +593,7 @@ int bcma_core_pci_plat_dev_init(struct p
|
||||
pr_info("PCI: Fixing up device %s\n", pci_name(dev));
|
||||
|
||||
/* Fix up interrupt lines */
|
||||
- dev->irq = bcma_core_irq(pc_host->pdev->core);
|
||||
+ dev->irq = bcma_core_irq(pc_host->pdev->core, 0);
|
||||
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
|
||||
|
||||
readrq = pcie_get_readrq(dev);
|
||||
@@ -617,6 +617,6 @@ int bcma_core_pci_pcibios_map_irq(const
|
||||
|
||||
pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host,
|
||||
pci_ops);
|
||||
- return bcma_core_irq(pc_host->pdev->core);
|
||||
+ return bcma_core_irq(pc_host->pdev->core, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(bcma_core_pci_pcibios_map_irq);
|
||||
--- a/drivers/bcma/main.c
|
||||
+++ b/drivers/bcma/main.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_address.h>
|
||||
+#include <linux/of_irq.h>
|
||||
|
||||
MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -153,6 +154,46 @@ static struct device_node *bcma_of_find_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+static int bcma_of_irq_parse(struct platform_device *parent,
|
||||
+ struct bcma_device *core,
|
||||
+ struct of_phandle_args *out_irq, int num)
|
||||
+{
|
||||
+ __be32 laddr[1];
|
||||
+ int rc;
|
||||
+
|
||||
+ if (core->dev.of_node) {
|
||||
+ rc = of_irq_parse_one(core->dev.of_node, num, out_irq);
|
||||
+ if (!rc)
|
||||
+ return rc;
|
||||
+ }
|
||||
+
|
||||
+ out_irq->np = parent->dev.of_node;
|
||||
+ out_irq->args_count = 1;
|
||||
+ out_irq->args[0] = num;
|
||||
+
|
||||
+ laddr[0] = cpu_to_be32(core->addr);
|
||||
+ return of_irq_parse_raw(laddr, out_irq);
|
||||
+}
|
||||
+
|
||||
+static unsigned int bcma_of_get_irq(struct platform_device *parent,
|
||||
+ struct bcma_device *core, int num)
|
||||
+{
|
||||
+ struct of_phandle_args out_irq;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!parent || !parent->dev.of_node)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = bcma_of_irq_parse(parent, core, &out_irq, num);
|
||||
+ if (ret) {
|
||||
+ bcma_debug(core->bus, "bcma_of_get_irq() failed with rc=%d\n",
|
||||
+ ret);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ return irq_create_of_mapping(&out_irq);
|
||||
+}
|
||||
+
|
||||
static void bcma_of_fill_device(struct platform_device *parent,
|
||||
struct bcma_device *core)
|
||||
{
|
||||
@@ -161,18 +202,47 @@ static void bcma_of_fill_device(struct p
|
||||
node = bcma_of_find_child_device(parent, core);
|
||||
if (node)
|
||||
core->dev.of_node = node;
|
||||
+
|
||||
+ core->irq = bcma_of_get_irq(parent, core, 0);
|
||||
}
|
||||
#else
|
||||
static void bcma_of_fill_device(struct platform_device *parent,
|
||||
struct bcma_device *core)
|
||||
{
|
||||
}
|
||||
+static inline unsigned int bcma_of_get_irq(struct platform_device *parent,
|
||||
+ struct bcma_device *core, int num)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
-static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
|
||||
+unsigned int bcma_core_irq(struct bcma_device *core, int num)
|
||||
{
|
||||
- int err;
|
||||
+ struct bcma_bus *bus = core->bus;
|
||||
+ unsigned int mips_irq;
|
||||
+
|
||||
+ switch (bus->hosttype) {
|
||||
+ case BCMA_HOSTTYPE_PCI:
|
||||
+ return bus->host_pci->irq;
|
||||
+ case BCMA_HOSTTYPE_SOC:
|
||||
+ if (bus->drv_mips.core && num == 0) {
|
||||
+ mips_irq = bcma_core_mips_irq(core);
|
||||
+ return mips_irq <= 4 ? mips_irq + 2 : 0;
|
||||
+ }
|
||||
+ if (bus->host_pdev)
|
||||
+ return bcma_of_get_irq(bus->host_pdev, core, num);
|
||||
+ return 0;
|
||||
+ case BCMA_HOSTTYPE_SDIO:
|
||||
+ return 0;
|
||||
+ }
|
||||
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(bcma_core_irq);
|
||||
+
|
||||
+void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
|
||||
+{
|
||||
core->dev.release = bcma_release_core_dev;
|
||||
core->dev.bus = &bcma_bus_type;
|
||||
dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
|
||||
@@ -196,6 +266,11 @@ static void bcma_register_core(struct bc
|
||||
case BCMA_HOSTTYPE_SDIO:
|
||||
break;
|
||||
}
|
||||
+}
|
||||
+
|
||||
+static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
|
||||
+{
|
||||
+ int err;
|
||||
|
||||
err = device_register(&core->dev);
|
||||
if (err) {
|
||||
--- a/drivers/bcma/scan.c
|
||||
+++ b/drivers/bcma/scan.c
|
||||
@@ -505,6 +505,7 @@ int bcma_bus_scan(struct bcma_bus *bus)
|
||||
bus->nr_cores++;
|
||||
other_core = bcma_find_core_reverse(bus, core->id.id);
|
||||
core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1;
|
||||
+ bcma_prepare_core(bus, core);
|
||||
|
||||
bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
|
||||
core->core_index, bcma_device_name(&core->id),
|
||||
--- a/include/linux/bcma/bcma.h
|
||||
+++ b/include/linux/bcma/bcma.h
|
||||
@@ -448,4 +448,6 @@ extern u32 bcma_chipco_pll_read(struct b
|
||||
#define BCMA_DMA_TRANSLATION_DMA64_CMT 0x80000000 /* Client Mode Translation for 64-bit DMA */
|
||||
extern u32 bcma_core_dma_translation(struct bcma_device *core);
|
||||
|
||||
+extern unsigned int bcma_core_irq(struct bcma_device *core, int num);
|
||||
+
|
||||
#endif /* LINUX_BCMA_H_ */
|
||||
--- a/include/linux/bcma/bcma_driver_mips.h
|
||||
+++ b/include/linux/bcma/bcma_driver_mips.h
|
||||
@@ -43,12 +43,12 @@ struct bcma_drv_mips {
|
||||
extern void bcma_core_mips_init(struct bcma_drv_mips *mcore);
|
||||
extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
|
||||
|
||||
-extern unsigned int bcma_core_irq(struct bcma_device *core);
|
||||
+extern unsigned int bcma_core_mips_irq(struct bcma_device *dev);
|
||||
#else
|
||||
static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { }
|
||||
static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { }
|
||||
|
||||
-static inline unsigned int bcma_core_irq(struct bcma_device *core)
|
||||
+static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
--- a/Documentation/devicetree/bindings/bus/bcma.txt
|
||||
+++ b/Documentation/devicetree/bindings/bus/bcma.txt
|
||||
@@ -8,6 +8,11 @@ Required properties:
|
||||
|
||||
The cores on the AXI bus are automatically detected by bcma with the
|
||||
memory ranges they are using and they get registered afterwards.
|
||||
+Automatic detection of the IRQ number is not working on
|
||||
+BCM47xx/BCM53xx ARM SoCs. To assign IRQ numbers to the cores, provide
|
||||
+them manually through device tree. Use an interrupt-map to specify the
|
||||
+IRQ used by the devices on the bus. The first address is just an index,
|
||||
+because we do not have any special register.
|
||||
|
||||
The top-level axi bus may contain children representing attached cores
|
||||
(devices). This is needed since some hardware details can't be auto
|
||||
@@ -22,6 +27,22 @@ Example:
|
||||
ranges = <0x00000000 0x18000000 0x00100000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
+ #interrupt-cells = <1>;
|
||||
+ interrupt-map-mask = <0x000fffff 0xffff>;
|
||||
+ interrupt-map =
|
||||
+ /* Ethernet Controller 0 */
|
||||
+ <0x00024000 0 &gic GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+
|
||||
+ /* Ethernet Controller 1 */
|
||||
+ <0x00025000 0 &gic GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+
|
||||
+ /* PCIe Controller 0 */
|
||||
+ <0x00012000 0 &gic GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+ <0x00012000 1 &gic GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+ <0x00012000 2 &gic GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+ <0x00012000 3 &gic GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+ <0x00012000 4 &gic GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+ <0x00012000 5 &gic GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
chipcommon {
|
||||
reg = <0x00000000 0x1000>;
|
|
@ -1,527 +0,0 @@
|
|||
--- a/drivers/bcma/bcma_private.h
|
||||
+++ b/drivers/bcma/bcma_private.h
|
||||
@@ -23,22 +23,18 @@ struct bcma_bus;
|
||||
bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value,
|
||||
int timeout);
|
||||
void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core);
|
||||
+void bcma_init_bus(struct bcma_bus *bus);
|
||||
int bcma_bus_register(struct bcma_bus *bus);
|
||||
void bcma_bus_unregister(struct bcma_bus *bus);
|
||||
-int __init bcma_bus_early_register(struct bcma_bus *bus,
|
||||
- struct bcma_device *core_cc,
|
||||
- struct bcma_device *core_mips);
|
||||
+int __init bcma_bus_early_register(struct bcma_bus *bus);
|
||||
#ifdef CONFIG_PM
|
||||
int bcma_bus_suspend(struct bcma_bus *bus);
|
||||
int bcma_bus_resume(struct bcma_bus *bus);
|
||||
#endif
|
||||
|
||||
/* scan.c */
|
||||
+void bcma_detect_chip(struct bcma_bus *bus);
|
||||
int bcma_bus_scan(struct bcma_bus *bus);
|
||||
-int __init bcma_bus_scan_early(struct bcma_bus *bus,
|
||||
- struct bcma_device_id *match,
|
||||
- struct bcma_device *core);
|
||||
-void bcma_init_bus(struct bcma_bus *bus);
|
||||
|
||||
/* sprom.c */
|
||||
int bcma_sprom_get(struct bcma_bus *bus);
|
||||
@@ -109,6 +105,14 @@ extern int bcma_chipco_watchdog_register
|
||||
#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
|
||||
bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc);
|
||||
void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
|
||||
+#else
|
||||
+static inline bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
|
||||
+{
|
||||
+ return false;
|
||||
+}
|
||||
+static inline void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
|
||||
+{
|
||||
+}
|
||||
#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
|
||||
|
||||
#ifdef CONFIG_BCMA_DRIVER_GPIO
|
||||
--- a/drivers/bcma/driver_chipcommon.c
|
||||
+++ b/drivers/bcma/driver_chipcommon.c
|
||||
@@ -79,7 +79,9 @@ static int bcma_chipco_watchdog_ticks_pe
|
||||
|
||||
if (cc->capabilities & BCMA_CC_CAP_PMU) {
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
|
||||
- /* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP clock */
|
||||
+ /* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP
|
||||
+ * clock
|
||||
+ */
|
||||
return bcma_chipco_get_alp_clock(cc) / 4000;
|
||||
else
|
||||
/* based on 32KHz ILP clock */
|
||||
@@ -97,7 +99,8 @@ int bcma_chipco_watchdog_register(struct
|
||||
wdt.driver_data = cc;
|
||||
wdt.timer_set = bcma_chipco_watchdog_timer_set_wdt;
|
||||
wdt.timer_set_ms = bcma_chipco_watchdog_timer_set_ms_wdt;
|
||||
- wdt.max_timer_ms = bcma_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms;
|
||||
+ wdt.max_timer_ms =
|
||||
+ bcma_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms;
|
||||
|
||||
pdev = platform_device_register_data(NULL, "bcm47xx-wdt",
|
||||
cc->core->bus->num, &wdt,
|
||||
@@ -175,7 +178,6 @@ void bcma_core_chipcommon_init(struct bc
|
||||
u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
|
||||
{
|
||||
u32 maxt;
|
||||
- enum bcma_clkmode clkmode;
|
||||
|
||||
maxt = bcma_chipco_watchdog_get_max_timer(cc);
|
||||
if (cc->capabilities & BCMA_CC_CAP_PMU) {
|
||||
@@ -185,8 +187,13 @@ u32 bcma_chipco_watchdog_timer_set(struc
|
||||
ticks = maxt;
|
||||
bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks);
|
||||
} else {
|
||||
- clkmode = ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC;
|
||||
- bcma_core_set_clockmode(cc->core, clkmode);
|
||||
+ struct bcma_bus *bus = cc->core->bus;
|
||||
+
|
||||
+ if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4707 &&
|
||||
+ bus->chipinfo.id != BCMA_CHIP_ID_BCM53018)
|
||||
+ bcma_core_set_clockmode(cc->core,
|
||||
+ ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC);
|
||||
+
|
||||
if (ticks > maxt)
|
||||
ticks = maxt;
|
||||
/* instant NMI */
|
||||
@@ -335,7 +342,8 @@ void bcma_chipco_serial_init(struct bcma
|
||||
| BCMA_CC_CORECTL_UARTCLKEN);
|
||||
}
|
||||
} else {
|
||||
- bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n", ccrev);
|
||||
+ bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n",
|
||||
+ ccrev);
|
||||
return;
|
||||
}
|
||||
|
||||
--- a/drivers/bcma/driver_pci.c
|
||||
+++ b/drivers/bcma/driver_pci.c
|
||||
@@ -145,6 +145,47 @@ static u16 bcma_pcie_mdio_writeread(stru
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
+ * Early init.
|
||||
+ **************************************************/
|
||||
+
|
||||
+static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
|
||||
+{
|
||||
+ struct bcma_device *core = pc->core;
|
||||
+ u16 val16, core_index;
|
||||
+ uint regoff;
|
||||
+
|
||||
+ regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
|
||||
+ core_index = (u16)core->core_index;
|
||||
+
|
||||
+ val16 = pcicore_read16(pc, regoff);
|
||||
+ if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
|
||||
+ != core_index) {
|
||||
+ val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
|
||||
+ (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
|
||||
+ pcicore_write16(pc, regoff, val16);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Apply some early fixes required before accessing SPROM.
|
||||
+ * See also si_pci_fixcfg.
|
||||
+ */
|
||||
+void bcma_core_pci_early_init(struct bcma_drv_pci *pc)
|
||||
+{
|
||||
+ if (pc->early_setup_done)
|
||||
+ return;
|
||||
+
|
||||
+ pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
|
||||
+ if (pc->hostmode)
|
||||
+ goto out;
|
||||
+
|
||||
+ bcma_core_pci_fixcfg(pc);
|
||||
+
|
||||
+out:
|
||||
+ pc->early_setup_done = true;
|
||||
+}
|
||||
+
|
||||
+/**************************************************
|
||||
* Workarounds.
|
||||
**************************************************/
|
||||
|
||||
@@ -175,24 +216,6 @@ static void bcma_pcicore_serdes_workarou
|
||||
tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
|
||||
}
|
||||
|
||||
-static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
|
||||
-{
|
||||
- struct bcma_device *core = pc->core;
|
||||
- u16 val16, core_index;
|
||||
- uint regoff;
|
||||
-
|
||||
- regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
|
||||
- core_index = (u16)core->core_index;
|
||||
-
|
||||
- val16 = pcicore_read16(pc, regoff);
|
||||
- if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
|
||||
- != core_index) {
|
||||
- val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
|
||||
- (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
|
||||
- pcicore_write16(pc, regoff, val16);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
|
||||
/* Needs to happen when coming out of 'standby'/'hibernate' */
|
||||
static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
|
||||
@@ -216,7 +239,6 @@ static void bcma_core_pci_config_fixup(s
|
||||
|
||||
static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
|
||||
{
|
||||
- bcma_core_pci_fixcfg(pc);
|
||||
bcma_pcicore_serdes_workaround(pc);
|
||||
bcma_core_pci_config_fixup(pc);
|
||||
}
|
||||
@@ -226,13 +248,11 @@ void bcma_core_pci_init(struct bcma_drv_
|
||||
if (pc->setup_done)
|
||||
return;
|
||||
|
||||
-#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
|
||||
- pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
|
||||
+ bcma_core_pci_early_init(pc);
|
||||
+
|
||||
if (pc->hostmode)
|
||||
bcma_core_pci_hostmode_init(pc);
|
||||
-#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
|
||||
-
|
||||
- if (!pc->hostmode)
|
||||
+ else
|
||||
bcma_core_pci_clientmode_init(pc);
|
||||
}
|
||||
|
||||
--- a/drivers/bcma/host_pci.c
|
||||
+++ b/drivers/bcma/host_pci.c
|
||||
@@ -13,10 +13,12 @@
|
||||
|
||||
static void bcma_host_pci_switch_core(struct bcma_device *core)
|
||||
{
|
||||
+ int win2 = core->bus->host_is_pcie2 ?
|
||||
+ BCMA_PCIE2_BAR0_WIN2 : BCMA_PCI_BAR0_WIN2;
|
||||
+
|
||||
pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN,
|
||||
core->addr);
|
||||
- pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2,
|
||||
- core->wrap);
|
||||
+ pci_write_config_dword(core->bus->host_pci, win2, core->wrap);
|
||||
core->bus->mapped_core = core;
|
||||
bcma_debug(core->bus, "Switched to core: 0x%X\n", core->id.id);
|
||||
}
|
||||
--- a/drivers/bcma/host_soc.c
|
||||
+++ b/drivers/bcma/host_soc.c
|
||||
@@ -193,7 +193,7 @@ int __init bcma_host_soc_init(struct bcm
|
||||
int err;
|
||||
|
||||
/* Scan bus and initialize it */
|
||||
- err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
|
||||
+ err = bcma_bus_early_register(bus);
|
||||
if (err)
|
||||
iounmap(bus->mmio);
|
||||
|
||||
--- a/drivers/bcma/main.c
|
||||
+++ b/drivers/bcma/main.c
|
||||
@@ -268,6 +268,18 @@ void bcma_prepare_core(struct bcma_bus *
|
||||
}
|
||||
}
|
||||
|
||||
+void bcma_init_bus(struct bcma_bus *bus)
|
||||
+{
|
||||
+ mutex_lock(&bcma_buses_mutex);
|
||||
+ bus->num = bcma_bus_next_num++;
|
||||
+ mutex_unlock(&bcma_buses_mutex);
|
||||
+
|
||||
+ INIT_LIST_HEAD(&bus->cores);
|
||||
+ bus->nr_cores = 0;
|
||||
+
|
||||
+ bcma_detect_chip(bus);
|
||||
+}
|
||||
+
|
||||
static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
|
||||
{
|
||||
int err;
|
||||
@@ -356,12 +368,19 @@ static void bcma_unregister_cores(struct
|
||||
struct bcma_device *core, *tmp;
|
||||
|
||||
list_for_each_entry_safe(core, tmp, &bus->cores, list) {
|
||||
+ if (!core->dev_registered)
|
||||
+ continue;
|
||||
list_del(&core->list);
|
||||
- if (core->dev_registered)
|
||||
- device_unregister(&core->dev);
|
||||
+ device_unregister(&core->dev);
|
||||
}
|
||||
if (bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
platform_device_unregister(bus->drv_cc.watchdog);
|
||||
+
|
||||
+ /* Now noone uses internally-handled cores, we can free them */
|
||||
+ list_for_each_entry_safe(core, tmp, &bus->cores, list) {
|
||||
+ list_del(&core->list);
|
||||
+ kfree(core);
|
||||
+ }
|
||||
}
|
||||
|
||||
int bcma_bus_register(struct bcma_bus *bus)
|
||||
@@ -369,10 +388,6 @@ int bcma_bus_register(struct bcma_bus *b
|
||||
int err;
|
||||
struct bcma_device *core;
|
||||
|
||||
- mutex_lock(&bcma_buses_mutex);
|
||||
- bus->num = bcma_bus_next_num++;
|
||||
- mutex_unlock(&bcma_buses_mutex);
|
||||
-
|
||||
/* Scan for devices (cores) */
|
||||
err = bcma_bus_scan(bus);
|
||||
if (err) {
|
||||
@@ -387,6 +402,13 @@ int bcma_bus_register(struct bcma_bus *b
|
||||
bcma_core_chipcommon_early_init(&bus->drv_cc);
|
||||
}
|
||||
|
||||
+ /* Early init PCIE core */
|
||||
+ core = bcma_find_core(bus, BCMA_CORE_PCIE);
|
||||
+ if (core) {
|
||||
+ bus->drv_pci[0].core = core;
|
||||
+ bcma_core_pci_early_init(&bus->drv_pci[0]);
|
||||
+ }
|
||||
+
|
||||
/* Cores providing flash access go before SPROM init */
|
||||
list_for_each_entry(core, &bus->cores, list) {
|
||||
if (bcma_is_core_needed_early(core->id.id))
|
||||
@@ -459,7 +481,6 @@ int bcma_bus_register(struct bcma_bus *b
|
||||
|
||||
void bcma_bus_unregister(struct bcma_bus *bus)
|
||||
{
|
||||
- struct bcma_device *cores[3];
|
||||
int err;
|
||||
|
||||
err = bcma_gpio_unregister(&bus->drv_cc);
|
||||
@@ -470,46 +491,23 @@ void bcma_bus_unregister(struct bcma_bus
|
||||
|
||||
bcma_core_chipcommon_b_free(&bus->drv_cc_b);
|
||||
|
||||
- cores[0] = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
|
||||
- cores[1] = bcma_find_core(bus, BCMA_CORE_PCIE);
|
||||
- cores[2] = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
|
||||
-
|
||||
bcma_unregister_cores(bus);
|
||||
-
|
||||
- kfree(cores[2]);
|
||||
- kfree(cores[1]);
|
||||
- kfree(cores[0]);
|
||||
}
|
||||
|
||||
-int __init bcma_bus_early_register(struct bcma_bus *bus,
|
||||
- struct bcma_device *core_cc,
|
||||
- struct bcma_device *core_mips)
|
||||
+/*
|
||||
+ * This is a special version of bus registration function designed for SoCs.
|
||||
+ * It scans bus and performs basic initialization of main cores only.
|
||||
+ * Please note it requires memory allocation, however it won't try to sleep.
|
||||
+ */
|
||||
+int __init bcma_bus_early_register(struct bcma_bus *bus)
|
||||
{
|
||||
int err;
|
||||
struct bcma_device *core;
|
||||
- struct bcma_device_id match;
|
||||
-
|
||||
- match.manuf = BCMA_MANUF_BCM;
|
||||
- match.id = bcma_cc_core_id(bus);
|
||||
- match.class = BCMA_CL_SIM;
|
||||
- match.rev = BCMA_ANY_REV;
|
||||
|
||||
- /* Scan for chip common core */
|
||||
- err = bcma_bus_scan_early(bus, &match, core_cc);
|
||||
- if (err) {
|
||||
- bcma_err(bus, "Failed to scan for common core: %d\n", err);
|
||||
- return -1;
|
||||
- }
|
||||
-
|
||||
- match.manuf = BCMA_MANUF_MIPS;
|
||||
- match.id = BCMA_CORE_MIPS_74K;
|
||||
- match.class = BCMA_CL_SIM;
|
||||
- match.rev = BCMA_ANY_REV;
|
||||
-
|
||||
- /* Scan for mips core */
|
||||
- err = bcma_bus_scan_early(bus, &match, core_mips);
|
||||
+ /* Scan for devices (cores) */
|
||||
+ err = bcma_bus_scan(bus);
|
||||
if (err) {
|
||||
- bcma_err(bus, "Failed to scan for mips core: %d\n", err);
|
||||
+ bcma_err(bus, "Failed to scan bus: %d\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
--- a/drivers/bcma/scan.c
|
||||
+++ b/drivers/bcma/scan.c
|
||||
@@ -435,15 +435,12 @@ static int bcma_get_next_core(struct bcm
|
||||
return 0;
|
||||
}
|
||||
|
||||
-void bcma_init_bus(struct bcma_bus *bus)
|
||||
+void bcma_detect_chip(struct bcma_bus *bus)
|
||||
{
|
||||
s32 tmp;
|
||||
struct bcma_chipinfo *chipinfo = &(bus->chipinfo);
|
||||
char chip_id[8];
|
||||
|
||||
- INIT_LIST_HEAD(&bus->cores);
|
||||
- bus->nr_cores = 0;
|
||||
-
|
||||
bcma_scan_switch_core(bus, BCMA_ADDR_BASE);
|
||||
|
||||
tmp = bcma_scan_read32(bus, 0, BCMA_CC_ID);
|
||||
@@ -464,6 +461,10 @@ int bcma_bus_scan(struct bcma_bus *bus)
|
||||
|
||||
int err, core_num = 0;
|
||||
|
||||
+ /* Skip if bus was already scanned (e.g. during early register) */
|
||||
+ if (bus->nr_cores)
|
||||
+ return 0;
|
||||
+
|
||||
erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
|
||||
if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
|
||||
eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
|
||||
@@ -519,64 +520,6 @@ int bcma_bus_scan(struct bcma_bus *bus)
|
||||
out:
|
||||
if (bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
iounmap(eromptr);
|
||||
-
|
||||
- return err;
|
||||
-}
|
||||
-
|
||||
-int __init bcma_bus_scan_early(struct bcma_bus *bus,
|
||||
- struct bcma_device_id *match,
|
||||
- struct bcma_device *core)
|
||||
-{
|
||||
- u32 erombase;
|
||||
- u32 __iomem *eromptr, *eromend;
|
||||
-
|
||||
- int err = -ENODEV;
|
||||
- int core_num = 0;
|
||||
-
|
||||
- erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
|
||||
- if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
|
||||
- eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
|
||||
- if (!eromptr)
|
||||
- return -ENOMEM;
|
||||
- } else {
|
||||
- eromptr = bus->mmio;
|
||||
- }
|
||||
-
|
||||
- eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
|
||||
-
|
||||
- bcma_scan_switch_core(bus, erombase);
|
||||
-
|
||||
- while (eromptr < eromend) {
|
||||
- memset(core, 0, sizeof(*core));
|
||||
- INIT_LIST_HEAD(&core->list);
|
||||
- core->bus = bus;
|
||||
-
|
||||
- err = bcma_get_next_core(bus, &eromptr, match, core_num, core);
|
||||
- if (err == -ENODEV) {
|
||||
- core_num++;
|
||||
- continue;
|
||||
- } else if (err == -ENXIO)
|
||||
- continue;
|
||||
- else if (err == -ESPIPE)
|
||||
- break;
|
||||
- else if (err < 0)
|
||||
- goto out;
|
||||
-
|
||||
- core->core_index = core_num++;
|
||||
- bus->nr_cores++;
|
||||
- bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
|
||||
- core->core_index, bcma_device_name(&core->id),
|
||||
- core->id.manuf, core->id.id, core->id.rev,
|
||||
- core->id.class);
|
||||
-
|
||||
- list_add_tail(&core->list, &bus->cores);
|
||||
- err = 0;
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
-out:
|
||||
- if (bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
- iounmap(eromptr);
|
||||
|
||||
return err;
|
||||
}
|
||||
--- a/drivers/bcma/sprom.c
|
||||
+++ b/drivers/bcma/sprom.c
|
||||
@@ -579,7 +579,8 @@ int bcma_sprom_get(struct bcma_bus *bus)
|
||||
u16 offset = BCMA_CC_SPROM;
|
||||
u16 *sprom;
|
||||
size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
|
||||
- SSB_SPROMSIZE_WORDS_R10, };
|
||||
+ SSB_SPROMSIZE_WORDS_R10,
|
||||
+ SSB_SPROMSIZE_WORDS_R11, };
|
||||
int i, err = 0;
|
||||
|
||||
if (!bus->drv_cc.core)
|
||||
--- a/include/linux/bcma/bcma.h
|
||||
+++ b/include/linux/bcma/bcma.h
|
||||
@@ -319,6 +319,7 @@ struct bcma_bus {
|
||||
const struct bcma_host_ops *ops;
|
||||
|
||||
enum bcma_hosttype hosttype;
|
||||
+ bool host_is_pcie2; /* Used for BCMA_HOSTTYPE_PCI only */
|
||||
union {
|
||||
/* Pointer to the PCI bus (only for BCMA_HOSTTYPE_PCI) */
|
||||
struct pci_dev *host_pci;
|
||||
--- a/include/linux/bcma/bcma_driver_pci.h
|
||||
+++ b/include/linux/bcma/bcma_driver_pci.h
|
||||
@@ -223,6 +223,7 @@ struct bcma_drv_pci_host {
|
||||
|
||||
struct bcma_drv_pci {
|
||||
struct bcma_device *core;
|
||||
+ u8 early_setup_done:1;
|
||||
u8 setup_done:1;
|
||||
u8 hostmode:1;
|
||||
|
||||
@@ -237,6 +238,7 @@ struct bcma_drv_pci {
|
||||
#define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val)
|
||||
#define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val)
|
||||
|
||||
+extern void bcma_core_pci_early_init(struct bcma_drv_pci *pc);
|
||||
extern void bcma_core_pci_init(struct bcma_drv_pci *pc);
|
||||
extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
|
||||
struct bcma_device *core, bool enable);
|
||||
--- a/include/linux/bcma/bcma_regs.h
|
||||
+++ b/include/linux/bcma/bcma_regs.h
|
||||
@@ -64,6 +64,8 @@
|
||||
#define BCMA_PCI_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */
|
||||
#define BCMA_PCI_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */
|
||||
|
||||
+#define BCMA_PCIE2_BAR0_WIN2 0x70
|
||||
+
|
||||
/* SiliconBackplane Address Map.
|
||||
* All regions may not exist on all chips.
|
||||
*/
|
||||
--- a/include/linux/bcma/bcma_soc.h
|
||||
+++ b/include/linux/bcma/bcma_soc.h
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
struct bcma_soc {
|
||||
struct bcma_bus bus;
|
||||
- struct bcma_device core_cc;
|
||||
- struct bcma_device core_mips;
|
||||
};
|
||||
|
||||
int __init bcma_host_soc_register(struct bcma_soc *soc);
|
||||
--- a/include/linux/ssb/ssb_regs.h
|
||||
+++ b/include/linux/ssb/ssb_regs.h
|
||||
@@ -173,6 +173,7 @@
|
||||
#define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16))
|
||||
#define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16))
|
||||
#define SSB_SPROMSIZE_WORDS_R10 230
|
||||
+#define SSB_SPROMSIZE_WORDS_R11 234
|
||||
#define SSB_SPROM_BASE1 0x1000
|
||||
#define SSB_SPROM_BASE31 0x0800
|
||||
#define SSB_SPROM_REVISION 0x007E
|
|
@ -1,680 +0,0 @@
|
|||
--- a/drivers/bcma/bcma_private.h
|
||||
+++ b/drivers/bcma/bcma_private.h
|
||||
@@ -24,6 +24,7 @@ bool bcma_wait_value(struct bcma_device
|
||||
int timeout);
|
||||
void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core);
|
||||
void bcma_init_bus(struct bcma_bus *bus);
|
||||
+void bcma_unregister_cores(struct bcma_bus *bus);
|
||||
int bcma_bus_register(struct bcma_bus *bus);
|
||||
void bcma_bus_unregister(struct bcma_bus *bus);
|
||||
int __init bcma_bus_early_register(struct bcma_bus *bus);
|
||||
@@ -40,6 +41,9 @@ int bcma_bus_scan(struct bcma_bus *bus);
|
||||
int bcma_sprom_get(struct bcma_bus *bus);
|
||||
|
||||
/* driver_chipcommon.c */
|
||||
+void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc);
|
||||
+void bcma_core_chipcommon_init(struct bcma_drv_cc *cc);
|
||||
+void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
|
||||
#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
|
||||
extern struct platform_device bcma_pflash_dev;
|
||||
@@ -50,6 +54,8 @@ int bcma_core_chipcommon_b_init(struct b
|
||||
void bcma_core_chipcommon_b_free(struct bcma_drv_cc_b *ccb);
|
||||
|
||||
/* driver_chipcommon_pmu.c */
|
||||
+void bcma_pmu_early_init(struct bcma_drv_cc *cc);
|
||||
+void bcma_pmu_init(struct bcma_drv_cc *cc);
|
||||
u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc);
|
||||
u32 bcma_pmu_get_cpu_clock(struct bcma_drv_cc *cc);
|
||||
|
||||
@@ -98,7 +104,35 @@ static inline void __exit bcma_host_soc_
|
||||
#endif /* CONFIG_BCMA_HOST_SOC && CONFIG_OF */
|
||||
|
||||
/* driver_pci.c */
|
||||
+#ifdef CONFIG_BCMA_DRIVER_PCI
|
||||
u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
|
||||
+void bcma_core_pci_early_init(struct bcma_drv_pci *pc);
|
||||
+void bcma_core_pci_init(struct bcma_drv_pci *pc);
|
||||
+void bcma_core_pci_up(struct bcma_drv_pci *pc);
|
||||
+void bcma_core_pci_down(struct bcma_drv_pci *pc);
|
||||
+#else
|
||||
+static inline void bcma_core_pci_early_init(struct bcma_drv_pci *pc)
|
||||
+{
|
||||
+ WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
|
||||
+}
|
||||
+static inline void bcma_core_pci_init(struct bcma_drv_pci *pc)
|
||||
+{
|
||||
+ /* Initialization is required for PCI hosted bus */
|
||||
+ WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+/* driver_pcie2.c */
|
||||
+#ifdef CONFIG_BCMA_DRIVER_PCI
|
||||
+void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2);
|
||||
+void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2);
|
||||
+#else
|
||||
+static inline void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
|
||||
+{
|
||||
+ /* Initialization is required for PCI hosted bus */
|
||||
+ WARN_ON(pcie2->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
|
||||
+}
|
||||
+#endif
|
||||
|
||||
extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc);
|
||||
|
||||
@@ -115,6 +149,39 @@ static inline void bcma_core_pci_hostmod
|
||||
}
|
||||
#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
|
||||
|
||||
+/**************************************************
|
||||
+ * driver_mips.c
|
||||
+ **************************************************/
|
||||
+
|
||||
+#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
+unsigned int bcma_core_mips_irq(struct bcma_device *dev);
|
||||
+void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
|
||||
+void bcma_core_mips_init(struct bcma_drv_mips *mcore);
|
||||
+#else
|
||||
+static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore)
|
||||
+{
|
||||
+}
|
||||
+static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore)
|
||||
+{
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+/**************************************************
|
||||
+ * driver_gmac_cmn.c
|
||||
+ **************************************************/
|
||||
+
|
||||
+#ifdef CONFIG_BCMA_DRIVER_GMAC_CMN
|
||||
+void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc);
|
||||
+#else
|
||||
+static inline void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc)
|
||||
+{
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
#ifdef CONFIG_BCMA_DRIVER_GPIO
|
||||
/* driver_gpio.c */
|
||||
int bcma_gpio_init(struct bcma_drv_cc *cc);
|
||||
--- a/drivers/bcma/driver_gpio.c
|
||||
+++ b/drivers/bcma/driver_gpio.c
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "bcma_private.h"
|
||||
|
||||
+#define BCMA_GPIO_MAX_PINS 32
|
||||
+
|
||||
static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct bcma_drv_cc, gpio);
|
||||
@@ -76,7 +78,7 @@ static void bcma_gpio_free(struct gpio_c
|
||||
bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
|
||||
}
|
||||
|
||||
-#if IS_BUILTIN(CONFIG_BCM47XX)
|
||||
+#if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X)
|
||||
static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
|
||||
{
|
||||
struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
|
||||
@@ -204,6 +206,7 @@ static void bcma_gpio_irq_domain_exit(st
|
||||
|
||||
int bcma_gpio_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
+ struct bcma_bus *bus = cc->core->bus;
|
||||
struct gpio_chip *chip = &cc->gpio;
|
||||
int err;
|
||||
|
||||
@@ -215,14 +218,14 @@ int bcma_gpio_init(struct bcma_drv_cc *c
|
||||
chip->set = bcma_gpio_set_value;
|
||||
chip->direction_input = bcma_gpio_direction_input;
|
||||
chip->direction_output = bcma_gpio_direction_output;
|
||||
-#if IS_BUILTIN(CONFIG_BCM47XX)
|
||||
+#if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X)
|
||||
chip->to_irq = bcma_gpio_to_irq;
|
||||
#endif
|
||||
#if IS_BUILTIN(CONFIG_OF)
|
||||
if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
chip->of_node = cc->core->dev.of_node;
|
||||
#endif
|
||||
- switch (cc->core->bus->chipinfo.id) {
|
||||
+ switch (bus->chipinfo.id) {
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM53572:
|
||||
chip->ngpio = 32;
|
||||
@@ -231,13 +234,17 @@ int bcma_gpio_init(struct bcma_drv_cc *c
|
||||
chip->ngpio = 16;
|
||||
}
|
||||
|
||||
- /* There is just one SoC in one device and its GPIO addresses should be
|
||||
- * deterministic to address them more easily. The other buses could get
|
||||
- * a random base number. */
|
||||
- if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
- chip->base = 0;
|
||||
- else
|
||||
- chip->base = -1;
|
||||
+ /*
|
||||
+ * On MIPS we register GPIO devices (LEDs, buttons) using absolute GPIO
|
||||
+ * pin numbers. We don't have Device Tree there and we can't really use
|
||||
+ * relative (per chip) numbers.
|
||||
+ * So let's use predictable base for BCM47XX and "random" for all other.
|
||||
+ */
|
||||
+#if IS_BUILTIN(CONFIG_BCM47XX)
|
||||
+ chip->base = bus->num * BCMA_GPIO_MAX_PINS;
|
||||
+#else
|
||||
+ chip->base = -1;
|
||||
+#endif
|
||||
|
||||
err = bcma_gpio_irq_domain_init(cc);
|
||||
if (err)
|
||||
--- a/drivers/bcma/driver_pci.c
|
||||
+++ b/drivers/bcma/driver_pci.c
|
||||
@@ -282,39 +282,6 @@ void bcma_core_pci_power_save(struct bcm
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_core_pci_power_save);
|
||||
|
||||
-int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
|
||||
- bool enable)
|
||||
-{
|
||||
- struct pci_dev *pdev;
|
||||
- u32 coremask, tmp;
|
||||
- int err = 0;
|
||||
-
|
||||
- if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
|
||||
- /* This bcma device is not on a PCI host-bus. So the IRQs are
|
||||
- * not routed through the PCI core.
|
||||
- * So we must not enable routing through the PCI core. */
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
- pdev = pc->core->bus->host_pci;
|
||||
-
|
||||
- err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
|
||||
- if (err)
|
||||
- goto out;
|
||||
-
|
||||
- coremask = BIT(core->core_index) << 8;
|
||||
- if (enable)
|
||||
- tmp |= coremask;
|
||||
- else
|
||||
- tmp &= ~coremask;
|
||||
-
|
||||
- err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
|
||||
-
|
||||
-out:
|
||||
- return err;
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
|
||||
-
|
||||
static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
|
||||
{
|
||||
u32 w;
|
||||
@@ -328,28 +295,12 @@ static void bcma_core_pci_extend_L1timer
|
||||
bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
|
||||
}
|
||||
|
||||
-void bcma_core_pci_up(struct bcma_bus *bus)
|
||||
+void bcma_core_pci_up(struct bcma_drv_pci *pc)
|
||||
{
|
||||
- struct bcma_drv_pci *pc;
|
||||
-
|
||||
- if (bus->hosttype != BCMA_HOSTTYPE_PCI)
|
||||
- return;
|
||||
-
|
||||
- pc = &bus->drv_pci[0];
|
||||
-
|
||||
bcma_core_pci_extend_L1timer(pc, true);
|
||||
}
|
||||
-EXPORT_SYMBOL_GPL(bcma_core_pci_up);
|
||||
|
||||
-void bcma_core_pci_down(struct bcma_bus *bus)
|
||||
+void bcma_core_pci_down(struct bcma_drv_pci *pc)
|
||||
{
|
||||
- struct bcma_drv_pci *pc;
|
||||
-
|
||||
- if (bus->hosttype != BCMA_HOSTTYPE_PCI)
|
||||
- return;
|
||||
-
|
||||
- pc = &bus->drv_pci[0];
|
||||
-
|
||||
bcma_core_pci_extend_L1timer(pc, false);
|
||||
}
|
||||
-EXPORT_SYMBOL_GPL(bcma_core_pci_down);
|
||||
--- a/drivers/bcma/driver_pci_host.c
|
||||
+++ b/drivers/bcma/driver_pci_host.c
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "bcma_private.h"
|
||||
#include <linux/pci.h>
|
||||
+#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <asm/paccess.h>
|
||||
--- a/drivers/bcma/driver_pcie2.c
|
||||
+++ b/drivers/bcma/driver_pcie2.c
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "bcma_private.h"
|
||||
#include <linux/bcma/bcma.h>
|
||||
+#include <linux/pci.h>
|
||||
|
||||
/**************************************************
|
||||
* R/W ops.
|
||||
@@ -156,14 +157,23 @@ static void pciedev_reg_pm_clk_period(st
|
||||
|
||||
void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
|
||||
{
|
||||
- struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo;
|
||||
+ struct bcma_bus *bus = pcie2->core->bus;
|
||||
+ struct bcma_chipinfo *ci = &bus->chipinfo;
|
||||
u32 tmp;
|
||||
|
||||
tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54));
|
||||
if ((tmp & 0xe) >> 1 == 2)
|
||||
bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17);
|
||||
|
||||
- /* TODO: Do we need pcie_reqsize? */
|
||||
+ switch (bus->chipinfo.id) {
|
||||
+ case BCMA_CHIP_ID_BCM4360:
|
||||
+ case BCMA_CHIP_ID_BCM4352:
|
||||
+ pcie2->reqsize = 1024;
|
||||
+ break;
|
||||
+ default:
|
||||
+ pcie2->reqsize = 128;
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3)
|
||||
bcma_core_pcie2_war_delay_perst_enab(pcie2, true);
|
||||
@@ -173,3 +183,18 @@ void bcma_core_pcie2_init(struct bcma_dr
|
||||
pciedev_crwlpciegen2_180(pcie2);
|
||||
pciedev_crwlpciegen2_182(pcie2);
|
||||
}
|
||||
+
|
||||
+/**************************************************
|
||||
+ * Runtime ops.
|
||||
+ **************************************************/
|
||||
+
|
||||
+void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2)
|
||||
+{
|
||||
+ struct bcma_bus *bus = pcie2->core->bus;
|
||||
+ struct pci_dev *dev = bus->host_pci;
|
||||
+ int err;
|
||||
+
|
||||
+ err = pcie_set_readrq(dev, pcie2->reqsize);
|
||||
+ if (err)
|
||||
+ bcma_err(bus, "Error setting PCI_EXP_DEVCTL_READRQ: %d\n", err);
|
||||
+}
|
||||
--- a/drivers/bcma/host_pci.c
|
||||
+++ b/drivers/bcma/host_pci.c
|
||||
@@ -213,16 +213,26 @@ static int bcma_host_pci_probe(struct pc
|
||||
/* Initialize struct, detect chip */
|
||||
bcma_init_bus(bus);
|
||||
|
||||
+ /* Scan bus to find out generation of PCIe core */
|
||||
+ err = bcma_bus_scan(bus);
|
||||
+ if (err)
|
||||
+ goto err_pci_unmap_mmio;
|
||||
+
|
||||
+ if (bcma_find_core(bus, BCMA_CORE_PCIE2))
|
||||
+ bus->host_is_pcie2 = true;
|
||||
+
|
||||
/* Register */
|
||||
err = bcma_bus_register(bus);
|
||||
if (err)
|
||||
- goto err_pci_unmap_mmio;
|
||||
+ goto err_unregister_cores;
|
||||
|
||||
pci_set_drvdata(dev, bus);
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
+err_unregister_cores:
|
||||
+ bcma_unregister_cores(bus);
|
||||
err_pci_unmap_mmio:
|
||||
pci_iounmap(dev, bus->mmio);
|
||||
err_pci_release_regions:
|
||||
@@ -283,9 +293,12 @@ static const struct pci_device_id bcma_p
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
|
||||
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4360) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) },
|
||||
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
|
||||
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) }, /* 0xa8db, BCM43217 (sic!) */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) }, /* 0xa8dc */
|
||||
@@ -310,3 +323,65 @@ void __exit bcma_host_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&bcma_pci_bridge_driver);
|
||||
}
|
||||
+
|
||||
+/**************************************************
|
||||
+ * Runtime ops for drivers.
|
||||
+ **************************************************/
|
||||
+
|
||||
+/* See also pcicore_up */
|
||||
+void bcma_host_pci_up(struct bcma_bus *bus)
|
||||
+{
|
||||
+ if (bus->hosttype != BCMA_HOSTTYPE_PCI)
|
||||
+ return;
|
||||
+
|
||||
+ if (bus->host_is_pcie2)
|
||||
+ bcma_core_pcie2_up(&bus->drv_pcie2);
|
||||
+ else
|
||||
+ bcma_core_pci_up(&bus->drv_pci[0]);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(bcma_host_pci_up);
|
||||
+
|
||||
+/* See also pcicore_down */
|
||||
+void bcma_host_pci_down(struct bcma_bus *bus)
|
||||
+{
|
||||
+ if (bus->hosttype != BCMA_HOSTTYPE_PCI)
|
||||
+ return;
|
||||
+
|
||||
+ if (!bus->host_is_pcie2)
|
||||
+ bcma_core_pci_down(&bus->drv_pci[0]);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(bcma_host_pci_down);
|
||||
+
|
||||
+/* See also si_pci_setup */
|
||||
+int bcma_host_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core,
|
||||
+ bool enable)
|
||||
+{
|
||||
+ struct pci_dev *pdev;
|
||||
+ u32 coremask, tmp;
|
||||
+ int err = 0;
|
||||
+
|
||||
+ if (bus->hosttype != BCMA_HOSTTYPE_PCI) {
|
||||
+ /* This bcma device is not on a PCI host-bus. So the IRQs are
|
||||
+ * not routed through the PCI core.
|
||||
+ * So we must not enable routing through the PCI core. */
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ pdev = bus->host_pci;
|
||||
+
|
||||
+ err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
|
||||
+ if (err)
|
||||
+ goto out;
|
||||
+
|
||||
+ coremask = BIT(core->core_index) << 8;
|
||||
+ if (enable)
|
||||
+ tmp |= coremask;
|
||||
+ else
|
||||
+ tmp &= ~coremask;
|
||||
+
|
||||
+ err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
|
||||
+
|
||||
+out:
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(bcma_host_pci_irq_ctl);
|
||||
--- a/drivers/bcma/main.c
|
||||
+++ b/drivers/bcma/main.c
|
||||
@@ -363,7 +363,7 @@ static int bcma_register_devices(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void bcma_unregister_cores(struct bcma_bus *bus)
|
||||
+void bcma_unregister_cores(struct bcma_bus *bus)
|
||||
{
|
||||
struct bcma_device *core, *tmp;
|
||||
|
||||
--- a/drivers/net/wireless/b43/main.c
|
||||
+++ b/drivers/net/wireless/b43/main.c
|
||||
@@ -4770,7 +4770,7 @@ static void b43_wireless_core_exit(struc
|
||||
switch (dev->dev->bus_type) {
|
||||
#ifdef CONFIG_B43_BCMA
|
||||
case B43_BUS_BCMA:
|
||||
- bcma_core_pci_down(dev->dev->bdev->bus);
|
||||
+ bcma_host_pci_down(dev->dev->bdev->bus);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_B43_SSB
|
||||
@@ -4817,9 +4817,9 @@ static int b43_wireless_core_init(struct
|
||||
switch (dev->dev->bus_type) {
|
||||
#ifdef CONFIG_B43_BCMA
|
||||
case B43_BUS_BCMA:
|
||||
- bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0],
|
||||
+ bcma_host_pci_irq_ctl(dev->dev->bdev->bus,
|
||||
dev->dev->bdev, true);
|
||||
- bcma_core_pci_up(dev->dev->bdev->bus);
|
||||
+ bcma_host_pci_up(dev->dev->bdev->bus);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_B43_SSB
|
||||
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
|
||||
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
|
||||
@@ -4669,7 +4669,7 @@ static int brcms_b_attach(struct brcms_c
|
||||
brcms_c_coredisable(wlc_hw);
|
||||
|
||||
/* Match driver "down" state */
|
||||
- bcma_core_pci_down(wlc_hw->d11core->bus);
|
||||
+ bcma_host_pci_down(wlc_hw->d11core->bus);
|
||||
|
||||
/* turn off pll and xtal to match driver "down" state */
|
||||
brcms_b_xtal(wlc_hw, OFF);
|
||||
@@ -4960,7 +4960,7 @@ static int brcms_b_up_prep(struct brcms_
|
||||
* Configure pci/pcmcia here instead of in brcms_c_attach()
|
||||
* to allow mfg hotswap: down, hotswap (chip power cycle), up.
|
||||
*/
|
||||
- bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci[0], wlc_hw->d11core,
|
||||
+ bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core,
|
||||
true);
|
||||
|
||||
/*
|
||||
@@ -4970,12 +4970,12 @@ static int brcms_b_up_prep(struct brcms_
|
||||
*/
|
||||
if (brcms_b_radio_read_hwdisabled(wlc_hw)) {
|
||||
/* put SB PCI in down state again */
|
||||
- bcma_core_pci_down(wlc_hw->d11core->bus);
|
||||
+ bcma_host_pci_down(wlc_hw->d11core->bus);
|
||||
brcms_b_xtal(wlc_hw, OFF);
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
- bcma_core_pci_up(wlc_hw->d11core->bus);
|
||||
+ bcma_host_pci_up(wlc_hw->d11core->bus);
|
||||
|
||||
/* reset the d11 core */
|
||||
brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
|
||||
@@ -5172,7 +5172,7 @@ static int brcms_b_down_finish(struct br
|
||||
|
||||
/* turn off primary xtal and pll */
|
||||
if (!wlc_hw->noreset) {
|
||||
- bcma_core_pci_down(wlc_hw->d11core->bus);
|
||||
+ bcma_host_pci_down(wlc_hw->d11core->bus);
|
||||
brcms_b_xtal(wlc_hw, OFF);
|
||||
}
|
||||
}
|
||||
--- a/include/linux/bcma/bcma.h
|
||||
+++ b/include/linux/bcma/bcma.h
|
||||
@@ -435,6 +435,27 @@ static inline struct bcma_device *bcma_f
|
||||
return bcma_find_core_unit(bus, coreid, 0);
|
||||
}
|
||||
|
||||
+#ifdef CONFIG_BCMA_HOST_PCI
|
||||
+extern void bcma_host_pci_up(struct bcma_bus *bus);
|
||||
+extern void bcma_host_pci_down(struct bcma_bus *bus);
|
||||
+extern int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
|
||||
+ struct bcma_device *core, bool enable);
|
||||
+#else
|
||||
+static inline void bcma_host_pci_up(struct bcma_bus *bus)
|
||||
+{
|
||||
+}
|
||||
+static inline void bcma_host_pci_down(struct bcma_bus *bus)
|
||||
+{
|
||||
+}
|
||||
+static inline int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
|
||||
+ struct bcma_device *core, bool enable)
|
||||
+{
|
||||
+ if (bus->hosttype == BCMA_HOSTTYPE_PCI)
|
||||
+ return -ENOTSUPP;
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
extern bool bcma_core_is_enabled(struct bcma_device *core);
|
||||
extern void bcma_core_disable(struct bcma_device *core, u32 flags);
|
||||
extern int bcma_core_enable(struct bcma_device *core, u32 flags);
|
||||
--- a/include/linux/bcma/bcma_driver_pci.h
|
||||
+++ b/include/linux/bcma/bcma_driver_pci.h
|
||||
@@ -238,13 +238,13 @@ struct bcma_drv_pci {
|
||||
#define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val)
|
||||
#define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val)
|
||||
|
||||
-extern void bcma_core_pci_early_init(struct bcma_drv_pci *pc);
|
||||
-extern void bcma_core_pci_init(struct bcma_drv_pci *pc);
|
||||
-extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
|
||||
- struct bcma_device *core, bool enable);
|
||||
-extern void bcma_core_pci_up(struct bcma_bus *bus);
|
||||
-extern void bcma_core_pci_down(struct bcma_bus *bus);
|
||||
+#ifdef CONFIG_BCMA_DRIVER_PCI
|
||||
extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up);
|
||||
+#else
|
||||
+static inline void bcma_core_pci_power_save(struct bcma_bus *bus, bool up)
|
||||
+{
|
||||
+}
|
||||
+#endif
|
||||
|
||||
extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
|
||||
extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
|
||||
--- a/include/linux/bcma/bcma_driver_pcie2.h
|
||||
+++ b/include/linux/bcma/bcma_driver_pcie2.h
|
||||
@@ -143,6 +143,8 @@
|
||||
|
||||
struct bcma_drv_pcie2 {
|
||||
struct bcma_device *core;
|
||||
+
|
||||
+ u16 reqsize;
|
||||
};
|
||||
|
||||
#define pcie2_read16(pcie2, offset) bcma_read16((pcie2)->core, offset)
|
||||
@@ -153,6 +155,4 @@ struct bcma_drv_pcie2 {
|
||||
#define pcie2_set32(pcie2, offset, set) bcma_set32((pcie2)->core, offset, set)
|
||||
#define pcie2_mask32(pcie2, offset, mask) bcma_mask32((pcie2)->core, offset, mask)
|
||||
|
||||
-void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2);
|
||||
-
|
||||
#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */
|
||||
--- a/drivers/bcma/Kconfig
|
||||
+++ b/drivers/bcma/Kconfig
|
||||
@@ -26,6 +26,7 @@ config BCMA_HOST_PCI_POSSIBLE
|
||||
config BCMA_HOST_PCI
|
||||
bool "Support for BCMA on PCI-host bus"
|
||||
depends on BCMA_HOST_PCI_POSSIBLE
|
||||
+ select BCMA_DRIVER_PCI
|
||||
default y
|
||||
|
||||
config BCMA_DRIVER_PCI_HOSTMODE
|
||||
@@ -44,6 +45,22 @@ config BCMA_HOST_SOC
|
||||
|
||||
If unsure, say N
|
||||
|
||||
+config BCMA_DRIVER_PCI
|
||||
+ bool "BCMA Broadcom PCI core driver"
|
||||
+ depends on BCMA && PCI
|
||||
+ default y
|
||||
+ help
|
||||
+ BCMA bus may have many versions of PCIe core. This driver
|
||||
+ supports:
|
||||
+ 1) PCIe core working in clientmode
|
||||
+ 2) PCIe Gen 2 clientmode core
|
||||
+
|
||||
+ In general PCIe (Gen 2) clientmode core is required on PCIe
|
||||
+ hosted buses. It's responsible for initialization and basic
|
||||
+ hardware management.
|
||||
+ This driver is also prerequisite for a hostmode PCIe core
|
||||
+ support.
|
||||
+
|
||||
config BCMA_DRIVER_MIPS
|
||||
bool "BCMA Broadcom MIPS core driver"
|
||||
depends on BCMA && MIPS
|
||||
--- a/drivers/bcma/Makefile
|
||||
+++ b/drivers/bcma/Makefile
|
||||
@@ -3,8 +3,8 @@ bcma-y += driver_chipcommon.o driver
|
||||
bcma-y += driver_chipcommon_b.o
|
||||
bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o
|
||||
bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o
|
||||
-bcma-y += driver_pci.o
|
||||
-bcma-y += driver_pcie2.o
|
||||
+bcma-$(CONFIG_BCMA_DRIVER_PCI) += driver_pci.o
|
||||
+bcma-$(CONFIG_BCMA_DRIVER_PCI) += driver_pcie2.o
|
||||
bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o
|
||||
bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
|
||||
bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o
|
||||
--- a/include/linux/bcma/bcma_driver_chipcommon.h
|
||||
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
|
||||
@@ -663,14 +663,6 @@ struct bcma_drv_cc_b {
|
||||
#define bcma_cc_maskset32(cc, offset, mask, set) \
|
||||
bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set))
|
||||
|
||||
-extern void bcma_core_chipcommon_init(struct bcma_drv_cc *cc);
|
||||
-extern void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc);
|
||||
-
|
||||
-extern void bcma_chipco_suspend(struct bcma_drv_cc *cc);
|
||||
-extern void bcma_chipco_resume(struct bcma_drv_cc *cc);
|
||||
-
|
||||
-void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
|
||||
-
|
||||
extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks);
|
||||
|
||||
extern u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc);
|
||||
@@ -690,9 +682,6 @@ u32 bcma_chipco_gpio_pullup(struct bcma_
|
||||
u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value);
|
||||
|
||||
/* PMU support */
|
||||
-extern void bcma_pmu_init(struct bcma_drv_cc *cc);
|
||||
-extern void bcma_pmu_early_init(struct bcma_drv_cc *cc);
|
||||
-
|
||||
extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset,
|
||||
u32 value);
|
||||
extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset,
|
||||
--- a/include/linux/bcma/bcma_driver_gmac_cmn.h
|
||||
+++ b/include/linux/bcma/bcma_driver_gmac_cmn.h
|
||||
@@ -91,10 +91,4 @@ struct bcma_drv_gmac_cmn {
|
||||
#define gmac_cmn_write16(gc, offset, val) bcma_write16((gc)->core, offset, val)
|
||||
#define gmac_cmn_write32(gc, offset, val) bcma_write32((gc)->core, offset, val)
|
||||
|
||||
-#ifdef CONFIG_BCMA_DRIVER_GMAC_CMN
|
||||
-extern void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc);
|
||||
-#else
|
||||
-static inline void bcma_core_gmac_cmn_init(struct bcma_drv_gmac_cmn *gc) { }
|
||||
-#endif
|
||||
-
|
||||
#endif /* LINUX_BCMA_DRIVER_GMAC_CMN_H_ */
|
||||
--- a/include/linux/bcma/bcma_driver_mips.h
|
||||
+++ b/include/linux/bcma/bcma_driver_mips.h
|
||||
@@ -39,21 +39,6 @@ struct bcma_drv_mips {
|
||||
u8 early_setup_done:1;
|
||||
};
|
||||
|
||||
-#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
-extern void bcma_core_mips_init(struct bcma_drv_mips *mcore);
|
||||
-extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
|
||||
-
|
||||
-extern unsigned int bcma_core_mips_irq(struct bcma_device *dev);
|
||||
-#else
|
||||
-static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { }
|
||||
-static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { }
|
||||
-
|
||||
-static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev)
|
||||
-{
|
||||
- return 0;
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore);
|
||||
|
||||
#endif /* LINUX_BCMA_DRIVER_MIPS_H_ */
|
|
@ -1,86 +0,0 @@
|
|||
--- a/drivers/bcma/driver_gpio.c
|
||||
+++ b/drivers/bcma/driver_gpio.c
|
||||
@@ -226,6 +226,7 @@ int bcma_gpio_init(struct bcma_drv_cc *c
|
||||
chip->of_node = cc->core->dev.of_node;
|
||||
#endif
|
||||
switch (bus->chipinfo.id) {
|
||||
+ case BCMA_CHIP_ID_BCM4707:
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM53572:
|
||||
chip->ngpio = 32;
|
||||
@@ -235,16 +236,17 @@ int bcma_gpio_init(struct bcma_drv_cc *c
|
||||
}
|
||||
|
||||
/*
|
||||
- * On MIPS we register GPIO devices (LEDs, buttons) using absolute GPIO
|
||||
- * pin numbers. We don't have Device Tree there and we can't really use
|
||||
- * relative (per chip) numbers.
|
||||
- * So let's use predictable base for BCM47XX and "random" for all other.
|
||||
+ * Register SoC GPIO devices with absolute GPIO pin base.
|
||||
+ * On MIPS, we don't have Device Tree and we can't use relative (per chip)
|
||||
+ * GPIO numbers.
|
||||
+ * On some ARM devices, user space may want to access some system GPIO
|
||||
+ * pins directly, which is easier to do with a predictable GPIO base.
|
||||
*/
|
||||
-#if IS_BUILTIN(CONFIG_BCM47XX)
|
||||
- chip->base = bus->num * BCMA_GPIO_MAX_PINS;
|
||||
-#else
|
||||
- chip->base = -1;
|
||||
-#endif
|
||||
+ if (IS_BUILTIN(CONFIG_BCM47XX) ||
|
||||
+ cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
+ chip->base = bus->num * BCMA_GPIO_MAX_PINS;
|
||||
+ else
|
||||
+ chip->base = -1;
|
||||
|
||||
err = bcma_gpio_irq_domain_init(cc);
|
||||
if (err)
|
||||
--- a/drivers/bcma/Kconfig
|
||||
+++ b/drivers/bcma/Kconfig
|
||||
@@ -29,12 +29,6 @@ config BCMA_HOST_PCI
|
||||
select BCMA_DRIVER_PCI
|
||||
default y
|
||||
|
||||
-config BCMA_DRIVER_PCI_HOSTMODE
|
||||
- bool "Driver for PCI core working in hostmode"
|
||||
- depends on BCMA && MIPS && BCMA_HOST_PCI
|
||||
- help
|
||||
- PCI core hostmode operation (external PCI bus).
|
||||
-
|
||||
config BCMA_HOST_SOC
|
||||
bool "Support for BCMA in a SoC"
|
||||
depends on BCMA
|
||||
@@ -61,6 +55,12 @@ config BCMA_DRIVER_PCI
|
||||
This driver is also prerequisite for a hostmode PCIe core
|
||||
support.
|
||||
|
||||
+config BCMA_DRIVER_PCI_HOSTMODE
|
||||
+ bool "Driver for PCI core working in hostmode"
|
||||
+ depends on BCMA && MIPS && BCMA_DRIVER_PCI
|
||||
+ help
|
||||
+ PCI core hostmode operation (external PCI bus).
|
||||
+
|
||||
config BCMA_DRIVER_MIPS
|
||||
bool "BCMA Broadcom MIPS core driver"
|
||||
depends on BCMA && MIPS
|
||||
--- a/include/linux/bcma/bcma_driver_pci.h
|
||||
+++ b/include/linux/bcma/bcma_driver_pci.h
|
||||
@@ -246,7 +246,18 @@ static inline void bcma_core_pci_power_s
|
||||
}
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
|
||||
extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
|
||||
extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
|
||||
+#else
|
||||
+static inline int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev)
|
||||
+{
|
||||
+ return -ENOTSUPP;
|
||||
+}
|
||||
+static inline int bcma_core_pci_plat_dev_init(struct pci_dev *dev)
|
||||
+{
|
||||
+ return -ENOTSUPP;
|
||||
+}
|
||||
+#endif
|
||||
|
||||
#endif /* LINUX_BCMA_DRIVER_PCI_H_ */
|
|
@ -1,26 +0,0 @@
|
|||
commit 55acca90da52b85299c033354e51ddaa7b73e019
|
||||
Author: Hante Meuleman <meuleman@broadcom.com>
|
||||
Date: Fri Sep 18 22:08:17 2015 +0200
|
||||
|
||||
brcmfmac: Add support for the BCM4365 and BCM4366 PCIE devices.
|
||||
|
||||
This patch adds support for the BCM4365 and BCM4366 11ac Wave2
|
||||
PCIE devices.
|
||||
|
||||
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
|
||||
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
|
||||
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
|
||||
Signed-off-by: Arend van Spriel <arend@broadcom.com>
|
||||
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
|
||||
|
||||
--- a/include/linux/bcma/bcma.h
|
||||
+++ b/include/linux/bcma/bcma.h
|
||||
@@ -151,6 +151,8 @@ struct bcma_host_ops {
|
||||
#define BCMA_CORE_PCIE2 0x83C /* PCI Express Gen2 */
|
||||
#define BCMA_CORE_USB30_DEV 0x83D
|
||||
#define BCMA_CORE_ARM_CR4 0x83E
|
||||
+#define BCMA_CORE_ARM_CA7 0x847
|
||||
+#define BCMA_CORE_SYS_MEM 0x849
|
||||
#define BCMA_CORE_DEFAULT 0xFFF
|
||||
|
||||
#define BCMA_MAX_NR_CORES 16
|
|
@ -1,52 +0,0 @@
|
|||
--- /dev/null
|
||||
+++ b/include/linux/bcm47xx_nvram.h
|
||||
@@ -0,0 +1,49 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License as published by the
|
||||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||
+ * option) any later version.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __BCM47XX_NVRAM_H
|
||||
+#define __BCM47XX_NVRAM_H
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+
|
||||
+#ifdef CONFIG_BCM47XX_NVRAM
|
||||
+int bcm47xx_nvram_init_from_mem(u32 base, u32 lim);
|
||||
+int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len);
|
||||
+int bcm47xx_nvram_gpio_pin(const char *name);
|
||||
+char *bcm47xx_nvram_get_contents(size_t *val_len);
|
||||
+static inline void bcm47xx_nvram_release_contents(char *nvram)
|
||||
+{
|
||||
+ vfree(nvram);
|
||||
+};
|
||||
+#else
|
||||
+static inline int bcm47xx_nvram_init_from_mem(u32 base, u32 lim)
|
||||
+{
|
||||
+ return -ENOTSUPP;
|
||||
+};
|
||||
+static inline int bcm47xx_nvram_getenv(const char *name, char *val,
|
||||
+ size_t val_len)
|
||||
+{
|
||||
+ return -ENOTSUPP;
|
||||
+};
|
||||
+static inline int bcm47xx_nvram_gpio_pin(const char *name)
|
||||
+{
|
||||
+ return -ENOTSUPP;
|
||||
+};
|
||||
+
|
||||
+static inline char *bcm47xx_nvram_get_contents(size_t *val_len)
|
||||
+{
|
||||
+ return NULL;
|
||||
+};
|
||||
+
|
||||
+static inline void bcm47xx_nvram_release_contents(char *nvram)
|
||||
+{
|
||||
+};
|
||||
+#endif
|
||||
+
|
||||
+#endif /* __BCM47XX_NVRAM_H */
|
|
@ -1,21 +0,0 @@
|
|||
From: Vadim Kochan <vadim4j@gmail.com>
|
||||
Date: Mon, 12 Jan 2015 16:34:05 +0200
|
||||
Subject: [PATCH] nl80211: Allow set network namespace by fd
|
||||
|
||||
Added new NL80211_ATTR_NETNS_FD which allows to
|
||||
set namespace via nl80211 by fd.
|
||||
|
||||
Signed-off-by: Vadim Kochan <vadim4j@gmail.com>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/core/net_namespace.c
|
||||
+++ b/net/core/net_namespace.c
|
||||
@@ -361,6 +361,7 @@ struct net *get_net_ns_by_fd(int fd)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
+EXPORT_SYMBOL_GPL(get_net_ns_by_fd);
|
||||
|
||||
struct net *get_net_ns_by_pid(pid_t pid)
|
||||
{
|
|
@ -1,49 +0,0 @@
|
|||
--- a/drivers/bcma/main.c
|
||||
+++ b/drivers/bcma/main.c
|
||||
@@ -637,11 +637,36 @@ static int bcma_device_uevent(struct dev
|
||||
core->id.rev, core->id.class);
|
||||
}
|
||||
|
||||
-static int __init bcma_modinit(void)
|
||||
+static unsigned int bcma_bus_registered;
|
||||
+
|
||||
+/*
|
||||
+ * If built-in, bus has to be registered early, before any driver calls
|
||||
+ * bcma_driver_register.
|
||||
+ * Otherwise registering driver would trigger BUG in driver_register.
|
||||
+ */
|
||||
+static int __init bcma_init_bus_register(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
+ if (bcma_bus_registered)
|
||||
+ return 0;
|
||||
+
|
||||
err = bus_register(&bcma_bus_type);
|
||||
+ if (!err)
|
||||
+ bcma_bus_registered = 1;
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+#ifndef MODULE
|
||||
+fs_initcall(bcma_init_bus_register);
|
||||
+#endif
|
||||
+
|
||||
+/* Main initialization has to be done with SPI/mtd/NAND/SPROM available */
|
||||
+static int __init bcma_modinit(void)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ err = bcma_init_bus_register();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -660,7 +685,7 @@ static int __init bcma_modinit(void)
|
||||
|
||||
return err;
|
||||
}
|
||||
-fs_initcall(bcma_modinit);
|
||||
+module_init(bcma_modinit);
|
||||
|
||||
static void __exit bcma_modexit(void)
|
||||
{
|
|
@ -1,716 +0,0 @@
|
|||
--- a/drivers/bcma/driver_chipcommon.c
|
||||
+++ b/drivers/bcma/driver_chipcommon.c
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
|
||||
+static void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
|
||||
+
|
||||
static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
|
||||
u32 mask, u32 value)
|
||||
{
|
||||
@@ -113,8 +115,37 @@ int bcma_chipco_watchdog_register(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void bcma_core_chipcommon_flash_detect(struct bcma_drv_cc *cc)
|
||||
+{
|
||||
+ struct bcma_bus *bus = cc->core->bus;
|
||||
+
|
||||
+ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
|
||||
+ case BCMA_CC_FLASHT_STSER:
|
||||
+ case BCMA_CC_FLASHT_ATSER:
|
||||
+ bcma_debug(bus, "Found serial flash\n");
|
||||
+ bcma_sflash_init(cc);
|
||||
+ break;
|
||||
+ case BCMA_CC_FLASHT_PARA:
|
||||
+ bcma_debug(bus, "Found parallel flash\n");
|
||||
+ bcma_pflash_init(cc);
|
||||
+ break;
|
||||
+ default:
|
||||
+ bcma_err(bus, "Flash type not supported\n");
|
||||
+ }
|
||||
+
|
||||
+ if (cc->core->id.rev == 38 ||
|
||||
+ bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
|
||||
+ if (cc->capabilities & BCMA_CC_CAP_NFLASH) {
|
||||
+ bcma_debug(bus, "Found NAND flash\n");
|
||||
+ bcma_nflash_init(cc);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
+ struct bcma_bus *bus = cc->core->bus;
|
||||
+
|
||||
if (cc->early_setup_done)
|
||||
return;
|
||||
|
||||
@@ -129,6 +160,12 @@ void bcma_core_chipcommon_early_init(str
|
||||
if (cc->capabilities & BCMA_CC_CAP_PMU)
|
||||
bcma_pmu_early_init(cc);
|
||||
|
||||
+ if (IS_BUILTIN(CONFIG_BCM47XX) && bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
+ bcma_chipco_serial_init(cc);
|
||||
+
|
||||
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
+ bcma_core_chipcommon_flash_detect(cc);
|
||||
+
|
||||
cc->early_setup_done = true;
|
||||
}
|
||||
|
||||
@@ -185,11 +222,12 @@ u32 bcma_chipco_watchdog_timer_set(struc
|
||||
ticks = 2;
|
||||
else if (ticks > maxt)
|
||||
ticks = maxt;
|
||||
- bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks);
|
||||
} else {
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
|
||||
if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4707 &&
|
||||
+ bus->chipinfo.id != BCMA_CHIP_ID_BCM47094 &&
|
||||
bus->chipinfo.id != BCMA_CHIP_ID_BCM53018)
|
||||
bcma_core_set_clockmode(cc->core,
|
||||
ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC);
|
||||
@@ -314,9 +352,9 @@ u32 bcma_chipco_gpio_pulldown(struct bcm
|
||||
return res;
|
||||
}
|
||||
|
||||
-#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
-void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
|
||||
+static void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
+#if IS_BUILTIN(CONFIG_BCM47XX)
|
||||
unsigned int irq;
|
||||
u32 baud_base;
|
||||
u32 i;
|
||||
@@ -358,5 +396,5 @@ void bcma_chipco_serial_init(struct bcma
|
||||
ports[i].baud_base = baud_base;
|
||||
ports[i].reg_shift = 0;
|
||||
}
|
||||
+#endif /* CONFIG_BCM47XX */
|
||||
}
|
||||
-#endif /* CONFIG_BCMA_DRIVER_MIPS */
|
||||
--- a/drivers/bcma/driver_chipcommon_pmu.c
|
||||
+++ b/drivers/bcma/driver_chipcommon_pmu.c
|
||||
@@ -15,44 +15,44 @@
|
||||
|
||||
u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
|
||||
{
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
|
||||
- bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
|
||||
- return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset);
|
||||
+ bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_ADDR);
|
||||
+ return bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_DATA);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_pll_read);
|
||||
|
||||
void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value)
|
||||
{
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
|
||||
- bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset);
|
||||
+ bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_ADDR);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_pll_write);
|
||||
|
||||
void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
|
||||
u32 set)
|
||||
{
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
|
||||
- bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
|
||||
- bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset);
|
||||
+ bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_ADDR);
|
||||
+ bcma_pmu_maskset32(cc, BCMA_CC_PMU_PLLCTL_DATA, mask, set);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset);
|
||||
|
||||
void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
|
||||
u32 offset, u32 mask, u32 set)
|
||||
{
|
||||
- bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset);
|
||||
- bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR);
|
||||
- bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_CHIPCTL_ADDR, offset);
|
||||
+ bcma_pmu_read32(cc, BCMA_CC_PMU_CHIPCTL_ADDR);
|
||||
+ bcma_pmu_maskset32(cc, BCMA_CC_PMU_CHIPCTL_DATA, mask, set);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset);
|
||||
|
||||
void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
|
||||
u32 set)
|
||||
{
|
||||
- bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset);
|
||||
- bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR);
|
||||
- bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_REGCTL_ADDR, offset);
|
||||
+ bcma_pmu_read32(cc, BCMA_CC_PMU_REGCTL_ADDR);
|
||||
+ bcma_pmu_maskset32(cc, BCMA_CC_PMU_REGCTL_DATA, mask, set);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset);
|
||||
|
||||
@@ -60,18 +60,18 @@ static u32 bcma_pmu_xtalfreq(struct bcma
|
||||
{
|
||||
u32 ilp_ctl, alp_hz;
|
||||
|
||||
- if (!(bcma_cc_read32(cc, BCMA_CC_PMU_STAT) &
|
||||
+ if (!(bcma_pmu_read32(cc, BCMA_CC_PMU_STAT) &
|
||||
BCMA_CC_PMU_STAT_EXT_LPO_AVAIL))
|
||||
return 0;
|
||||
|
||||
- bcma_cc_write32(cc, BCMA_CC_PMU_XTAL_FREQ,
|
||||
- BIT(BCMA_CC_PMU_XTAL_FREQ_MEASURE_SHIFT));
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_XTAL_FREQ,
|
||||
+ BIT(BCMA_CC_PMU_XTAL_FREQ_MEASURE_SHIFT));
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
- ilp_ctl = bcma_cc_read32(cc, BCMA_CC_PMU_XTAL_FREQ);
|
||||
+ ilp_ctl = bcma_pmu_read32(cc, BCMA_CC_PMU_XTAL_FREQ);
|
||||
ilp_ctl &= BCMA_CC_PMU_XTAL_FREQ_ILPCTL_MASK;
|
||||
|
||||
- bcma_cc_write32(cc, BCMA_CC_PMU_XTAL_FREQ, 0);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_XTAL_FREQ, 0);
|
||||
|
||||
alp_hz = ilp_ctl * 32768 / 4;
|
||||
return (alp_hz + 50000) / 100000 * 100;
|
||||
@@ -127,8 +127,8 @@ static void bcma_pmu2_pll_init0(struct b
|
||||
mask = (u32)~(BCMA_RES_4314_HT_AVAIL |
|
||||
BCMA_RES_4314_MACPHY_CLK_AVAIL);
|
||||
|
||||
- bcma_cc_mask32(cc, BCMA_CC_PMU_MINRES_MSK, mask);
|
||||
- bcma_cc_mask32(cc, BCMA_CC_PMU_MAXRES_MSK, mask);
|
||||
+ bcma_pmu_mask32(cc, BCMA_CC_PMU_MINRES_MSK, mask);
|
||||
+ bcma_pmu_mask32(cc, BCMA_CC_PMU_MAXRES_MSK, mask);
|
||||
bcma_wait_value(cc->core, BCMA_CLKCTLST,
|
||||
BCMA_CLKCTLST_HAVEHT, 0, 20000);
|
||||
break;
|
||||
@@ -140,7 +140,7 @@ static void bcma_pmu2_pll_init0(struct b
|
||||
|
||||
/* Flush */
|
||||
if (cc->pmu.rev >= 2)
|
||||
- bcma_cc_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
|
||||
+ bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
|
||||
|
||||
/* TODO: Do we need to update OTP? */
|
||||
}
|
||||
@@ -195,9 +195,9 @@ static void bcma_pmu_resources_init(stru
|
||||
|
||||
/* Set the resource masks. */
|
||||
if (min_msk)
|
||||
- bcma_cc_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk);
|
||||
if (max_msk)
|
||||
- bcma_cc_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk);
|
||||
|
||||
/*
|
||||
* Add some delay; allow resources to come up and settle.
|
||||
@@ -269,23 +269,33 @@ static void bcma_pmu_workarounds(struct
|
||||
|
||||
void bcma_pmu_early_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
+ struct bcma_bus *bus = cc->core->bus;
|
||||
u32 pmucap;
|
||||
|
||||
- pmucap = bcma_cc_read32(cc, BCMA_CC_PMU_CAP);
|
||||
+ if (cc->core->id.rev >= 35 &&
|
||||
+ cc->capabilities_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) {
|
||||
+ cc->pmu.core = bcma_find_core(bus, BCMA_CORE_PMU);
|
||||
+ if (!cc->pmu.core)
|
||||
+ bcma_warn(bus, "Couldn't find expected PMU core");
|
||||
+ }
|
||||
+ if (!cc->pmu.core)
|
||||
+ cc->pmu.core = cc->core;
|
||||
+
|
||||
+ pmucap = bcma_pmu_read32(cc, BCMA_CC_PMU_CAP);
|
||||
cc->pmu.rev = (pmucap & BCMA_CC_PMU_CAP_REVISION);
|
||||
|
||||
- bcma_debug(cc->core->bus, "Found rev %u PMU (capabilities 0x%08X)\n",
|
||||
- cc->pmu.rev, pmucap);
|
||||
+ bcma_debug(bus, "Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev,
|
||||
+ pmucap);
|
||||
}
|
||||
|
||||
void bcma_pmu_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
if (cc->pmu.rev == 1)
|
||||
- bcma_cc_mask32(cc, BCMA_CC_PMU_CTL,
|
||||
- ~BCMA_CC_PMU_CTL_NOILPONW);
|
||||
+ bcma_pmu_mask32(cc, BCMA_CC_PMU_CTL,
|
||||
+ ~BCMA_CC_PMU_CTL_NOILPONW);
|
||||
else
|
||||
- bcma_cc_set32(cc, BCMA_CC_PMU_CTL,
|
||||
- BCMA_CC_PMU_CTL_NOILPONW);
|
||||
+ bcma_pmu_set32(cc, BCMA_CC_PMU_CTL,
|
||||
+ BCMA_CC_PMU_CTL_NOILPONW);
|
||||
|
||||
bcma_pmu_pll_init(cc);
|
||||
bcma_pmu_resources_init(cc);
|
||||
@@ -472,8 +482,8 @@ u32 bcma_pmu_get_cpu_clock(struct bcma_d
|
||||
static void bcma_pmu_spuravoid_pll_write(struct bcma_drv_cc *cc, u32 offset,
|
||||
u32 value)
|
||||
{
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, value);
|
||||
}
|
||||
|
||||
void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
||||
@@ -497,20 +507,20 @@ void bcma_pmu_spuravoid_pllupdate(struct
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) ? 6 : 0;
|
||||
|
||||
/* RMW only the P1 divider */
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR,
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR,
|
||||
BCMA_CC_PMU_PLL_CTL0 + phypll_offset);
|
||||
- tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
|
||||
+ tmp = bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_DATA);
|
||||
tmp &= (~(BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK));
|
||||
tmp |= (bcm5357_bcm43236_p1div[spuravoid] << BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT);
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, tmp);
|
||||
|
||||
/* RMW only the int feedback divider */
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR,
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR,
|
||||
BCMA_CC_PMU_PLL_CTL2 + phypll_offset);
|
||||
- tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
|
||||
+ tmp = bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_DATA);
|
||||
tmp &= ~(BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK);
|
||||
tmp |= (bcm5357_bcm43236_ndiv[spuravoid]) << BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT;
|
||||
- bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, tmp);
|
||||
|
||||
tmp = BCMA_CC_PMU_CTL_PLL_UPD;
|
||||
break;
|
||||
@@ -646,7 +656,7 @@ void bcma_pmu_spuravoid_pllupdate(struct
|
||||
break;
|
||||
}
|
||||
|
||||
- tmp |= bcma_cc_read32(cc, BCMA_CC_PMU_CTL);
|
||||
- bcma_cc_write32(cc, BCMA_CC_PMU_CTL, tmp);
|
||||
+ tmp |= bcma_pmu_read32(cc, BCMA_CC_PMU_CTL);
|
||||
+ bcma_pmu_write32(cc, BCMA_CC_PMU_CTL, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_pmu_spuravoid_pllupdate);
|
||||
--- a/drivers/bcma/driver_chipcommon_sflash.c
|
||||
+++ b/drivers/bcma/driver_chipcommon_sflash.c
|
||||
@@ -38,6 +38,7 @@ static const struct bcma_sflash_tbl_e bc
|
||||
{ "M25P32", 0x15, 0x10000, 64, },
|
||||
{ "M25P64", 0x16, 0x10000, 128, },
|
||||
{ "M25FL128", 0x17, 0x10000, 256, },
|
||||
+ { "MX25L25635F", 0x18, 0x10000, 512, },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
--- a/drivers/bcma/scan.c
|
||||
+++ b/drivers/bcma/scan.c
|
||||
@@ -98,6 +98,9 @@ static const struct bcma_device_id_name
|
||||
{ BCMA_CORE_SHIM, "SHIM" },
|
||||
{ BCMA_CORE_PCIE2, "PCIe Gen2" },
|
||||
{ BCMA_CORE_ARM_CR4, "ARM CR4" },
|
||||
+ { BCMA_CORE_GCI, "GCI" },
|
||||
+ { BCMA_CORE_CMEM, "CNDS DDR2/3 memory controller" },
|
||||
+ { BCMA_CORE_ARM_CA7, "ARM CA7" },
|
||||
{ BCMA_CORE_DEFAULT, "Default" },
|
||||
};
|
||||
|
||||
@@ -315,6 +318,8 @@ static int bcma_get_next_core(struct bcm
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_4706_MAC_GBIT_COMMON:
|
||||
case BCMA_CORE_NS_CHIPCOMMON_B:
|
||||
+ case BCMA_CORE_PMU:
|
||||
+ case BCMA_CORE_GCI:
|
||||
/* Not used yet: case BCMA_CORE_OOB_ROUTER: */
|
||||
break;
|
||||
default:
|
||||
--- a/drivers/net/wireless/b43/main.c
|
||||
+++ b/drivers/net/wireless/b43/main.c
|
||||
@@ -1215,10 +1215,10 @@ void b43_wireless_core_phy_pll_reset(str
|
||||
case B43_BUS_BCMA:
|
||||
bcma_cc = &dev->dev->bdev->bus->drv_cc;
|
||||
|
||||
- bcma_cc_write32(bcma_cc, BCMA_CC_CHIPCTL_ADDR, 0);
|
||||
- bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4);
|
||||
- bcma_cc_set32(bcma_cc, BCMA_CC_CHIPCTL_DATA, 0x4);
|
||||
- bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4);
|
||||
+ bcma_cc_write32(bcma_cc, BCMA_CC_PMU_CHIPCTL_ADDR, 0);
|
||||
+ bcma_cc_mask32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, ~0x4);
|
||||
+ bcma_cc_set32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, 0x4);
|
||||
+ bcma_cc_mask32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, ~0x4);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_B43_SSB
|
||||
--- a/include/linux/bcma/bcma.h
|
||||
+++ b/include/linux/bcma/bcma.h
|
||||
@@ -151,6 +151,8 @@ struct bcma_host_ops {
|
||||
#define BCMA_CORE_PCIE2 0x83C /* PCI Express Gen2 */
|
||||
#define BCMA_CORE_USB30_DEV 0x83D
|
||||
#define BCMA_CORE_ARM_CR4 0x83E
|
||||
+#define BCMA_CORE_GCI 0x840
|
||||
+#define BCMA_CORE_CMEM 0x846 /* CNDS DDR2/3 memory controller */
|
||||
#define BCMA_CORE_ARM_CA7 0x847
|
||||
#define BCMA_CORE_SYS_MEM 0x849
|
||||
#define BCMA_CORE_DEFAULT 0xFFF
|
||||
@@ -200,6 +202,7 @@ struct bcma_host_ops {
|
||||
#define BCMA_PKG_ID_BCM4707 1
|
||||
#define BCMA_PKG_ID_BCM4708 2
|
||||
#define BCMA_PKG_ID_BCM4709 0
|
||||
+#define BCMA_CHIP_ID_BCM47094 53030
|
||||
#define BCMA_CHIP_ID_BCM53018 53018
|
||||
|
||||
/* Board types (on PCI usually equals to the subsystem dev id) */
|
||||
--- a/include/linux/bcma/bcma_driver_chipcommon.h
|
||||
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
|
||||
@@ -217,6 +217,11 @@
|
||||
#define BCMA_CC_CLKDIV_JTAG_SHIFT 8
|
||||
#define BCMA_CC_CLKDIV_UART 0x000000FF
|
||||
#define BCMA_CC_CAP_EXT 0x00AC /* Capabilities */
|
||||
+#define BCMA_CC_CAP_EXT_SECI_PRESENT 0x00000001
|
||||
+#define BCMA_CC_CAP_EXT_GSIO_PRESENT 0x00000002
|
||||
+#define BCMA_CC_CAP_EXT_GCI_PRESENT 0x00000004
|
||||
+#define BCMA_CC_CAP_EXT_SECI_PUART_PRESENT 0x00000008 /* UART present */
|
||||
+#define BCMA_CC_CAP_EXT_AOB_PRESENT 0x00000040
|
||||
#define BCMA_CC_PLLONDELAY 0x00B0 /* Rev >= 4 only */
|
||||
#define BCMA_CC_FREFSELDELAY 0x00B4 /* Rev >= 4 only */
|
||||
#define BCMA_CC_SLOWCLKCTL 0x00B8 /* 6 <= Rev <= 9 only */
|
||||
@@ -351,12 +356,12 @@
|
||||
#define BCMA_CC_PMU_RES_REQTS 0x0640 /* PMU res req timer sel */
|
||||
#define BCMA_CC_PMU_RES_REQT 0x0644 /* PMU res req timer */
|
||||
#define BCMA_CC_PMU_RES_REQM 0x0648 /* PMU res req mask */
|
||||
-#define BCMA_CC_CHIPCTL_ADDR 0x0650
|
||||
-#define BCMA_CC_CHIPCTL_DATA 0x0654
|
||||
-#define BCMA_CC_REGCTL_ADDR 0x0658
|
||||
-#define BCMA_CC_REGCTL_DATA 0x065C
|
||||
-#define BCMA_CC_PLLCTL_ADDR 0x0660
|
||||
-#define BCMA_CC_PLLCTL_DATA 0x0664
|
||||
+#define BCMA_CC_PMU_CHIPCTL_ADDR 0x0650
|
||||
+#define BCMA_CC_PMU_CHIPCTL_DATA 0x0654
|
||||
+#define BCMA_CC_PMU_REGCTL_ADDR 0x0658
|
||||
+#define BCMA_CC_PMU_REGCTL_DATA 0x065C
|
||||
+#define BCMA_CC_PMU_PLLCTL_ADDR 0x0660
|
||||
+#define BCMA_CC_PMU_PLLCTL_DATA 0x0664
|
||||
#define BCMA_CC_PMU_STRAPOPT 0x0668 /* (corerev >= 28) */
|
||||
#define BCMA_CC_PMU_XTAL_FREQ 0x066C /* (pmurev >= 10) */
|
||||
#define BCMA_CC_PMU_XTAL_FREQ_ILPCTL_MASK 0x00001FFF
|
||||
@@ -566,17 +571,16 @@
|
||||
* Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU)
|
||||
*/
|
||||
struct bcma_chipcommon_pmu {
|
||||
+ struct bcma_device *core; /* Can be separated core or just ChipCommon one */
|
||||
u8 rev; /* PMU revision */
|
||||
u32 crystalfreq; /* The active crystal frequency (in kHz) */
|
||||
};
|
||||
|
||||
-#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
+#ifdef CONFIG_BCMA_PFLASH
|
||||
struct bcma_pflash {
|
||||
bool present;
|
||||
- u8 buswidth;
|
||||
- u32 window;
|
||||
- u32 window_size;
|
||||
};
|
||||
+#endif
|
||||
|
||||
#ifdef CONFIG_BCMA_SFLASH
|
||||
struct bcma_sflash {
|
||||
@@ -602,6 +606,7 @@ struct bcma_nflash {
|
||||
};
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
struct bcma_serial_port {
|
||||
void *regs;
|
||||
unsigned long clockspeed;
|
||||
@@ -621,8 +626,9 @@ struct bcma_drv_cc {
|
||||
/* Fast Powerup Delay constant */
|
||||
u16 fast_pwrup_delay;
|
||||
struct bcma_chipcommon_pmu pmu;
|
||||
-#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
+#ifdef CONFIG_BCMA_PFLASH
|
||||
struct bcma_pflash pflash;
|
||||
+#endif
|
||||
#ifdef CONFIG_BCMA_SFLASH
|
||||
struct bcma_sflash sflash;
|
||||
#endif
|
||||
@@ -630,6 +636,7 @@ struct bcma_drv_cc {
|
||||
struct bcma_nflash nflash;
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
int nr_serial_ports;
|
||||
struct bcma_serial_port serial_ports[4];
|
||||
#endif /* CONFIG_BCMA_DRIVER_MIPS */
|
||||
@@ -663,6 +670,19 @@ struct bcma_drv_cc_b {
|
||||
#define bcma_cc_maskset32(cc, offset, mask, set) \
|
||||
bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set))
|
||||
|
||||
+/* PMU registers access */
|
||||
+#define bcma_pmu_read32(cc, offset) \
|
||||
+ bcma_read32((cc)->pmu.core, offset)
|
||||
+#define bcma_pmu_write32(cc, offset, val) \
|
||||
+ bcma_write32((cc)->pmu.core, offset, val)
|
||||
+
|
||||
+#define bcma_pmu_mask32(cc, offset, mask) \
|
||||
+ bcma_pmu_write32(cc, offset, bcma_pmu_read32(cc, offset) & (mask))
|
||||
+#define bcma_pmu_set32(cc, offset, set) \
|
||||
+ bcma_pmu_write32(cc, offset, bcma_pmu_read32(cc, offset) | (set))
|
||||
+#define bcma_pmu_maskset32(cc, offset, mask, set) \
|
||||
+ bcma_pmu_write32(cc, offset, (bcma_pmu_read32(cc, offset) & (mask)) | (set))
|
||||
+
|
||||
extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks);
|
||||
|
||||
extern u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc);
|
||||
--- a/drivers/bcma/bcma_private.h
|
||||
+++ b/drivers/bcma/bcma_private.h
|
||||
@@ -44,10 +44,6 @@ int bcma_sprom_get(struct bcma_bus *bus)
|
||||
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc);
|
||||
void bcma_core_chipcommon_init(struct bcma_drv_cc *cc);
|
||||
void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
|
||||
-#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
-void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
|
||||
-extern struct platform_device bcma_pflash_dev;
|
||||
-#endif /* CONFIG_BCMA_DRIVER_MIPS */
|
||||
|
||||
/* driver_chipcommon_b.c */
|
||||
int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb);
|
||||
@@ -59,6 +55,21 @@ void bcma_pmu_init(struct bcma_drv_cc *c
|
||||
u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc);
|
||||
u32 bcma_pmu_get_cpu_clock(struct bcma_drv_cc *cc);
|
||||
|
||||
+/**************************************************
|
||||
+ * driver_chipcommon_sflash.c
|
||||
+ **************************************************/
|
||||
+
|
||||
+#ifdef CONFIG_BCMA_PFLASH
|
||||
+extern struct platform_device bcma_pflash_dev;
|
||||
+int bcma_pflash_init(struct bcma_drv_cc *cc);
|
||||
+#else
|
||||
+static inline int bcma_pflash_init(struct bcma_drv_cc *cc)
|
||||
+{
|
||||
+ bcma_err(cc->core->bus, "Parallel flash not supported\n");
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif /* CONFIG_BCMA_PFLASH */
|
||||
+
|
||||
#ifdef CONFIG_BCMA_SFLASH
|
||||
/* driver_chipcommon_sflash.c */
|
||||
int bcma_sflash_init(struct bcma_drv_cc *cc);
|
||||
--- a/drivers/bcma/driver_gpio.c
|
||||
+++ b/drivers/bcma/driver_gpio.c
|
||||
@@ -229,6 +229,7 @@ int bcma_gpio_init(struct bcma_drv_cc *c
|
||||
case BCMA_CHIP_ID_BCM4707:
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
case BCMA_CHIP_ID_BCM53572:
|
||||
+ case BCMA_CHIP_ID_BCM47094:
|
||||
chip->ngpio = 32;
|
||||
break;
|
||||
default:
|
||||
--- a/drivers/bcma/driver_mips.c
|
||||
+++ b/drivers/bcma/driver_mips.c
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include <linux/bcma/bcma.h>
|
||||
|
||||
-#include <linux/mtd/physmap.h>
|
||||
-#include <linux/platform_device.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial_reg.h>
|
||||
@@ -29,26 +27,6 @@ enum bcma_boot_dev {
|
||||
BCMA_BOOT_DEV_NAND,
|
||||
};
|
||||
|
||||
-static const char * const part_probes[] = { "bcm47xxpart", NULL };
|
||||
-
|
||||
-static struct physmap_flash_data bcma_pflash_data = {
|
||||
- .part_probe_types = part_probes,
|
||||
-};
|
||||
-
|
||||
-static struct resource bcma_pflash_resource = {
|
||||
- .name = "bcma_pflash",
|
||||
- .flags = IORESOURCE_MEM,
|
||||
-};
|
||||
-
|
||||
-struct platform_device bcma_pflash_dev = {
|
||||
- .name = "physmap-flash",
|
||||
- .dev = {
|
||||
- .platform_data = &bcma_pflash_data,
|
||||
- },
|
||||
- .resource = &bcma_pflash_resource,
|
||||
- .num_resources = 1,
|
||||
-};
|
||||
-
|
||||
/* The 47162a0 hangs when reading MIPS DMP registers registers */
|
||||
static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
|
||||
{
|
||||
@@ -269,48 +247,11 @@ static enum bcma_boot_dev bcma_boot_dev(
|
||||
return BCMA_BOOT_DEV_SERIAL;
|
||||
}
|
||||
|
||||
-static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
|
||||
+static void bcma_core_mips_nvram_init(struct bcma_drv_mips *mcore)
|
||||
{
|
||||
struct bcma_bus *bus = mcore->core->bus;
|
||||
- struct bcma_drv_cc *cc = &bus->drv_cc;
|
||||
- struct bcma_pflash *pflash = &cc->pflash;
|
||||
enum bcma_boot_dev boot_dev;
|
||||
|
||||
- switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
|
||||
- case BCMA_CC_FLASHT_STSER:
|
||||
- case BCMA_CC_FLASHT_ATSER:
|
||||
- bcma_debug(bus, "Found serial flash\n");
|
||||
- bcma_sflash_init(cc);
|
||||
- break;
|
||||
- case BCMA_CC_FLASHT_PARA:
|
||||
- bcma_debug(bus, "Found parallel flash\n");
|
||||
- pflash->present = true;
|
||||
- pflash->window = BCMA_SOC_FLASH2;
|
||||
- pflash->window_size = BCMA_SOC_FLASH2_SZ;
|
||||
-
|
||||
- if ((bcma_read32(cc->core, BCMA_CC_FLASH_CFG) &
|
||||
- BCMA_CC_FLASH_CFG_DS) == 0)
|
||||
- pflash->buswidth = 1;
|
||||
- else
|
||||
- pflash->buswidth = 2;
|
||||
-
|
||||
- bcma_pflash_data.width = pflash->buswidth;
|
||||
- bcma_pflash_resource.start = pflash->window;
|
||||
- bcma_pflash_resource.end = pflash->window + pflash->window_size;
|
||||
-
|
||||
- break;
|
||||
- default:
|
||||
- bcma_err(bus, "Flash type not supported\n");
|
||||
- }
|
||||
-
|
||||
- if (cc->core->id.rev == 38 ||
|
||||
- bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
|
||||
- if (cc->capabilities & BCMA_CC_CAP_NFLASH) {
|
||||
- bcma_debug(bus, "Found NAND flash\n");
|
||||
- bcma_nflash_init(cc);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
/* Determine flash type this SoC boots from */
|
||||
boot_dev = bcma_boot_dev(bus);
|
||||
switch (boot_dev) {
|
||||
@@ -328,13 +269,10 @@ static void bcma_core_mips_flash_detect(
|
||||
|
||||
void bcma_core_mips_early_init(struct bcma_drv_mips *mcore)
|
||||
{
|
||||
- struct bcma_bus *bus = mcore->core->bus;
|
||||
-
|
||||
if (mcore->early_setup_done)
|
||||
return;
|
||||
|
||||
- bcma_chipco_serial_init(&bus->drv_cc);
|
||||
- bcma_core_mips_flash_detect(mcore);
|
||||
+ bcma_core_mips_nvram_init(mcore);
|
||||
|
||||
mcore->early_setup_done = true;
|
||||
}
|
||||
--- a/drivers/bcma/host_pci.c
|
||||
+++ b/drivers/bcma/host_pci.c
|
||||
@@ -294,7 +294,7 @@ static const struct pci_device_id bcma_p
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4360) },
|
||||
- { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) },
|
||||
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0016) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
|
||||
--- a/drivers/bcma/Kconfig
|
||||
+++ b/drivers/bcma/Kconfig
|
||||
@@ -70,6 +70,11 @@ config BCMA_DRIVER_MIPS
|
||||
|
||||
If unsure, say N
|
||||
|
||||
+config BCMA_PFLASH
|
||||
+ bool
|
||||
+ depends on BCMA_DRIVER_MIPS
|
||||
+ default y
|
||||
+
|
||||
config BCMA_SFLASH
|
||||
bool
|
||||
depends on BCMA_DRIVER_MIPS
|
||||
--- a/drivers/bcma/Makefile
|
||||
+++ b/drivers/bcma/Makefile
|
||||
@@ -1,6 +1,7 @@
|
||||
bcma-y += main.o scan.o core.o sprom.o
|
||||
bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
|
||||
bcma-y += driver_chipcommon_b.o
|
||||
+bcma-$(CONFIG_BCMA_PFLASH) += driver_chipcommon_pflash.o
|
||||
bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o
|
||||
bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o
|
||||
bcma-$(CONFIG_BCMA_DRIVER_PCI) += driver_pci.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/bcma/driver_chipcommon_pflash.c
|
||||
@@ -0,0 +1,49 @@
|
||||
+/*
|
||||
+ * Broadcom specific AMBA
|
||||
+ * ChipCommon parallel flash
|
||||
+ *
|
||||
+ * Licensed under the GNU/GPL. See COPYING for details.
|
||||
+ */
|
||||
+
|
||||
+#include "bcma_private.h"
|
||||
+
|
||||
+#include <linux/bcma/bcma.h>
|
||||
+#include <linux/mtd/physmap.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+static const char * const part_probes[] = { "bcm47xxpart", NULL };
|
||||
+
|
||||
+static struct physmap_flash_data bcma_pflash_data = {
|
||||
+ .part_probe_types = part_probes,
|
||||
+};
|
||||
+
|
||||
+static struct resource bcma_pflash_resource = {
|
||||
+ .name = "bcma_pflash",
|
||||
+ .flags = IORESOURCE_MEM,
|
||||
+};
|
||||
+
|
||||
+struct platform_device bcma_pflash_dev = {
|
||||
+ .name = "physmap-flash",
|
||||
+ .dev = {
|
||||
+ .platform_data = &bcma_pflash_data,
|
||||
+ },
|
||||
+ .resource = &bcma_pflash_resource,
|
||||
+ .num_resources = 1,
|
||||
+};
|
||||
+
|
||||
+int bcma_pflash_init(struct bcma_drv_cc *cc)
|
||||
+{
|
||||
+ struct bcma_pflash *pflash = &cc->pflash;
|
||||
+
|
||||
+ pflash->present = true;
|
||||
+
|
||||
+ if (!(bcma_read32(cc->core, BCMA_CC_FLASH_CFG) & BCMA_CC_FLASH_CFG_DS))
|
||||
+ bcma_pflash_data.width = 1;
|
||||
+ else
|
||||
+ bcma_pflash_data.width = 2;
|
||||
+
|
||||
+ bcma_pflash_resource.start = BCMA_SOC_FLASH2;
|
||||
+ bcma_pflash_resource.end = BCMA_SOC_FLASH2 + BCMA_SOC_FLASH2_SZ;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
--- a/drivers/bcma/main.c
|
||||
+++ b/drivers/bcma/main.c
|
||||
@@ -325,7 +325,7 @@ static int bcma_register_devices(struct
|
||||
bcma_register_core(bus, core);
|
||||
}
|
||||
|
||||
-#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
+#ifdef CONFIG_BCMA_PFLASH
|
||||
if (bus->drv_cc.pflash.present) {
|
||||
err = platform_device_register(&bcma_pflash_dev);
|
||||
if (err)
|
|
@ -1,50 +0,0 @@
|
|||
--- a/drivers/mtd/bcm47xxpart.c
|
||||
+++ b/drivers/mtd/bcm47xxpart.c
|
||||
@@ -15,8 +15,12 @@
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
-/* 10 parts were found on sflash on Netgear WNDR4500 */
|
||||
-#define BCM47XXPART_MAX_PARTS 12
|
||||
+/*
|
||||
+ * NAND flash on Netgear R6250 was verified to contain 15 partitions.
|
||||
+ * This will result in allocating too big array for some old devices, but the
|
||||
+ * memory will be freed soon anyway (see mtd_device_parse_register).
|
||||
+ */
|
||||
+#define BCM47XXPART_MAX_PARTS 20
|
||||
|
||||
/*
|
||||
* Amount of bytes we read when analyzing each block of flash memory.
|
||||
@@ -168,18 +172,26 @@ static int bcm47xxpart_parse(struct mtd_
|
||||
i++;
|
||||
}
|
||||
|
||||
- bcm47xxpart_add_part(&parts[curr_part++], "linux",
|
||||
- offset + trx->offset[i], 0);
|
||||
- i++;
|
||||
+ if (trx->offset[i]) {
|
||||
+ bcm47xxpart_add_part(&parts[curr_part++],
|
||||
+ "linux",
|
||||
+ offset + trx->offset[i],
|
||||
+ 0);
|
||||
+ i++;
|
||||
+ }
|
||||
|
||||
/*
|
||||
* Pure rootfs size is known and can be calculated as:
|
||||
* trx->length - trx->offset[i]. We don't fill it as
|
||||
* we want to have jffs2 (overlay) in the same mtd.
|
||||
*/
|
||||
- bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
|
||||
- offset + trx->offset[i], 0);
|
||||
- i++;
|
||||
+ if (trx->offset[i]) {
|
||||
+ bcm47xxpart_add_part(&parts[curr_part++],
|
||||
+ "rootfs",
|
||||
+ offset + trx->offset[i],
|
||||
+ 0);
|
||||
+ i++;
|
||||
+ }
|
||||
|
||||
last_trx_part = curr_part - 1;
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
--- a/drivers/mtd/bcm47xxpart.c
|
||||
+++ b/drivers/mtd/bcm47xxpart.c
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
+#include <uapi/linux/magic.h>
|
||||
+
|
||||
/*
|
||||
* NAND flash on Netgear R6250 was verified to contain 15 partitions.
|
||||
* This will result in allocating too big array for some old devices, but the
|
||||
@@ -39,7 +41,8 @@
|
||||
#define ML_MAGIC1 0x39685a42
|
||||
#define ML_MAGIC2 0x26594131
|
||||
#define TRX_MAGIC 0x30524448
|
||||
-#define SQSH_MAGIC 0x71736873 /* shsq */
|
||||
+#define SHSQ_MAGIC 0x71736873 /* shsq (weird ZTE H218N endianness) */
|
||||
+#define UBI_EC_MAGIC 0x23494255 /* UBI# */
|
||||
|
||||
struct trx_header {
|
||||
uint32_t magic;
|
||||
@@ -50,7 +53,7 @@ struct trx_header {
|
||||
uint32_t offset[3];
|
||||
} __packed;
|
||||
|
||||
-static void bcm47xxpart_add_part(struct mtd_partition *part, char *name,
|
||||
+static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name,
|
||||
u64 offset, uint32_t mask_flags)
|
||||
{
|
||||
part->name = name;
|
||||
@@ -58,6 +61,26 @@ static void bcm47xxpart_add_part(struct
|
||||
part->mask_flags = mask_flags;
|
||||
}
|
||||
|
||||
+static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
|
||||
+ size_t offset)
|
||||
+{
|
||||
+ uint32_t buf;
|
||||
+ size_t bytes_read;
|
||||
+
|
||||
+ if (mtd_read(master, offset, sizeof(buf), &bytes_read,
|
||||
+ (uint8_t *)&buf) < 0) {
|
||||
+ pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
|
||||
+ offset);
|
||||
+ goto out_default;
|
||||
+ }
|
||||
+
|
||||
+ if (buf == UBI_EC_MAGIC)
|
||||
+ return "ubi";
|
||||
+
|
||||
+out_default:
|
||||
+ return "rootfs";
|
||||
+}
|
||||
+
|
||||
static int bcm47xxpart_parse(struct mtd_info *master,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
@@ -73,8 +96,12 @@ static int bcm47xxpart_parse(struct mtd_
|
||||
int last_trx_part = -1;
|
||||
int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
|
||||
|
||||
- if (blocksize <= 0x10000)
|
||||
- blocksize = 0x10000;
|
||||
+ /*
|
||||
+ * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
|
||||
+ * partitions were aligned to at least 0x1000 anyway.
|
||||
+ */
|
||||
+ if (blocksize < 0x1000)
|
||||
+ blocksize = 0x1000;
|
||||
|
||||
/* Alloc */
|
||||
parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,
|
||||
@@ -186,8 +213,11 @@ static int bcm47xxpart_parse(struct mtd_
|
||||
* we want to have jffs2 (overlay) in the same mtd.
|
||||
*/
|
||||
if (trx->offset[i]) {
|
||||
+ const char *name;
|
||||
+
|
||||
+ name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]);
|
||||
bcm47xxpart_add_part(&parts[curr_part++],
|
||||
- "rootfs",
|
||||
+ name,
|
||||
offset + trx->offset[i],
|
||||
0);
|
||||
i++;
|
||||
@@ -203,7 +233,8 @@ static int bcm47xxpart_parse(struct mtd_
|
||||
}
|
||||
|
||||
/* Squashfs on devices not using TRX */
|
||||
- if (buf[0x000 / 4] == SQSH_MAGIC) {
|
||||
+ if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC ||
|
||||
+ buf[0x000 / 4] == SHSQ_MAGIC) {
|
||||
bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
|
||||
offset, 0);
|
||||
continue;
|
|
@ -1,15 +0,0 @@
|
|||
mtd: spi-nor: support for (GigaDevice) GD25Q128B
|
||||
|
||||
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -510,6 +510,7 @@ static const struct spi_device_id spi_no
|
||||
/* GigaDevice */
|
||||
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
+ { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) },
|
||||
|
||||
/* Intel/Numonyx -- xxxs33b */
|
||||
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
|
|
@ -1,39 +0,0 @@
|
|||
--- a/drivers/mtd/devices/m25p80.c
|
||||
+++ b/drivers/mtd/devices/m25p80.c
|
||||
@@ -310,11 +310,21 @@ static const struct spi_device_id m25p_i
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, m25p_ids);
|
||||
|
||||
+static const struct of_device_id m25p_of_table[] = {
|
||||
+ /*
|
||||
+ * Generic compatibility for SPI NOR that can be identified by the
|
||||
+ * JEDEC READ ID opcode (0x9F). Use this, if possible.
|
||||
+ */
|
||||
+ { .compatible = "jedec,spi-nor" },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, m25p_of_table);
|
||||
|
||||
static struct spi_driver m25p80_driver = {
|
||||
.driver = {
|
||||
.name = "m25p80",
|
||||
.owner = THIS_MODULE,
|
||||
+ .of_match_table = m25p_of_table,
|
||||
},
|
||||
.id_table = m25p_ids,
|
||||
.probe = m25p_probe,
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -934,8 +934,11 @@ int spi_nor_scan(struct spi_nor *nor, co
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- id = spi_nor_match_id(name);
|
||||
+ if (name)
|
||||
+ id = spi_nor_match_id(name);
|
||||
if (!id)
|
||||
+ id = nor->read_id(nor);
|
||||
+ if (IS_ERR_OR_NULL(id))
|
||||
return -ENOENT;
|
||||
|
||||
info = (void *)id->driver_data;
|
|
@ -1,509 +0,0 @@
|
|||
Subject: netfilter: conntrack: cache route for forwarded connections
|
||||
|
||||
... to avoid per-packet FIB lookup if possible.
|
||||
|
||||
The cached dst is re-used provided the input interface
|
||||
is the same as that of the previous packet in the same direction.
|
||||
|
||||
If not, the cached dst is invalidated.
|
||||
|
||||
For ipv6 we also need to store sernum, else dst_check doesn't work,
|
||||
pointed out by Eric Dumazet.
|
||||
|
||||
This should speed up forwarding when conntrack is already in use
|
||||
anyway, especially when using reverse path filtering -- active RPF
|
||||
enforces two FIB lookups for each packet.
|
||||
|
||||
Before the routing cache removal this didn't matter since RPF was performed
|
||||
only when route cache didn't yield a result; but without route cache it
|
||||
comes at higher price.
|
||||
|
||||
Julian Anastasov suggested to add NETDEV_UNREGISTER handler to
|
||||
avoid holding on to dsts of 'frozen' conntracks.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
--- a/include/net/netfilter/nf_conntrack_extend.h
|
||||
+++ b/include/net/netfilter/nf_conntrack_extend.h
|
||||
@@ -30,6 +30,9 @@ enum nf_ct_ext_id {
|
||||
#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY)
|
||||
NF_CT_EXT_SYNPROXY,
|
||||
#endif
|
||||
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
|
||||
+ NF_CT_EXT_RTCACHE,
|
||||
+#endif
|
||||
NF_CT_EXT_NUM,
|
||||
};
|
||||
|
||||
@@ -43,6 +46,7 @@ enum nf_ct_ext_id {
|
||||
#define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout
|
||||
#define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels
|
||||
#define NF_CT_EXT_SYNPROXY_TYPE struct nf_conn_synproxy
|
||||
+#define NF_CT_EXT_RTCACHE_TYPE struct nf_conn_rtcache
|
||||
|
||||
/* Extensions: optional stuff which isn't permanently in struct. */
|
||||
struct nf_ct_ext {
|
||||
--- /dev/null
|
||||
+++ b/include/net/netfilter/nf_conntrack_rtcache.h
|
||||
@@ -0,0 +1,34 @@
|
||||
+#include <linux/gfp.h>
|
||||
+#include <net/netfilter/nf_conntrack.h>
|
||||
+#include <net/netfilter/nf_conntrack_extend.h>
|
||||
+
|
||||
+struct dst_entry;
|
||||
+
|
||||
+struct nf_conn_dst_cache {
|
||||
+ struct dst_entry *dst;
|
||||
+ int iif;
|
||||
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
|
||||
+ u32 cookie;
|
||||
+#endif
|
||||
+
|
||||
+};
|
||||
+
|
||||
+struct nf_conn_rtcache {
|
||||
+ struct nf_conn_dst_cache cached_dst[IP_CT_DIR_MAX];
|
||||
+};
|
||||
+
|
||||
+static inline
|
||||
+struct nf_conn_rtcache *nf_ct_rtcache_find(const struct nf_conn *ct)
|
||||
+{
|
||||
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
|
||||
+ return nf_ct_ext_find(ct, NF_CT_EXT_RTCACHE);
|
||||
+#else
|
||||
+ return NULL;
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
+static inline int nf_conn_rtcache_iif_get(const struct nf_conn_rtcache *rtc,
|
||||
+ enum ip_conntrack_dir dir)
|
||||
+{
|
||||
+ return rtc->cached_dst[dir].iif;
|
||||
+}
|
||||
--- a/net/netfilter/Kconfig
|
||||
+++ b/net/netfilter/Kconfig
|
||||
@@ -106,6 +106,18 @@ config NF_CONNTRACK_EVENTS
|
||||
|
||||
If unsure, say `N'.
|
||||
|
||||
+config NF_CONNTRACK_RTCACHE
|
||||
+ tristate "Cache route entries in conntrack objects"
|
||||
+ depends on NETFILTER_ADVANCED
|
||||
+ depends on NF_CONNTRACK
|
||||
+ help
|
||||
+ If this option is enabled, the connection tracking code will
|
||||
+ cache routing information for each connection that is being
|
||||
+ forwarded, at a cost of 32 bytes per conntrack object.
|
||||
+
|
||||
+ To compile it as a module, choose M here. If unsure, say N.
|
||||
+ The module will be called nf_conntrack_rtcache.
|
||||
+
|
||||
config NF_CONNTRACK_TIMEOUT
|
||||
bool 'Connection tracking timeout'
|
||||
depends on NETFILTER_ADVANCED
|
||||
--- a/net/netfilter/Makefile
|
||||
+++ b/net/netfilter/Makefile
|
||||
@@ -18,6 +18,9 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += n
|
||||
# connection tracking
|
||||
obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
|
||||
|
||||
+# optional conntrack route cache extension
|
||||
+obj-$(CONFIG_NF_CONNTRACK_RTCACHE) += nf_conntrack_rtcache.o
|
||||
+
|
||||
# SCTP protocol connection tracking
|
||||
obj-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o
|
||||
obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o
|
||||
--- /dev/null
|
||||
+++ b/net/netfilter/nf_conntrack_rtcache.c
|
||||
@@ -0,0 +1,391 @@
|
||||
+/* route cache for netfilter.
|
||||
+ *
|
||||
+ * (C) 2014 Red Hat GmbH
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/netfilter.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <linux/stddef.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include <net/dst.h>
|
||||
+
|
||||
+#include <net/netfilter/nf_conntrack.h>
|
||||
+#include <net/netfilter/nf_conntrack_core.h>
|
||||
+#include <net/netfilter/nf_conntrack_extend.h>
|
||||
+#include <net/netfilter/nf_conntrack_rtcache.h>
|
||||
+
|
||||
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
|
||||
+#include <net/ip6_fib.h>
|
||||
+#endif
|
||||
+
|
||||
+static void __nf_conn_rtcache_destroy(struct nf_conn_rtcache *rtc,
|
||||
+ enum ip_conntrack_dir dir)
|
||||
+{
|
||||
+ struct dst_entry *dst = rtc->cached_dst[dir].dst;
|
||||
+
|
||||
+ dst_release(dst);
|
||||
+}
|
||||
+
|
||||
+static void nf_conn_rtcache_destroy(struct nf_conn *ct)
|
||||
+{
|
||||
+ struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct);
|
||||
+
|
||||
+ if (!rtc)
|
||||
+ return;
|
||||
+
|
||||
+ __nf_conn_rtcache_destroy(rtc, IP_CT_DIR_ORIGINAL);
|
||||
+ __nf_conn_rtcache_destroy(rtc, IP_CT_DIR_REPLY);
|
||||
+}
|
||||
+
|
||||
+static void nf_ct_rtcache_ext_add(struct nf_conn *ct)
|
||||
+{
|
||||
+ struct nf_conn_rtcache *rtc;
|
||||
+
|
||||
+ rtc = nf_ct_ext_add(ct, NF_CT_EXT_RTCACHE, GFP_ATOMIC);
|
||||
+ if (rtc) {
|
||||
+ rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif = -1;
|
||||
+ rtc->cached_dst[IP_CT_DIR_ORIGINAL].dst = NULL;
|
||||
+ rtc->cached_dst[IP_CT_DIR_REPLY].iif = -1;
|
||||
+ rtc->cached_dst[IP_CT_DIR_REPLY].dst = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static struct nf_conn_rtcache *nf_ct_rtcache_find_usable(struct nf_conn *ct)
|
||||
+{
|
||||
+ if (nf_ct_is_untracked(ct))
|
||||
+ return NULL;
|
||||
+ return nf_ct_rtcache_find(ct);
|
||||
+}
|
||||
+
|
||||
+static struct dst_entry *
|
||||
+nf_conn_rtcache_dst_get(const struct nf_conn_rtcache *rtc,
|
||||
+ enum ip_conntrack_dir dir)
|
||||
+{
|
||||
+ return rtc->cached_dst[dir].dst;
|
||||
+}
|
||||
+
|
||||
+static u32 nf_rtcache_get_cookie(int pf, const struct dst_entry *dst)
|
||||
+{
|
||||
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
|
||||
+ if (pf == NFPROTO_IPV6) {
|
||||
+ const struct rt6_info *rt = (const struct rt6_info *)dst;
|
||||
+
|
||||
+ if (rt->rt6i_node)
|
||||
+ return (u32)rt->rt6i_node->fn_sernum;
|
||||
+ }
|
||||
+#endif
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void nf_conn_rtcache_dst_set(int pf,
|
||||
+ struct nf_conn_rtcache *rtc,
|
||||
+ struct dst_entry *dst,
|
||||
+ enum ip_conntrack_dir dir, int iif)
|
||||
+{
|
||||
+ if (rtc->cached_dst[dir].iif != iif)
|
||||
+ rtc->cached_dst[dir].iif = iif;
|
||||
+
|
||||
+ if (rtc->cached_dst[dir].dst != dst) {
|
||||
+ struct dst_entry *old;
|
||||
+
|
||||
+ dst_hold(dst);
|
||||
+
|
||||
+ old = xchg(&rtc->cached_dst[dir].dst, dst);
|
||||
+ dst_release(old);
|
||||
+
|
||||
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
|
||||
+ if (pf == NFPROTO_IPV6)
|
||||
+ rtc->cached_dst[dir].cookie =
|
||||
+ nf_rtcache_get_cookie(pf, dst);
|
||||
+#endif
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void nf_conn_rtcache_dst_obsolete(struct nf_conn_rtcache *rtc,
|
||||
+ enum ip_conntrack_dir dir)
|
||||
+{
|
||||
+ struct dst_entry *old;
|
||||
+
|
||||
+ pr_debug("Invalidate iif %d for dir %d on cache %p\n",
|
||||
+ rtc->cached_dst[dir].iif, dir, rtc);
|
||||
+
|
||||
+ old = xchg(&rtc->cached_dst[dir].dst, NULL);
|
||||
+ dst_release(old);
|
||||
+ rtc->cached_dst[dir].iif = -1;
|
||||
+}
|
||||
+
|
||||
+static unsigned int nf_rtcache_in(const struct nf_hook_ops *ops,
|
||||
+ struct sk_buff *skb,
|
||||
+ const struct net_device *in,
|
||||
+ const struct net_device *out,
|
||||
+ int (*okfn)(struct sk_buff *))
|
||||
+{
|
||||
+ struct nf_conn_rtcache *rtc;
|
||||
+ enum ip_conntrack_info ctinfo;
|
||||
+ enum ip_conntrack_dir dir;
|
||||
+ struct dst_entry *dst;
|
||||
+ struct nf_conn *ct;
|
||||
+ int iif;
|
||||
+ u32 cookie;
|
||||
+
|
||||
+ if (skb_dst(skb) || skb->sk)
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ ct = nf_ct_get(skb, &ctinfo);
|
||||
+ if (!ct)
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ rtc = nf_ct_rtcache_find_usable(ct);
|
||||
+ if (!rtc)
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ /* if iif changes, don't use cache and let ip stack
|
||||
+ * do route lookup.
|
||||
+ *
|
||||
+ * If rp_filter is enabled it might toss skb, so
|
||||
+ * we don't want to avoid these checks.
|
||||
+ */
|
||||
+ dir = CTINFO2DIR(ctinfo);
|
||||
+ iif = nf_conn_rtcache_iif_get(rtc, dir);
|
||||
+ if (in->ifindex != iif) {
|
||||
+ pr_debug("ct %p, iif %d, cached iif %d, skip cached entry\n",
|
||||
+ ct, iif, in->ifindex);
|
||||
+ return NF_ACCEPT;
|
||||
+ }
|
||||
+ dst = nf_conn_rtcache_dst_get(rtc, dir);
|
||||
+ if (dst == NULL)
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ cookie = nf_rtcache_get_cookie(ops->pf, dst);
|
||||
+
|
||||
+ dst = dst_check(dst, cookie);
|
||||
+ pr_debug("obtained dst %p for skb %p, cookie %d\n", dst, skb, cookie);
|
||||
+ if (likely(dst))
|
||||
+ skb_dst_set_noref_force(skb, dst);
|
||||
+ else
|
||||
+ nf_conn_rtcache_dst_obsolete(rtc, dir);
|
||||
+
|
||||
+ return NF_ACCEPT;
|
||||
+}
|
||||
+
|
||||
+static unsigned int nf_rtcache_forward(const struct nf_hook_ops *ops,
|
||||
+ struct sk_buff *skb,
|
||||
+ const struct net_device *in,
|
||||
+ const struct net_device *out,
|
||||
+ int (*okfn)(struct sk_buff *))
|
||||
+{
|
||||
+ struct nf_conn_rtcache *rtc;
|
||||
+ enum ip_conntrack_info ctinfo;
|
||||
+ enum ip_conntrack_dir dir;
|
||||
+ struct nf_conn *ct;
|
||||
+ struct dst_entry *dst = skb_dst(skb);
|
||||
+ int iif;
|
||||
+
|
||||
+ ct = nf_ct_get(skb, &ctinfo);
|
||||
+ if (!ct)
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ if (dst && dst_xfrm(dst))
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ if (!nf_ct_is_confirmed(ct)) {
|
||||
+ if (WARN_ON(nf_ct_rtcache_find(ct)))
|
||||
+ return NF_ACCEPT;
|
||||
+ nf_ct_rtcache_ext_add(ct);
|
||||
+ return NF_ACCEPT;
|
||||
+ }
|
||||
+
|
||||
+ rtc = nf_ct_rtcache_find_usable(ct);
|
||||
+ if (!rtc)
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ dir = CTINFO2DIR(ctinfo);
|
||||
+ iif = nf_conn_rtcache_iif_get(rtc, dir);
|
||||
+ pr_debug("ct %p, skb %p, dir %d, iif %d, cached iif %d\n",
|
||||
+ ct, skb, dir, iif, in->ifindex);
|
||||
+ if (likely(in->ifindex == iif))
|
||||
+ return NF_ACCEPT;
|
||||
+
|
||||
+ nf_conn_rtcache_dst_set(ops->pf, rtc, skb_dst(skb), dir, in->ifindex);
|
||||
+ return NF_ACCEPT;
|
||||
+}
|
||||
+
|
||||
+static int nf_rtcache_dst_remove(struct nf_conn *ct, void *data)
|
||||
+{
|
||||
+ struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct);
|
||||
+ struct net_device *dev = data;
|
||||
+
|
||||
+ if (!rtc)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (dev->ifindex == rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif ||
|
||||
+ dev->ifindex == rtc->cached_dst[IP_CT_DIR_REPLY].iif) {
|
||||
+ nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_ORIGINAL);
|
||||
+ nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_REPLY);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nf_rtcache_netdev_event(struct notifier_block *this,
|
||||
+ unsigned long event, void *ptr)
|
||||
+{
|
||||
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
+ struct net *net = dev_net(dev);
|
||||
+
|
||||
+ if (event == NETDEV_DOWN)
|
||||
+ nf_ct_iterate_cleanup(net, nf_rtcache_dst_remove, dev, 0, 0);
|
||||
+
|
||||
+ return NOTIFY_DONE;
|
||||
+}
|
||||
+
|
||||
+static struct notifier_block nf_rtcache_notifier = {
|
||||
+ .notifier_call = nf_rtcache_netdev_event,
|
||||
+};
|
||||
+
|
||||
+static struct nf_hook_ops rtcache_ops[] = {
|
||||
+ {
|
||||
+ .hook = nf_rtcache_in,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .pf = NFPROTO_IPV4,
|
||||
+ .hooknum = NF_INET_PRE_ROUTING,
|
||||
+ .priority = NF_IP_PRI_LAST,
|
||||
+ },
|
||||
+ {
|
||||
+ .hook = nf_rtcache_forward,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .pf = NFPROTO_IPV4,
|
||||
+ .hooknum = NF_INET_FORWARD,
|
||||
+ .priority = NF_IP_PRI_LAST,
|
||||
+ },
|
||||
+#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
|
||||
+ {
|
||||
+ .hook = nf_rtcache_in,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .pf = NFPROTO_IPV6,
|
||||
+ .hooknum = NF_INET_PRE_ROUTING,
|
||||
+ .priority = NF_IP_PRI_LAST,
|
||||
+ },
|
||||
+ {
|
||||
+ .hook = nf_rtcache_forward,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .pf = NFPROTO_IPV6,
|
||||
+ .hooknum = NF_INET_FORWARD,
|
||||
+ .priority = NF_IP_PRI_LAST,
|
||||
+ },
|
||||
+#endif
|
||||
+};
|
||||
+
|
||||
+static struct nf_ct_ext_type rtcache_extend __read_mostly = {
|
||||
+ .len = sizeof(struct nf_conn_rtcache),
|
||||
+ .align = __alignof__(struct nf_conn_rtcache),
|
||||
+ .id = NF_CT_EXT_RTCACHE,
|
||||
+ .destroy = nf_conn_rtcache_destroy,
|
||||
+};
|
||||
+
|
||||
+static int __init nf_conntrack_rtcache_init(void)
|
||||
+{
|
||||
+ int ret = nf_ct_extend_register(&rtcache_extend);
|
||||
+
|
||||
+ if (ret < 0) {
|
||||
+ pr_err("nf_conntrack_rtcache: Unable to register extension\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = nf_register_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops));
|
||||
+ if (ret < 0) {
|
||||
+ nf_ct_extend_unregister(&rtcache_extend);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = register_netdevice_notifier(&nf_rtcache_notifier);
|
||||
+ if (ret) {
|
||||
+ nf_unregister_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops));
|
||||
+ nf_ct_extend_unregister(&rtcache_extend);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int nf_rtcache_ext_remove(struct nf_conn *ct, void *data)
|
||||
+{
|
||||
+ struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct);
|
||||
+
|
||||
+ return rtc != NULL;
|
||||
+}
|
||||
+
|
||||
+static bool __exit nf_conntrack_rtcache_wait_for_dying(struct net *net)
|
||||
+{
|
||||
+ bool wait = false;
|
||||
+ int cpu;
|
||||
+
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ struct nf_conntrack_tuple_hash *h;
|
||||
+ struct hlist_nulls_node *n;
|
||||
+ struct nf_conn *ct;
|
||||
+ struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+ spin_lock_bh(&pcpu->lock);
|
||||
+
|
||||
+ hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) {
|
||||
+ ct = nf_ct_tuplehash_to_ctrack(h);
|
||||
+ if (nf_ct_rtcache_find(ct) != NULL) {
|
||||
+ wait = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ spin_unlock_bh(&pcpu->lock);
|
||||
+ rcu_read_unlock();
|
||||
+ }
|
||||
+
|
||||
+ return wait;
|
||||
+}
|
||||
+
|
||||
+static void __exit nf_conntrack_rtcache_fini(void)
|
||||
+{
|
||||
+ struct net *net;
|
||||
+ int count = 0;
|
||||
+
|
||||
+ /* remove hooks so no new connections get rtcache extension */
|
||||
+ nf_unregister_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops));
|
||||
+
|
||||
+ synchronize_net();
|
||||
+
|
||||
+ unregister_netdevice_notifier(&nf_rtcache_notifier);
|
||||
+
|
||||
+ rtnl_lock();
|
||||
+
|
||||
+ /* zap all conntracks with rtcache extension */
|
||||
+ for_each_net(net)
|
||||
+ nf_ct_iterate_cleanup(net, nf_rtcache_ext_remove, NULL, 0, 0);
|
||||
+
|
||||
+ for_each_net(net) {
|
||||
+ /* .. and make sure they're gone from dying list, too */
|
||||
+ while (nf_conntrack_rtcache_wait_for_dying(net)) {
|
||||
+ msleep(200);
|
||||
+ WARN_ONCE(++count > 25, "Waiting for all rtcache conntracks to go away\n");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ rtnl_unlock();
|
||||
+ synchronize_net();
|
||||
+ nf_ct_extend_unregister(&rtcache_extend);
|
||||
+}
|
||||
+module_init(nf_conntrack_rtcache_init);
|
||||
+module_exit(nf_conntrack_rtcache_fini);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
|
||||
+MODULE_DESCRIPTION("Conntrack route cache extension");
|
|
@ -1,99 +0,0 @@
|
|||
From 6ae4ae8e512bd229f806c22f8a2cd751e4f987c2 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
|
||||
Date: Sat, 23 May 2015 03:12:34 +0200
|
||||
Subject: [PATCH] bridge: allow setting hash_max + multicast_router if
|
||||
interface is down
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Network managers like netifd (used in OpenWRT for instance) try to
|
||||
configure interface options after creation but before setting the
|
||||
interface up.
|
||||
|
||||
Unfortunately the sysfs / bridge currently only allows to configure the
|
||||
hash_max and multicast_router options when the bridge interface is up.
|
||||
But since br_multicast_init() doesn't start any timers and only sets
|
||||
default values and initializes timers it should be save to reconfigure
|
||||
the default values after that, before things actually get active after
|
||||
the bridge is set up.
|
||||
|
||||
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
net/bridge/br_multicast.c | 24 +++---------------------
|
||||
1 file changed, 3 insertions(+), 21 deletions(-)
|
||||
|
||||
--- a/net/bridge/br_multicast.c
|
||||
+++ b/net/bridge/br_multicast.c
|
||||
@@ -1948,11 +1948,9 @@ out:
|
||||
|
||||
int br_multicast_set_router(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
- int err = -ENOENT;
|
||||
+ int err = -EINVAL;
|
||||
|
||||
spin_lock_bh(&br->multicast_lock);
|
||||
- if (!netif_running(br->dev))
|
||||
- goto unlock;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
@@ -1963,13 +1961,8 @@ int br_multicast_set_router(struct net_b
|
||||
br->multicast_router = val;
|
||||
err = 0;
|
||||
break;
|
||||
-
|
||||
- default:
|
||||
- err = -EINVAL;
|
||||
- break;
|
||||
}
|
||||
|
||||
-unlock:
|
||||
spin_unlock_bh(&br->multicast_lock);
|
||||
|
||||
return err;
|
||||
@@ -1978,11 +1971,9 @@ unlock:
|
||||
int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
|
||||
{
|
||||
struct net_bridge *br = p->br;
|
||||
- int err = -ENOENT;
|
||||
+ int err = -EINVAL;
|
||||
|
||||
spin_lock(&br->multicast_lock);
|
||||
- if (!netif_running(br->dev) || p->state == BR_STATE_DISABLED)
|
||||
- goto unlock;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
@@ -2004,13 +1995,8 @@ int br_multicast_set_port_router(struct
|
||||
|
||||
br_multicast_add_router(br, p);
|
||||
break;
|
||||
-
|
||||
- default:
|
||||
- err = -EINVAL;
|
||||
- break;
|
||||
}
|
||||
|
||||
-unlock:
|
||||
spin_unlock(&br->multicast_lock);
|
||||
|
||||
return err;
|
||||
@@ -2115,15 +2101,11 @@ unlock:
|
||||
|
||||
int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
- int err = -ENOENT;
|
||||
+ int err = -EINVAL;
|
||||
u32 old;
|
||||
struct net_bridge_mdb_htable *mdb;
|
||||
|
||||
spin_lock_bh(&br->multicast_lock);
|
||||
- if (!netif_running(br->dev))
|
||||
- goto unlock;
|
||||
-
|
||||
- err = -EINVAL;
|
||||
if (!is_power_of_2(val))
|
||||
goto unlock;
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
--- a/arch/mips/boot/compressed/string.c
|
||||
+++ b/arch/mips/boot/compressed/string.c
|
||||
@@ -26,3 +26,19 @@ void *memset(void *s, int c, size_t n)
|
||||
ss[i] = c;
|
||||
return s;
|
||||
}
|
||||
+
|
||||
+void *memmove(void *__dest, __const void *__src, size_t count)
|
||||
+{
|
||||
+ unsigned char *d = __dest;
|
||||
+ const unsigned char *s = __src;
|
||||
+
|
||||
+ if (__dest == __src)
|
||||
+ return __dest;
|
||||
+
|
||||
+ if (__dest < __src)
|
||||
+ return memcpy(__dest, __src, count);
|
||||
+
|
||||
+ while (count--)
|
||||
+ d[count] = s[count];
|
||||
+ return __dest;
|
||||
+}
|
|
@ -1,44 +0,0 @@
|
|||
From 6216642f200258708e47170ff14ba8ecb486f4f0 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sun, 18 Jan 2015 19:49:58 +0100
|
||||
Subject: [PATCH] bgmac: register napi before the device
|
||||
|
||||
napi should get registered before the netdev and not after.
|
||||
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/ethernet/broadcom/bgmac.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -1521,6 +1521,8 @@ static int bgmac_probe(struct bcma_devic
|
||||
if (core->bus->sprom.boardflags_lo & BGMAC_BFL_ENETADM)
|
||||
bgmac_warn(bgmac, "Support for ADMtek ethernet switch not implemented\n");
|
||||
|
||||
+ netif_napi_add(net_dev, &bgmac->napi, bgmac_poll, BGMAC_WEIGHT);
|
||||
+
|
||||
err = bgmac_mii_register(bgmac);
|
||||
if (err) {
|
||||
bgmac_err(bgmac, "Cannot register MDIO\n");
|
||||
@@ -1535,8 +1537,6 @@ static int bgmac_probe(struct bcma_devic
|
||||
|
||||
netif_carrier_off(net_dev);
|
||||
|
||||
- netif_napi_add(net_dev, &bgmac->napi, bgmac_poll, BGMAC_WEIGHT);
|
||||
-
|
||||
return 0;
|
||||
|
||||
err_mii_unregister:
|
||||
@@ -1555,9 +1555,9 @@ static void bgmac_remove(struct bcma_dev
|
||||
{
|
||||
struct bgmac *bgmac = bcma_get_drvdata(core);
|
||||
|
||||
- netif_napi_del(&bgmac->napi);
|
||||
unregister_netdev(bgmac->net_dev);
|
||||
bgmac_mii_unregister(bgmac);
|
||||
+ netif_napi_del(&bgmac->napi);
|
||||
bgmac_dma_free(bgmac);
|
||||
bcma_set_drvdata(core, NULL);
|
||||
free_netdev(bgmac->net_dev);
|
|
@ -1,30 +0,0 @@
|
|||
From 43f159c60a99318b1ef7d1d7c16c4dfdd06bfd90 Mon Sep 17 00:00:00 2001
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Sun, 18 Jan 2015 19:49:59 +0100
|
||||
Subject: [PATCH] bgmac: activate irqs only if there is nothing to poll
|
||||
|
||||
IRQs should only get activated when there is nothing to poll in the
|
||||
queue any more and to after every poll.
|
||||
|
||||
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/ethernet/broadcom/bgmac.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -1167,10 +1167,10 @@ static int bgmac_poll(struct napi_struct
|
||||
bgmac->int_status = 0;
|
||||
}
|
||||
|
||||
- if (handled < weight)
|
||||
+ if (handled < weight) {
|
||||
napi_complete(napi);
|
||||
-
|
||||
- bgmac_chip_intrs_on(bgmac);
|
||||
+ bgmac_chip_intrs_on(bgmac);
|
||||
+ }
|
||||
|
||||
return handled;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
From 8edfe3b6fad28da191c8fa15e4e0d8f7335a0091 Mon Sep 17 00:00:00 2001
|
||||
From: Peter Senna Tschudin <peter.senna@gmail.com>
|
||||
Date: Sat, 7 Mar 2015 12:10:26 +0100
|
||||
Subject: [PATCH] bgmac: Clean warning messages
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
On my test environment the throughput of a file transfer drops
|
||||
from 4.4MBps to 116KBps due the number of repeated warning
|
||||
messages. This patch removes the warning messages as DMA works
|
||||
correctly with addresses using 0xC0000000 bits.
|
||||
|
||||
Signed-off-by: Peter Senna Tschudin <peter.senna@gmail.com>
|
||||
Acked-by: Rafał Miłecki <zajec5@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/ethernet/broadcom/bgmac.c | 7 -------
|
||||
1 file changed, 7 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -302,9 +302,6 @@ static int bgmac_dma_rx_skb_for_slot(str
|
||||
slot->skb = skb;
|
||||
slot->dma_addr = dma_addr;
|
||||
|
||||
- if (slot->dma_addr & 0xC0000000)
|
||||
- bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -505,8 +502,6 @@ static int bgmac_dma_alloc(struct bgmac
|
||||
ring->mmio_base);
|
||||
goto err_dma_free;
|
||||
}
|
||||
- if (ring->dma_base & 0xC0000000)
|
||||
- bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
|
||||
|
||||
ring->unaligned = bgmac_dma_unaligned(bgmac, ring,
|
||||
BGMAC_DMA_RING_TX);
|
||||
@@ -536,8 +531,6 @@ static int bgmac_dma_alloc(struct bgmac
|
||||
err = -ENOMEM;
|
||||
goto err_dma_free;
|
||||
}
|
||||
- if (ring->dma_base & 0xC0000000)
|
||||
- bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
|
||||
|
||||
ring->unaligned = bgmac_dma_unaligned(bgmac, ring,
|
||||
BGMAC_DMA_RING_RX);
|
|
@ -1,76 +0,0 @@
|
|||
From c25b23b8a387e7d31f7a74af8e37b61e9e6ebb21 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
|
||||
Date: Fri, 20 Mar 2015 23:14:31 +0100
|
||||
Subject: [PATCH] bgmac: register fixed PHY for ARM BCM470X / BCM5301X chipsets
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
On ARM SoCs with bgmac Ethernet hardware we don't have any normal PHY.
|
||||
There is always a switch attached but it's not even controlled over MDIO
|
||||
like in case of MIPS devices.
|
||||
We need a fixed PHY to be able to send/receive packets from the switch.
|
||||
|
||||
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/ethernet/broadcom/bgmac.c | 34 ++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 34 insertions(+)
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/phy.h>
|
||||
+#include <linux/phy_fixed.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <bcm47xx_nvram.h>
|
||||
@@ -1330,13 +1331,46 @@ static void bgmac_adjust_link(struct net
|
||||
}
|
||||
}
|
||||
|
||||
+static int bgmac_fixed_phy_register(struct bgmac *bgmac)
|
||||
+{
|
||||
+ struct fixed_phy_status fphy_status = {
|
||||
+ .link = 1,
|
||||
+ .speed = SPEED_1000,
|
||||
+ .duplex = DUPLEX_FULL,
|
||||
+ };
|
||||
+ struct phy_device *phy_dev;
|
||||
+ int err;
|
||||
+
|
||||
+ phy_dev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
|
||||
+ if (!phy_dev || IS_ERR(phy_dev)) {
|
||||
+ bgmac_err(bgmac, "Failed to register fixed PHY device\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ err = phy_connect_direct(bgmac->net_dev, phy_dev, bgmac_adjust_link,
|
||||
+ PHY_INTERFACE_MODE_MII);
|
||||
+ if (err) {
|
||||
+ bgmac_err(bgmac, "Connecting PHY failed\n");
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ bgmac->phy_dev = phy_dev;
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
static int bgmac_mii_register(struct bgmac *bgmac)
|
||||
{
|
||||
+ struct bcma_chipinfo *ci = &bgmac->core->bus->chipinfo;
|
||||
struct mii_bus *mii_bus;
|
||||
struct phy_device *phy_dev;
|
||||
char bus_id[MII_BUS_ID_SIZE + 3];
|
||||
int i, err = 0;
|
||||
|
||||
+ if (ci->id == BCMA_CHIP_ID_BCM4707 ||
|
||||
+ ci->id == BCMA_CHIP_ID_BCM53018)
|
||||
+ return bgmac_fixed_phy_register(bgmac);
|
||||
+
|
||||
mii_bus = mdiobus_alloc();
|
||||
if (!mii_bus)
|
||||
return -ENOMEM;
|
|
@ -1,28 +0,0 @@
|
|||
From fc300dc3733fdc328e6e10c7b8379b60c26cd648 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
|
||||
Date: Fri, 20 Mar 2015 23:14:32 +0100
|
||||
Subject: [PATCH] bgmac: allow enabling on ARCH_BCM_5301X
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Home routers based on ARM SoCs like BCM4708 also have bcma bus with core
|
||||
supported by bgmac.
|
||||
|
||||
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/ethernet/broadcom/Kconfig | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/Kconfig
|
||||
+++ b/drivers/net/ethernet/broadcom/Kconfig
|
||||
@@ -143,7 +143,7 @@ config BNX2X_SRIOV
|
||||
|
||||
config BGMAC
|
||||
tristate "BCMA bus GBit core support"
|
||||
- depends on BCMA_HOST_SOC && HAS_DMA && BCM47XX
|
||||
+ depends on BCMA_HOST_SOC && HAS_DMA && (BCM47XX || ARCH_BCM_5301X)
|
||||
select PHYLIB
|
||||
---help---
|
||||
This driver supports GBit MAC and BCM4706 GBit MAC cores on BCMA bus.
|
|
@ -1,30 +0,0 @@
|
|||
From 37e9a6904520b525b542ecd67201164d06fdb95a Mon Sep 17 00:00:00 2001
|
||||
From: Mark Salter <msalter@redhat.com>
|
||||
Date: Thu, 11 Dec 2014 23:03:26 -0500
|
||||
Subject: [PATCH] net: phy: export fixed_phy_register()
|
||||
|
||||
When building the bcmgenet driver as module, I get:
|
||||
|
||||
ERROR: "fixed_phy_register" [drivers/net/ethernet/broadcom/genet/genet.ko] undefined!
|
||||
|
||||
commit b0ba512e225d72 ("net: bcmgenet: enable driver to work without device
|
||||
tree") which added a call to fixed_phy_register. But fixed_phy_register
|
||||
needs to be exported if used from a module.
|
||||
|
||||
Signed-off-by: Mark Salter <msalter@redhat.com>
|
||||
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/fixed.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/drivers/net/phy/fixed.c
|
||||
+++ b/drivers/net/phy/fixed.c
|
||||
@@ -274,6 +274,7 @@ struct phy_device *fixed_phy_register(un
|
||||
|
||||
return phy;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(fixed_phy_register);
|
||||
|
||||
static int __init fixed_mdio_bus_init(void)
|
||||
{
|
|
@ -1,24 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 23 Mar 2015 02:40:06 +0100
|
||||
Subject: [PATCH] bgmac: fix descriptor frame start/end definitions
|
||||
|
||||
The start-of-frame and end-of-frame bits were accidentally swapped.
|
||||
In the current code it does not make any difference, since they are
|
||||
always used together.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -345,8 +345,8 @@
|
||||
|
||||
#define BGMAC_DESC_CTL0_EOT 0x10000000 /* End of ring */
|
||||
#define BGMAC_DESC_CTL0_IOC 0x20000000 /* IRQ on complete */
|
||||
-#define BGMAC_DESC_CTL0_SOF 0x40000000 /* Start of frame */
|
||||
-#define BGMAC_DESC_CTL0_EOF 0x80000000 /* End of frame */
|
||||
+#define BGMAC_DESC_CTL0_EOF 0x40000000 /* End of frame */
|
||||
+#define BGMAC_DESC_CTL0_SOF 0x80000000 /* Start of frame */
|
||||
#define BGMAC_DESC_CTL1_LEN 0x00001FFF
|
||||
|
||||
#define BGMAC_PHY_NOREGS 0x1E
|
|
@ -1,189 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 23 Mar 2015 02:41:25 +0100
|
||||
Subject: [PATCH] bgmac: implement GRO and use build_skb
|
||||
|
||||
This improves performance for routing and local rx
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -276,31 +276,31 @@ static int bgmac_dma_rx_skb_for_slot(str
|
||||
struct bgmac_slot_info *slot)
|
||||
{
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
- struct sk_buff *skb;
|
||||
dma_addr_t dma_addr;
|
||||
struct bgmac_rx_header *rx;
|
||||
+ void *buf;
|
||||
|
||||
/* Alloc skb */
|
||||
- skb = netdev_alloc_skb(bgmac->net_dev, BGMAC_RX_BUF_SIZE);
|
||||
- if (!skb)
|
||||
+ buf = netdev_alloc_frag(BGMAC_RX_ALLOC_SIZE);
|
||||
+ if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Poison - if everything goes fine, hardware will overwrite it */
|
||||
- rx = (struct bgmac_rx_header *)skb->data;
|
||||
+ rx = buf;
|
||||
rx->len = cpu_to_le16(0xdead);
|
||||
rx->flags = cpu_to_le16(0xbeef);
|
||||
|
||||
/* Map skb for the DMA */
|
||||
- dma_addr = dma_map_single(dma_dev, skb->data,
|
||||
- BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
+ dma_addr = dma_map_single(dma_dev, buf, BGMAC_RX_BUF_SIZE,
|
||||
+ DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dma_dev, dma_addr)) {
|
||||
bgmac_err(bgmac, "DMA mapping error\n");
|
||||
- dev_kfree_skb(skb);
|
||||
+ put_page(virt_to_head_page(buf));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Update the slot */
|
||||
- slot->skb = skb;
|
||||
+ slot->buf = buf;
|
||||
slot->dma_addr = dma_addr;
|
||||
|
||||
return 0;
|
||||
@@ -343,8 +343,9 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
while (ring->start != ring->end) {
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
struct bgmac_slot_info *slot = &ring->slots[ring->start];
|
||||
- struct sk_buff *skb = slot->skb;
|
||||
- struct bgmac_rx_header *rx;
|
||||
+ struct bgmac_rx_header *rx = slot->buf;
|
||||
+ struct sk_buff *skb;
|
||||
+ void *buf = slot->buf;
|
||||
u16 len, flags;
|
||||
|
||||
/* Unmap buffer to make it accessible to the CPU */
|
||||
@@ -352,7 +353,6 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
/* Get info from the header */
|
||||
- rx = (struct bgmac_rx_header *)skb->data;
|
||||
len = le16_to_cpu(rx->len);
|
||||
flags = le16_to_cpu(rx->flags);
|
||||
|
||||
@@ -393,12 +393,13 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
dma_unmap_single(dma_dev, old_dma_addr,
|
||||
BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
+ skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE);
|
||||
skb_put(skb, BGMAC_RX_FRAME_OFFSET + len);
|
||||
skb_pull(skb, BGMAC_RX_FRAME_OFFSET);
|
||||
|
||||
skb_checksum_none_assert(skb);
|
||||
skb->protocol = eth_type_trans(skb, bgmac->net_dev);
|
||||
- netif_receive_skb(skb);
|
||||
+ napi_gro_receive(&bgmac->napi, skb);
|
||||
handled++;
|
||||
} while (0);
|
||||
|
||||
@@ -434,12 +435,11 @@ static bool bgmac_dma_unaligned(struct b
|
||||
return false;
|
||||
}
|
||||
|
||||
-static void bgmac_dma_ring_free(struct bgmac *bgmac,
|
||||
- struct bgmac_dma_ring *ring)
|
||||
+static void bgmac_dma_tx_ring_free(struct bgmac *bgmac,
|
||||
+ struct bgmac_dma_ring *ring)
|
||||
{
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
struct bgmac_slot_info *slot;
|
||||
- int size;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ring->num_slots; i++) {
|
||||
@@ -451,23 +451,55 @@ static void bgmac_dma_ring_free(struct b
|
||||
dev_kfree_skb(slot->skb);
|
||||
}
|
||||
}
|
||||
+}
|
||||
+
|
||||
+static void bgmac_dma_rx_ring_free(struct bgmac *bgmac,
|
||||
+ struct bgmac_dma_ring *ring)
|
||||
+{
|
||||
+ struct device *dma_dev = bgmac->core->dma_dev;
|
||||
+ struct bgmac_slot_info *slot;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < ring->num_slots; i++) {
|
||||
+ slot = &ring->slots[i];
|
||||
+ if (!slot->buf)
|
||||
+ continue;
|
||||
|
||||
- if (ring->cpu_base) {
|
||||
- /* Free ring of descriptors */
|
||||
- size = ring->num_slots * sizeof(struct bgmac_dma_desc);
|
||||
- dma_free_coherent(dma_dev, size, ring->cpu_base,
|
||||
- ring->dma_base);
|
||||
+ if (slot->dma_addr)
|
||||
+ dma_unmap_single(dma_dev, slot->dma_addr,
|
||||
+ BGMAC_RX_BUF_SIZE,
|
||||
+ DMA_FROM_DEVICE);
|
||||
+ put_page(virt_to_head_page(slot->buf));
|
||||
}
|
||||
}
|
||||
|
||||
+static void bgmac_dma_ring_desc_free(struct bgmac *bgmac,
|
||||
+ struct bgmac_dma_ring *ring)
|
||||
+{
|
||||
+ struct device *dma_dev = bgmac->core->dma_dev;
|
||||
+ int size;
|
||||
+
|
||||
+ if (!ring->cpu_base)
|
||||
+ return;
|
||||
+
|
||||
+ /* Free ring of descriptors */
|
||||
+ size = ring->num_slots * sizeof(struct bgmac_dma_desc);
|
||||
+ dma_free_coherent(dma_dev, size, ring->cpu_base,
|
||||
+ ring->dma_base);
|
||||
+}
|
||||
+
|
||||
static void bgmac_dma_free(struct bgmac *bgmac)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
|
||||
- bgmac_dma_ring_free(bgmac, &bgmac->tx_ring[i]);
|
||||
- for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
|
||||
- bgmac_dma_ring_free(bgmac, &bgmac->rx_ring[i]);
|
||||
+ for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
|
||||
+ bgmac_dma_tx_ring_free(bgmac, &bgmac->tx_ring[i]);
|
||||
+ bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i]);
|
||||
+ }
|
||||
+ for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
|
||||
+ bgmac_dma_rx_ring_free(bgmac, &bgmac->rx_ring[i]);
|
||||
+ bgmac_dma_ring_desc_free(bgmac, &bgmac->rx_ring[i]);
|
||||
+ }
|
||||
}
|
||||
|
||||
static int bgmac_dma_alloc(struct bgmac *bgmac)
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -362,6 +362,8 @@
|
||||
#define BGMAC_RX_FRAME_OFFSET 30 /* There are 2 unused bytes between header and real data */
|
||||
#define BGMAC_RX_MAX_FRAME_SIZE 1536 /* Copied from b44/tg3 */
|
||||
#define BGMAC_RX_BUF_SIZE (BGMAC_RX_FRAME_OFFSET + BGMAC_RX_MAX_FRAME_SIZE)
|
||||
+#define BGMAC_RX_ALLOC_SIZE (SKB_DATA_ALIGN(BGMAC_RX_BUF_SIZE) + \
|
||||
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
|
||||
|
||||
#define BGMAC_BFL_ENETROBO 0x0010 /* has ephy roboswitch spi */
|
||||
#define BGMAC_BFL_ENETADM 0x0080 /* has ADMtek switch */
|
||||
@@ -383,7 +385,10 @@
|
||||
#define ETHER_MAX_LEN 1518
|
||||
|
||||
struct bgmac_slot_info {
|
||||
- struct sk_buff *skb;
|
||||
+ union {
|
||||
+ struct sk_buff *skb;
|
||||
+ void *buf;
|
||||
+ };
|
||||
dma_addr_t dma_addr;
|
||||
};
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 23 Mar 2015 02:42:26 +0100
|
||||
Subject: [PATCH] bgmac: implement scatter/gather support
|
||||
|
||||
Always use software checksumming, since the hardware does not have any
|
||||
checksum offload support.
|
||||
This significantly improves local TCP tx performance.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -115,53 +115,91 @@ static void bgmac_dma_tx_enable(struct b
|
||||
bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_CTL, ctl);
|
||||
}
|
||||
|
||||
+static void
|
||||
+bgmac_dma_tx_add_buf(struct bgmac *bgmac, struct bgmac_dma_ring *ring,
|
||||
+ int i, int len, u32 ctl0)
|
||||
+{
|
||||
+ struct bgmac_slot_info *slot;
|
||||
+ struct bgmac_dma_desc *dma_desc;
|
||||
+ u32 ctl1;
|
||||
+
|
||||
+ if (i == ring->num_slots - 1)
|
||||
+ ctl0 |= BGMAC_DESC_CTL0_EOT;
|
||||
+
|
||||
+ ctl1 = len & BGMAC_DESC_CTL1_LEN;
|
||||
+
|
||||
+ slot = &ring->slots[i];
|
||||
+ dma_desc = &ring->cpu_base[i];
|
||||
+ dma_desc->addr_low = cpu_to_le32(lower_32_bits(slot->dma_addr));
|
||||
+ dma_desc->addr_high = cpu_to_le32(upper_32_bits(slot->dma_addr));
|
||||
+ dma_desc->ctl0 = cpu_to_le32(ctl0);
|
||||
+ dma_desc->ctl1 = cpu_to_le32(ctl1);
|
||||
+}
|
||||
+
|
||||
static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac,
|
||||
struct bgmac_dma_ring *ring,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
struct net_device *net_dev = bgmac->net_dev;
|
||||
- struct bgmac_dma_desc *dma_desc;
|
||||
- struct bgmac_slot_info *slot;
|
||||
- u32 ctl0, ctl1;
|
||||
+ struct bgmac_slot_info *slot = &ring->slots[ring->end];
|
||||
int free_slots;
|
||||
+ int nr_frags;
|
||||
+ u32 flags;
|
||||
+ int index = ring->end;
|
||||
+ int i;
|
||||
|
||||
if (skb->len > BGMAC_DESC_CTL1_LEN) {
|
||||
bgmac_err(bgmac, "Too long skb (%d)\n", skb->len);
|
||||
- goto err_stop_drop;
|
||||
+ goto err_drop;
|
||||
}
|
||||
|
||||
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
|
||||
+ skb_checksum_help(skb);
|
||||
+
|
||||
+ nr_frags = skb_shinfo(skb)->nr_frags;
|
||||
+
|
||||
if (ring->start <= ring->end)
|
||||
free_slots = ring->start - ring->end + BGMAC_TX_RING_SLOTS;
|
||||
else
|
||||
free_slots = ring->start - ring->end;
|
||||
- if (free_slots == 1) {
|
||||
+
|
||||
+ if (free_slots <= nr_frags + 1) {
|
||||
bgmac_err(bgmac, "TX ring is full, queue should be stopped!\n");
|
||||
netif_stop_queue(net_dev);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
- slot = &ring->slots[ring->end];
|
||||
- slot->skb = skb;
|
||||
- slot->dma_addr = dma_map_single(dma_dev, skb->data, skb->len,
|
||||
+ slot->dma_addr = dma_map_single(dma_dev, skb->data, skb_headlen(skb),
|
||||
DMA_TO_DEVICE);
|
||||
- if (dma_mapping_error(dma_dev, slot->dma_addr)) {
|
||||
- bgmac_err(bgmac, "Mapping error of skb on ring 0x%X\n",
|
||||
- ring->mmio_base);
|
||||
- goto err_stop_drop;
|
||||
- }
|
||||
+ if (unlikely(dma_mapping_error(dma_dev, slot->dma_addr)))
|
||||
+ goto err_dma_head;
|
||||
|
||||
- ctl0 = BGMAC_DESC_CTL0_IOC | BGMAC_DESC_CTL0_SOF | BGMAC_DESC_CTL0_EOF;
|
||||
- if (ring->end == ring->num_slots - 1)
|
||||
- ctl0 |= BGMAC_DESC_CTL0_EOT;
|
||||
- ctl1 = skb->len & BGMAC_DESC_CTL1_LEN;
|
||||
+ flags = BGMAC_DESC_CTL0_SOF;
|
||||
+ if (!nr_frags)
|
||||
+ flags |= BGMAC_DESC_CTL0_EOF | BGMAC_DESC_CTL0_IOC;
|
||||
+
|
||||
+ bgmac_dma_tx_add_buf(bgmac, ring, index, skb_headlen(skb), flags);
|
||||
+ flags = 0;
|
||||
+
|
||||
+ for (i = 0; i < nr_frags; i++) {
|
||||
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
|
||||
+ int len = skb_frag_size(frag);
|
||||
+
|
||||
+ index = (index + 1) % BGMAC_TX_RING_SLOTS;
|
||||
+ slot = &ring->slots[index];
|
||||
+ slot->dma_addr = skb_frag_dma_map(dma_dev, frag, 0,
|
||||
+ len, DMA_TO_DEVICE);
|
||||
+ if (unlikely(dma_mapping_error(dma_dev, slot->dma_addr)))
|
||||
+ goto err_dma;
|
||||
|
||||
- dma_desc = ring->cpu_base;
|
||||
- dma_desc += ring->end;
|
||||
- dma_desc->addr_low = cpu_to_le32(lower_32_bits(slot->dma_addr));
|
||||
- dma_desc->addr_high = cpu_to_le32(upper_32_bits(slot->dma_addr));
|
||||
- dma_desc->ctl0 = cpu_to_le32(ctl0);
|
||||
- dma_desc->ctl1 = cpu_to_le32(ctl1);
|
||||
+ if (i == nr_frags - 1)
|
||||
+ flags |= BGMAC_DESC_CTL0_EOF | BGMAC_DESC_CTL0_IOC;
|
||||
+
|
||||
+ bgmac_dma_tx_add_buf(bgmac, ring, index, len, flags);
|
||||
+ }
|
||||
+
|
||||
+ slot->skb = skb;
|
||||
|
||||
netdev_sent_queue(net_dev, skb->len);
|
||||
|
||||
@@ -170,20 +208,35 @@ static netdev_tx_t bgmac_dma_tx_add(stru
|
||||
/* Increase ring->end to point empty slot. We tell hardware the first
|
||||
* slot it should *not* read.
|
||||
*/
|
||||
- if (++ring->end >= BGMAC_TX_RING_SLOTS)
|
||||
- ring->end = 0;
|
||||
+ ring->end = (index + 1) % BGMAC_TX_RING_SLOTS;
|
||||
bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_INDEX,
|
||||
ring->index_base +
|
||||
ring->end * sizeof(struct bgmac_dma_desc));
|
||||
|
||||
- /* Always keep one slot free to allow detecting bugged calls. */
|
||||
- if (--free_slots == 1)
|
||||
+ free_slots -= nr_frags + 1;
|
||||
+ if (free_slots < 8)
|
||||
netif_stop_queue(net_dev);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
-err_stop_drop:
|
||||
- netif_stop_queue(net_dev);
|
||||
+err_dma:
|
||||
+ dma_unmap_single(dma_dev, slot->dma_addr, skb_headlen(skb),
|
||||
+ DMA_TO_DEVICE);
|
||||
+
|
||||
+ while (i > 0) {
|
||||
+ int index = (ring->end + i) % BGMAC_TX_RING_SLOTS;
|
||||
+ struct bgmac_slot_info *slot = &ring->slots[index];
|
||||
+ u32 ctl1 = le32_to_cpu(ring->cpu_base[index].ctl1);
|
||||
+ int len = ctl1 & BGMAC_DESC_CTL1_LEN;
|
||||
+
|
||||
+ dma_unmap_page(dma_dev, slot->dma_addr, len, DMA_TO_DEVICE);
|
||||
+ }
|
||||
+
|
||||
+err_dma_head:
|
||||
+ bgmac_err(bgmac, "Mapping error of skb on ring 0x%X\n",
|
||||
+ ring->mmio_base);
|
||||
+
|
||||
+err_drop:
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
@@ -205,32 +258,45 @@ static void bgmac_dma_tx_free(struct bgm
|
||||
|
||||
while (ring->start != empty_slot) {
|
||||
struct bgmac_slot_info *slot = &ring->slots[ring->start];
|
||||
+ u32 ctl1 = le32_to_cpu(ring->cpu_base[ring->start].ctl1);
|
||||
+ int len = ctl1 & BGMAC_DESC_CTL1_LEN;
|
||||
|
||||
- if (slot->skb) {
|
||||
+ if (!slot->dma_addr) {
|
||||
+ bgmac_err(bgmac, "Hardware reported transmission for empty TX ring slot %d! End of ring: %d\n",
|
||||
+ ring->start, ring->end);
|
||||
+ goto next;
|
||||
+ }
|
||||
+
|
||||
+ if (ctl1 & BGMAC_DESC_CTL0_SOF)
|
||||
/* Unmap no longer used buffer */
|
||||
- dma_unmap_single(dma_dev, slot->dma_addr,
|
||||
- slot->skb->len, DMA_TO_DEVICE);
|
||||
- slot->dma_addr = 0;
|
||||
+ dma_unmap_single(dma_dev, slot->dma_addr, len,
|
||||
+ DMA_TO_DEVICE);
|
||||
+ else
|
||||
+ dma_unmap_page(dma_dev, slot->dma_addr, len,
|
||||
+ DMA_TO_DEVICE);
|
||||
|
||||
+ if (slot->skb) {
|
||||
bytes_compl += slot->skb->len;
|
||||
pkts_compl++;
|
||||
|
||||
/* Free memory! :) */
|
||||
dev_kfree_skb(slot->skb);
|
||||
slot->skb = NULL;
|
||||
- } else {
|
||||
- bgmac_err(bgmac, "Hardware reported transmission for empty TX ring slot %d! End of ring: %d\n",
|
||||
- ring->start, ring->end);
|
||||
}
|
||||
|
||||
+next:
|
||||
+ slot->dma_addr = 0;
|
||||
if (++ring->start >= BGMAC_TX_RING_SLOTS)
|
||||
ring->start = 0;
|
||||
freed = true;
|
||||
}
|
||||
|
||||
+ if (!pkts_compl)
|
||||
+ return;
|
||||
+
|
||||
netdev_completed_queue(bgmac->net_dev, pkts_compl, bytes_compl);
|
||||
|
||||
- if (freed && netif_queue_stopped(bgmac->net_dev))
|
||||
+ if (netif_queue_stopped(bgmac->net_dev))
|
||||
netif_wake_queue(bgmac->net_dev);
|
||||
}
|
||||
|
||||
@@ -439,17 +505,25 @@ static void bgmac_dma_tx_ring_free(struc
|
||||
struct bgmac_dma_ring *ring)
|
||||
{
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
+ struct bgmac_dma_desc *dma_desc = ring->cpu_base;
|
||||
struct bgmac_slot_info *slot;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ring->num_slots; i++) {
|
||||
+ int len = dma_desc[i].ctl1 & BGMAC_DESC_CTL1_LEN;
|
||||
+
|
||||
slot = &ring->slots[i];
|
||||
- if (slot->skb) {
|
||||
- if (slot->dma_addr)
|
||||
- dma_unmap_single(dma_dev, slot->dma_addr,
|
||||
- slot->skb->len, DMA_TO_DEVICE);
|
||||
- dev_kfree_skb(slot->skb);
|
||||
- }
|
||||
+ dev_kfree_skb(slot->skb);
|
||||
+
|
||||
+ if (!slot->dma_addr)
|
||||
+ continue;
|
||||
+
|
||||
+ if (slot->skb)
|
||||
+ dma_unmap_single(dma_dev, slot->dma_addr,
|
||||
+ len, DMA_TO_DEVICE);
|
||||
+ else
|
||||
+ dma_unmap_page(dma_dev, slot->dma_addr,
|
||||
+ len, DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1588,6 +1662,10 @@ static int bgmac_probe(struct bcma_devic
|
||||
goto err_dma_free;
|
||||
}
|
||||
|
||||
+ net_dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
|
||||
+ net_dev->hw_features = net_dev->features;
|
||||
+ net_dev->vlan_features = net_dev->features;
|
||||
+
|
||||
err = register_netdev(bgmac->net_dev);
|
||||
if (err) {
|
||||
bgmac_err(bgmac, "Cannot register net device\n");
|
|
@ -1,125 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 09:58:56 +0200
|
||||
Subject: [PATCH] bgmac: simplify tx ring index handling
|
||||
|
||||
Keep incrementing ring->start and ring->end instead of pointing it to
|
||||
the actual ring slot entry. This simplifies the calculation of the
|
||||
number of free slots.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -142,11 +142,10 @@ static netdev_tx_t bgmac_dma_tx_add(stru
|
||||
{
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
struct net_device *net_dev = bgmac->net_dev;
|
||||
- struct bgmac_slot_info *slot = &ring->slots[ring->end];
|
||||
- int free_slots;
|
||||
+ int index = ring->end % BGMAC_TX_RING_SLOTS;
|
||||
+ struct bgmac_slot_info *slot = &ring->slots[index];
|
||||
int nr_frags;
|
||||
u32 flags;
|
||||
- int index = ring->end;
|
||||
int i;
|
||||
|
||||
if (skb->len > BGMAC_DESC_CTL1_LEN) {
|
||||
@@ -159,12 +158,10 @@ static netdev_tx_t bgmac_dma_tx_add(stru
|
||||
|
||||
nr_frags = skb_shinfo(skb)->nr_frags;
|
||||
|
||||
- if (ring->start <= ring->end)
|
||||
- free_slots = ring->start - ring->end + BGMAC_TX_RING_SLOTS;
|
||||
- else
|
||||
- free_slots = ring->start - ring->end;
|
||||
-
|
||||
- if (free_slots <= nr_frags + 1) {
|
||||
+ /* ring->end - ring->start will return the number of valid slots,
|
||||
+ * even when ring->end overflows
|
||||
+ */
|
||||
+ if (ring->end - ring->start + nr_frags + 1 >= BGMAC_TX_RING_SLOTS) {
|
||||
bgmac_err(bgmac, "TX ring is full, queue should be stopped!\n");
|
||||
netif_stop_queue(net_dev);
|
||||
return NETDEV_TX_BUSY;
|
||||
@@ -200,7 +197,7 @@ static netdev_tx_t bgmac_dma_tx_add(stru
|
||||
}
|
||||
|
||||
slot->skb = skb;
|
||||
-
|
||||
+ ring->end += nr_frags + 1;
|
||||
netdev_sent_queue(net_dev, skb->len);
|
||||
|
||||
wmb();
|
||||
@@ -208,13 +205,12 @@ static netdev_tx_t bgmac_dma_tx_add(stru
|
||||
/* Increase ring->end to point empty slot. We tell hardware the first
|
||||
* slot it should *not* read.
|
||||
*/
|
||||
- ring->end = (index + 1) % BGMAC_TX_RING_SLOTS;
|
||||
bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_TX_INDEX,
|
||||
ring->index_base +
|
||||
- ring->end * sizeof(struct bgmac_dma_desc));
|
||||
+ (ring->end % BGMAC_TX_RING_SLOTS) *
|
||||
+ sizeof(struct bgmac_dma_desc));
|
||||
|
||||
- free_slots -= nr_frags + 1;
|
||||
- if (free_slots < 8)
|
||||
+ if (ring->end - ring->start >= BGMAC_TX_RING_SLOTS - 8)
|
||||
netif_stop_queue(net_dev);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@@ -256,17 +252,17 @@ static void bgmac_dma_tx_free(struct bgm
|
||||
empty_slot &= BGMAC_DMA_TX_STATDPTR;
|
||||
empty_slot /= sizeof(struct bgmac_dma_desc);
|
||||
|
||||
- while (ring->start != empty_slot) {
|
||||
- struct bgmac_slot_info *slot = &ring->slots[ring->start];
|
||||
- u32 ctl1 = le32_to_cpu(ring->cpu_base[ring->start].ctl1);
|
||||
- int len = ctl1 & BGMAC_DESC_CTL1_LEN;
|
||||
+ while (ring->start != ring->end) {
|
||||
+ int slot_idx = ring->start % BGMAC_TX_RING_SLOTS;
|
||||
+ struct bgmac_slot_info *slot = &ring->slots[slot_idx];
|
||||
+ u32 ctl1;
|
||||
+ int len;
|
||||
|
||||
- if (!slot->dma_addr) {
|
||||
- bgmac_err(bgmac, "Hardware reported transmission for empty TX ring slot %d! End of ring: %d\n",
|
||||
- ring->start, ring->end);
|
||||
- goto next;
|
||||
- }
|
||||
+ if (slot_idx == empty_slot)
|
||||
+ break;
|
||||
|
||||
+ ctl1 = le32_to_cpu(ring->cpu_base[slot_idx].ctl1);
|
||||
+ len = ctl1 & BGMAC_DESC_CTL1_LEN;
|
||||
if (ctl1 & BGMAC_DESC_CTL0_SOF)
|
||||
/* Unmap no longer used buffer */
|
||||
dma_unmap_single(dma_dev, slot->dma_addr, len,
|
||||
@@ -284,10 +280,8 @@ static void bgmac_dma_tx_free(struct bgm
|
||||
slot->skb = NULL;
|
||||
}
|
||||
|
||||
-next:
|
||||
slot->dma_addr = 0;
|
||||
- if (++ring->start >= BGMAC_TX_RING_SLOTS)
|
||||
- ring->start = 0;
|
||||
+ ring->start++;
|
||||
freed = true;
|
||||
}
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -414,10 +414,10 @@ enum bgmac_dma_ring_type {
|
||||
* empty.
|
||||
*/
|
||||
struct bgmac_dma_ring {
|
||||
- u16 num_slots;
|
||||
- u16 start;
|
||||
- u16 end;
|
||||
+ u32 start;
|
||||
+ u32 end;
|
||||
|
||||
+ u16 num_slots;
|
||||
u16 mmio_base;
|
||||
struct bgmac_dma_desc *cpu_base;
|
||||
dma_addr_t dma_base;
|
|
@ -1,87 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 10:08:04 +0200
|
||||
Subject: [PATCH] bgmac: leave interrupts disabled as long as there is work
|
||||
to do
|
||||
|
||||
Always poll rx and tx during NAPI poll instead of relying on the status
|
||||
of the first interrupt. This prevents bgmac_poll from leaving unfinished
|
||||
work around until the next IRQ.
|
||||
In my tests this makes bridging/routing throughput under heavy load more
|
||||
stable and ensures that no new IRQs arrive as long as bgmac_poll uses up
|
||||
the entire budget.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -1109,8 +1109,6 @@ static void bgmac_chip_reset(struct bgma
|
||||
bgmac_phy_init(bgmac);
|
||||
|
||||
netdev_reset_queue(bgmac->net_dev);
|
||||
-
|
||||
- bgmac->int_status = 0;
|
||||
}
|
||||
|
||||
static void bgmac_chip_intrs_on(struct bgmac *bgmac)
|
||||
@@ -1225,14 +1223,13 @@ static irqreturn_t bgmac_interrupt(int i
|
||||
if (!int_status)
|
||||
return IRQ_NONE;
|
||||
|
||||
- /* Ack */
|
||||
- bgmac_write(bgmac, BGMAC_INT_STATUS, int_status);
|
||||
+ int_status &= ~(BGMAC_IS_TX0 | BGMAC_IS_RX);
|
||||
+ if (int_status)
|
||||
+ bgmac_err(bgmac, "Unknown IRQs: 0x%08X\n", int_status);
|
||||
|
||||
/* Disable new interrupts until handling existing ones */
|
||||
bgmac_chip_intrs_off(bgmac);
|
||||
|
||||
- bgmac->int_status = int_status;
|
||||
-
|
||||
napi_schedule(&bgmac->napi);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@@ -1241,25 +1238,17 @@ static irqreturn_t bgmac_interrupt(int i
|
||||
static int bgmac_poll(struct napi_struct *napi, int weight)
|
||||
{
|
||||
struct bgmac *bgmac = container_of(napi, struct bgmac, napi);
|
||||
- struct bgmac_dma_ring *ring;
|
||||
int handled = 0;
|
||||
|
||||
- if (bgmac->int_status & BGMAC_IS_TX0) {
|
||||
- ring = &bgmac->tx_ring[0];
|
||||
- bgmac_dma_tx_free(bgmac, ring);
|
||||
- bgmac->int_status &= ~BGMAC_IS_TX0;
|
||||
- }
|
||||
+ /* Ack */
|
||||
+ bgmac_write(bgmac, BGMAC_INT_STATUS, ~0);
|
||||
|
||||
- if (bgmac->int_status & BGMAC_IS_RX) {
|
||||
- ring = &bgmac->rx_ring[0];
|
||||
- handled += bgmac_dma_rx_read(bgmac, ring, weight);
|
||||
- bgmac->int_status &= ~BGMAC_IS_RX;
|
||||
- }
|
||||
+ bgmac_dma_tx_free(bgmac, &bgmac->tx_ring[0]);
|
||||
+ handled += bgmac_dma_rx_read(bgmac, &bgmac->rx_ring[0], weight);
|
||||
|
||||
- if (bgmac->int_status) {
|
||||
- bgmac_err(bgmac, "Unknown IRQs: 0x%08X\n", bgmac->int_status);
|
||||
- bgmac->int_status = 0;
|
||||
- }
|
||||
+ /* Poll again if more events arrived in the meantime */
|
||||
+ if (bgmac_read(bgmac, BGMAC_INT_STATUS) & (BGMAC_IS_TX0 | BGMAC_IS_RX))
|
||||
+ return handled;
|
||||
|
||||
if (handled < weight) {
|
||||
napi_complete(napi);
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -452,7 +452,6 @@ struct bgmac {
|
||||
|
||||
/* Int */
|
||||
u32 int_mask;
|
||||
- u32 int_status;
|
||||
|
||||
/* Current MAC state */
|
||||
int mac_speed;
|
|
@ -1,66 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 10:13:28 +0200
|
||||
Subject: [PATCH] bgmac: set received skb headroom to NET_SKB_PAD
|
||||
|
||||
A packet buffer offset of 30 bytes is inefficient, because the first 2
|
||||
bytes end up in a different cacheline.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -346,13 +346,13 @@ static int bgmac_dma_rx_skb_for_slot(str
|
||||
return -ENOMEM;
|
||||
|
||||
/* Poison - if everything goes fine, hardware will overwrite it */
|
||||
- rx = buf;
|
||||
+ rx = buf + BGMAC_RX_BUF_OFFSET;
|
||||
rx->len = cpu_to_le16(0xdead);
|
||||
rx->flags = cpu_to_le16(0xbeef);
|
||||
|
||||
/* Map skb for the DMA */
|
||||
- dma_addr = dma_map_single(dma_dev, buf, BGMAC_RX_BUF_SIZE,
|
||||
- DMA_FROM_DEVICE);
|
||||
+ dma_addr = dma_map_single(dma_dev, buf + BGMAC_RX_BUF_OFFSET,
|
||||
+ BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dma_dev, dma_addr)) {
|
||||
bgmac_err(bgmac, "DMA mapping error\n");
|
||||
put_page(virt_to_head_page(buf));
|
||||
@@ -403,7 +403,7 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
while (ring->start != ring->end) {
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
struct bgmac_slot_info *slot = &ring->slots[ring->start];
|
||||
- struct bgmac_rx_header *rx = slot->buf;
|
||||
+ struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
|
||||
struct sk_buff *skb;
|
||||
void *buf = slot->buf;
|
||||
u16 len, flags;
|
||||
@@ -454,8 +454,10 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE);
|
||||
- skb_put(skb, BGMAC_RX_FRAME_OFFSET + len);
|
||||
- skb_pull(skb, BGMAC_RX_FRAME_OFFSET);
|
||||
+ skb_put(skb, BGMAC_RX_FRAME_OFFSET +
|
||||
+ BGMAC_RX_BUF_OFFSET + len);
|
||||
+ skb_pull(skb, BGMAC_RX_FRAME_OFFSET +
|
||||
+ BGMAC_RX_BUF_OFFSET);
|
||||
|
||||
skb_checksum_none_assert(skb);
|
||||
skb->protocol = eth_type_trans(skb, bgmac->net_dev);
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -360,9 +360,11 @@
|
||||
|
||||
#define BGMAC_RX_HEADER_LEN 28 /* Last 24 bytes are unused. Well... */
|
||||
#define BGMAC_RX_FRAME_OFFSET 30 /* There are 2 unused bytes between header and real data */
|
||||
+#define BGMAC_RX_BUF_OFFSET (NET_SKB_PAD + NET_IP_ALIGN - \
|
||||
+ BGMAC_RX_FRAME_OFFSET)
|
||||
#define BGMAC_RX_MAX_FRAME_SIZE 1536 /* Copied from b44/tg3 */
|
||||
#define BGMAC_RX_BUF_SIZE (BGMAC_RX_FRAME_OFFSET + BGMAC_RX_MAX_FRAME_SIZE)
|
||||
-#define BGMAC_RX_ALLOC_SIZE (SKB_DATA_ALIGN(BGMAC_RX_BUF_SIZE) + \
|
||||
+#define BGMAC_RX_ALLOC_SIZE (SKB_DATA_ALIGN(BGMAC_RX_BUF_SIZE + BGMAC_RX_BUF_OFFSET) + \
|
||||
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
|
||||
|
||||
#define BGMAC_BFL_ENETROBO 0x0010 /* has ephy roboswitch spi */
|
|
@ -1,130 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 22:23:07 +0200
|
||||
Subject: [PATCH] bgmac: simplify/optimize rx DMA error handling
|
||||
|
||||
Allocate a new buffer before processing the completed one. If allocation
|
||||
fails, reuse the old buffer.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -386,6 +386,19 @@ static void bgmac_dma_rx_setup_desc(stru
|
||||
dma_desc->ctl1 = cpu_to_le32(ctl1);
|
||||
}
|
||||
|
||||
+static void bgmac_dma_rx_poison_buf(struct device *dma_dev,
|
||||
+ struct bgmac_slot_info *slot)
|
||||
+{
|
||||
+ struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
|
||||
+
|
||||
+ dma_sync_single_for_cpu(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE,
|
||||
+ DMA_FROM_DEVICE);
|
||||
+ rx->len = cpu_to_le16(0xdead);
|
||||
+ rx->flags = cpu_to_le16(0xbeef);
|
||||
+ dma_sync_single_for_device(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE,
|
||||
+ DMA_FROM_DEVICE);
|
||||
+}
|
||||
+
|
||||
static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring,
|
||||
int weight)
|
||||
{
|
||||
@@ -406,53 +419,35 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
|
||||
struct sk_buff *skb;
|
||||
void *buf = slot->buf;
|
||||
+ dma_addr_t dma_addr = slot->dma_addr;
|
||||
u16 len, flags;
|
||||
|
||||
- /* Unmap buffer to make it accessible to the CPU */
|
||||
- dma_sync_single_for_cpu(dma_dev, slot->dma_addr,
|
||||
- BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
-
|
||||
- /* Get info from the header */
|
||||
- len = le16_to_cpu(rx->len);
|
||||
- flags = le16_to_cpu(rx->flags);
|
||||
-
|
||||
do {
|
||||
- dma_addr_t old_dma_addr = slot->dma_addr;
|
||||
- int err;
|
||||
+ /* Prepare new skb as replacement */
|
||||
+ if (bgmac_dma_rx_skb_for_slot(bgmac, slot)) {
|
||||
+ bgmac_dma_rx_poison_buf(dma_dev, slot);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Unmap buffer to make it accessible to the CPU */
|
||||
+ dma_unmap_single(dma_dev, dma_addr,
|
||||
+ BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
+
|
||||
+ /* Get info from the header */
|
||||
+ len = le16_to_cpu(rx->len);
|
||||
+ flags = le16_to_cpu(rx->flags);
|
||||
|
||||
/* Check for poison and drop or pass the packet */
|
||||
if (len == 0xdead && flags == 0xbeef) {
|
||||
bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n",
|
||||
ring->start);
|
||||
- dma_sync_single_for_device(dma_dev,
|
||||
- slot->dma_addr,
|
||||
- BGMAC_RX_BUF_SIZE,
|
||||
- DMA_FROM_DEVICE);
|
||||
+ put_page(virt_to_head_page(buf));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Omit CRC. */
|
||||
len -= ETH_FCS_LEN;
|
||||
|
||||
- /* Prepare new skb as replacement */
|
||||
- err = bgmac_dma_rx_skb_for_slot(bgmac, slot);
|
||||
- if (err) {
|
||||
- /* Poison the old skb */
|
||||
- rx->len = cpu_to_le16(0xdead);
|
||||
- rx->flags = cpu_to_le16(0xbeef);
|
||||
-
|
||||
- dma_sync_single_for_device(dma_dev,
|
||||
- slot->dma_addr,
|
||||
- BGMAC_RX_BUF_SIZE,
|
||||
- DMA_FROM_DEVICE);
|
||||
- break;
|
||||
- }
|
||||
- bgmac_dma_rx_setup_desc(bgmac, ring, ring->start);
|
||||
-
|
||||
- /* Unmap old skb, we'll pass it to the netfif */
|
||||
- dma_unmap_single(dma_dev, old_dma_addr,
|
||||
- BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
-
|
||||
skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE);
|
||||
skb_put(skb, BGMAC_RX_FRAME_OFFSET +
|
||||
BGMAC_RX_BUF_OFFSET + len);
|
||||
@@ -465,6 +460,8 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
handled++;
|
||||
} while (0);
|
||||
|
||||
+ bgmac_dma_rx_setup_desc(bgmac, ring, ring->start);
|
||||
+
|
||||
if (++ring->start >= BGMAC_RX_RING_SLOTS)
|
||||
ring->start = 0;
|
||||
|
||||
@@ -532,14 +529,14 @@ static void bgmac_dma_rx_ring_free(struc
|
||||
|
||||
for (i = 0; i < ring->num_slots; i++) {
|
||||
slot = &ring->slots[i];
|
||||
- if (!slot->buf)
|
||||
+ if (!slot->dma_addr)
|
||||
continue;
|
||||
|
||||
- if (slot->dma_addr)
|
||||
- dma_unmap_single(dma_dev, slot->dma_addr,
|
||||
- BGMAC_RX_BUF_SIZE,
|
||||
- DMA_FROM_DEVICE);
|
||||
+ dma_unmap_single(dma_dev, slot->dma_addr,
|
||||
+ BGMAC_RX_BUF_SIZE,
|
||||
+ DMA_FROM_DEVICE);
|
||||
put_page(virt_to_head_page(slot->buf));
|
||||
+ slot->dma_addr = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 22:28:20 +0200
|
||||
Subject: [PATCH] bgmac: add check for oversized packets
|
||||
|
||||
In very rare cases, the MAC can catch an internal buffer that is bigger
|
||||
than it's supposed to be. Instead of crashing the kernel, simply pass
|
||||
the buffer back to the hardware
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -445,6 +445,13 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
break;
|
||||
}
|
||||
|
||||
+ if (len > BGMAC_RX_ALLOC_SIZE) {
|
||||
+ bgmac_err(bgmac, "Found oversized packet at slot %d, DMA issue!\n",
|
||||
+ ring->start);
|
||||
+ put_page(virt_to_head_page(buf));
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
/* Omit CRC. */
|
||||
len -= ETH_FCS_LEN;
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 22:36:16 +0200
|
||||
Subject: [PATCH] bgmac: increase rx ring size from 511 to 512
|
||||
|
||||
Limiting it to 511 looks like a failed attempt at leaving one descriptor
|
||||
empty to allow the hardware to stop processing a buffer that has not
|
||||
been prepared yet. However, this doesn't work because this affects the
|
||||
total ring size as well
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -356,7 +356,7 @@
|
||||
#define BGMAC_MAX_RX_RINGS 1
|
||||
|
||||
#define BGMAC_TX_RING_SLOTS 128
|
||||
-#define BGMAC_RX_RING_SLOTS 512 - 1 /* Why -1? Well, Broadcom does that... */
|
||||
+#define BGMAC_RX_RING_SLOTS 512
|
||||
|
||||
#define BGMAC_RX_HEADER_LEN 28 /* Last 24 bytes are unused. Well... */
|
||||
#define BGMAC_RX_FRAME_OFFSET 30 /* There are 2 unused bytes between header and real data */
|
|
@ -1,184 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 23:19:32 +0200
|
||||
Subject: [PATCH] bgmac: simplify dma init/cleanup
|
||||
|
||||
Instead of allocating buffers at device init time and initializing
|
||||
descriptors at device open, do both at the same time (during open).
|
||||
Free all buffers when closing the device.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -562,18 +562,26 @@ static void bgmac_dma_ring_desc_free(str
|
||||
ring->dma_base);
|
||||
}
|
||||
|
||||
-static void bgmac_dma_free(struct bgmac *bgmac)
|
||||
+static void bgmac_dma_cleanup(struct bgmac *bgmac)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
|
||||
+ for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
|
||||
bgmac_dma_tx_ring_free(bgmac, &bgmac->tx_ring[i]);
|
||||
- bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i]);
|
||||
- }
|
||||
- for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
|
||||
+
|
||||
+ for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
|
||||
bgmac_dma_rx_ring_free(bgmac, &bgmac->rx_ring[i]);
|
||||
+}
|
||||
+
|
||||
+static void bgmac_dma_free(struct bgmac *bgmac)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
|
||||
+ bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i]);
|
||||
+
|
||||
+ for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
|
||||
bgmac_dma_ring_desc_free(bgmac, &bgmac->rx_ring[i]);
|
||||
- }
|
||||
}
|
||||
|
||||
static int bgmac_dma_alloc(struct bgmac *bgmac)
|
||||
@@ -621,8 +629,6 @@ static int bgmac_dma_alloc(struct bgmac
|
||||
}
|
||||
|
||||
for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
|
||||
- int j;
|
||||
-
|
||||
ring = &bgmac->rx_ring[i];
|
||||
ring->num_slots = BGMAC_RX_RING_SLOTS;
|
||||
ring->mmio_base = ring_base[i];
|
||||
@@ -645,15 +651,6 @@ static int bgmac_dma_alloc(struct bgmac
|
||||
ring->index_base = lower_32_bits(ring->dma_base);
|
||||
else
|
||||
ring->index_base = 0;
|
||||
-
|
||||
- /* Alloc RX slots */
|
||||
- for (j = 0; j < ring->num_slots; j++) {
|
||||
- err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
|
||||
- if (err) {
|
||||
- bgmac_err(bgmac, "Can't allocate skb for slot in RX ring\n");
|
||||
- goto err_dma_free;
|
||||
- }
|
||||
- }
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -663,10 +660,10 @@ err_dma_free:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
-static void bgmac_dma_init(struct bgmac *bgmac)
|
||||
+static int bgmac_dma_init(struct bgmac *bgmac)
|
||||
{
|
||||
struct bgmac_dma_ring *ring;
|
||||
- int i;
|
||||
+ int i, err;
|
||||
|
||||
for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
|
||||
ring = &bgmac->tx_ring[i];
|
||||
@@ -698,8 +695,13 @@ static void bgmac_dma_init(struct bgmac
|
||||
if (ring->unaligned)
|
||||
bgmac_dma_rx_enable(bgmac, ring);
|
||||
|
||||
- for (j = 0; j < ring->num_slots; j++)
|
||||
+ for (j = 0; j < ring->num_slots; j++) {
|
||||
+ err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
|
||||
+ if (err)
|
||||
+ goto error;
|
||||
+
|
||||
bgmac_dma_rx_setup_desc(bgmac, ring, j);
|
||||
+ }
|
||||
|
||||
bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX,
|
||||
ring->index_base +
|
||||
@@ -708,6 +710,12 @@ static void bgmac_dma_init(struct bgmac
|
||||
ring->start = 0;
|
||||
ring->end = 0;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+error:
|
||||
+ bgmac_dma_cleanup(bgmac);
|
||||
+ return err;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
@@ -1183,11 +1191,8 @@ static void bgmac_enable(struct bgmac *b
|
||||
}
|
||||
|
||||
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipinit */
|
||||
-static void bgmac_chip_init(struct bgmac *bgmac, bool full_init)
|
||||
+static void bgmac_chip_init(struct bgmac *bgmac)
|
||||
{
|
||||
- struct bgmac_dma_ring *ring;
|
||||
- int i;
|
||||
-
|
||||
/* 1 interrupt per received frame */
|
||||
bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT);
|
||||
|
||||
@@ -1205,16 +1210,7 @@ static void bgmac_chip_init(struct bgmac
|
||||
|
||||
bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + ETHER_MAX_LEN);
|
||||
|
||||
- if (full_init) {
|
||||
- bgmac_dma_init(bgmac);
|
||||
- if (1) /* FIXME: is there any case we don't want IRQs? */
|
||||
- bgmac_chip_intrs_on(bgmac);
|
||||
- } else {
|
||||
- for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
|
||||
- ring = &bgmac->rx_ring[i];
|
||||
- bgmac_dma_rx_enable(bgmac, ring);
|
||||
- }
|
||||
- }
|
||||
+ bgmac_chip_intrs_on(bgmac);
|
||||
|
||||
bgmac_enable(bgmac);
|
||||
}
|
||||
@@ -1274,23 +1270,27 @@ static int bgmac_open(struct net_device
|
||||
int err = 0;
|
||||
|
||||
bgmac_chip_reset(bgmac);
|
||||
+
|
||||
+ err = bgmac_dma_init(bgmac);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
/* Specs say about reclaiming rings here, but we do that in DMA init */
|
||||
- bgmac_chip_init(bgmac, true);
|
||||
+ bgmac_chip_init(bgmac);
|
||||
|
||||
err = request_irq(bgmac->core->irq, bgmac_interrupt, IRQF_SHARED,
|
||||
KBUILD_MODNAME, net_dev);
|
||||
if (err < 0) {
|
||||
bgmac_err(bgmac, "IRQ request error: %d!\n", err);
|
||||
- goto err_out;
|
||||
+ bgmac_dma_cleanup(bgmac);
|
||||
+ return err;
|
||||
}
|
||||
napi_enable(&bgmac->napi);
|
||||
|
||||
phy_start(bgmac->phy_dev);
|
||||
|
||||
netif_carrier_on(net_dev);
|
||||
-
|
||||
-err_out:
|
||||
- return err;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int bgmac_stop(struct net_device *net_dev)
|
||||
@@ -1306,6 +1306,7 @@ static int bgmac_stop(struct net_device
|
||||
free_irq(bgmac->core->irq, net_dev);
|
||||
|
||||
bgmac_chip_reset(bgmac);
|
||||
+ bgmac_dma_cleanup(bgmac);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 11:59:47 +0200
|
||||
Subject: [PATCH] bgmac: fix DMA rx corruption
|
||||
|
||||
The driver needs to inform the hardware about the first invalid (not yet
|
||||
filled) rx slot, by writing its DMA descriptor pointer offset to the
|
||||
BGMAC_DMA_RX_INDEX register.
|
||||
|
||||
This register was set to a value exceeding the rx ring size, effectively
|
||||
allowing the hardware constant access to the full ring, regardless of
|
||||
which slots are initialized.
|
||||
|
||||
To fix this issue, always mark the last filled rx slot as invalid.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -366,6 +366,16 @@ static int bgmac_dma_rx_skb_for_slot(str
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void bgmac_dma_rx_update_index(struct bgmac *bgmac,
|
||||
+ struct bgmac_dma_ring *ring)
|
||||
+{
|
||||
+ wmb();
|
||||
+
|
||||
+ bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX,
|
||||
+ ring->index_base +
|
||||
+ ring->end * sizeof(struct bgmac_dma_desc));
|
||||
+}
|
||||
+
|
||||
static void bgmac_dma_rx_setup_desc(struct bgmac *bgmac,
|
||||
struct bgmac_dma_ring *ring, int desc_idx)
|
||||
{
|
||||
@@ -384,6 +394,8 @@ static void bgmac_dma_rx_setup_desc(stru
|
||||
dma_desc->addr_high = cpu_to_le32(upper_32_bits(ring->slots[desc_idx].dma_addr));
|
||||
dma_desc->ctl0 = cpu_to_le32(ctl0);
|
||||
dma_desc->ctl1 = cpu_to_le32(ctl1);
|
||||
+
|
||||
+ ring->end = desc_idx;
|
||||
}
|
||||
|
||||
static void bgmac_dma_rx_poison_buf(struct device *dma_dev,
|
||||
@@ -411,9 +423,7 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
end_slot &= BGMAC_DMA_RX_STATDPTR;
|
||||
end_slot /= sizeof(struct bgmac_dma_desc);
|
||||
|
||||
- ring->end = end_slot;
|
||||
-
|
||||
- while (ring->start != ring->end) {
|
||||
+ while (ring->start != end_slot) {
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
struct bgmac_slot_info *slot = &ring->slots[ring->start];
|
||||
struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
|
||||
@@ -476,6 +486,8 @@ static int bgmac_dma_rx_read(struct bgma
|
||||
break;
|
||||
}
|
||||
|
||||
+ bgmac_dma_rx_update_index(bgmac, ring);
|
||||
+
|
||||
return handled;
|
||||
}
|
||||
|
||||
@@ -695,6 +707,8 @@ static int bgmac_dma_init(struct bgmac *
|
||||
if (ring->unaligned)
|
||||
bgmac_dma_rx_enable(bgmac, ring);
|
||||
|
||||
+ ring->start = 0;
|
||||
+ ring->end = 0;
|
||||
for (j = 0; j < ring->num_slots; j++) {
|
||||
err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
|
||||
if (err)
|
||||
@@ -703,12 +717,7 @@ static int bgmac_dma_init(struct bgmac *
|
||||
bgmac_dma_rx_setup_desc(bgmac, ring, j);
|
||||
}
|
||||
|
||||
- bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX,
|
||||
- ring->index_base +
|
||||
- ring->num_slots * sizeof(struct bgmac_dma_desc));
|
||||
-
|
||||
- ring->start = 0;
|
||||
- ring->end = 0;
|
||||
+ bgmac_dma_rx_update_index(bgmac, ring);
|
||||
}
|
||||
|
||||
return 0;
|
|
@ -1,132 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 12 Apr 2015 23:28:38 +0200
|
||||
Subject: [PATCH] bgmac: drop ring->num_slots
|
||||
|
||||
The ring size is always known at compile time, so make the code a bit
|
||||
more efficient
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -123,7 +123,7 @@ bgmac_dma_tx_add_buf(struct bgmac *bgmac
|
||||
struct bgmac_dma_desc *dma_desc;
|
||||
u32 ctl1;
|
||||
|
||||
- if (i == ring->num_slots - 1)
|
||||
+ if (i == BGMAC_TX_RING_SLOTS - 1)
|
||||
ctl0 |= BGMAC_DESC_CTL0_EOT;
|
||||
|
||||
ctl1 = len & BGMAC_DESC_CTL1_LEN;
|
||||
@@ -382,7 +382,7 @@ static void bgmac_dma_rx_setup_desc(stru
|
||||
struct bgmac_dma_desc *dma_desc = ring->cpu_base + desc_idx;
|
||||
u32 ctl0 = 0, ctl1 = 0;
|
||||
|
||||
- if (desc_idx == ring->num_slots - 1)
|
||||
+ if (desc_idx == BGMAC_RX_RING_SLOTS - 1)
|
||||
ctl0 |= BGMAC_DESC_CTL0_EOT;
|
||||
ctl1 |= BGMAC_RX_BUF_SIZE & BGMAC_DESC_CTL1_LEN;
|
||||
/* Is there any BGMAC device that requires extension? */
|
||||
@@ -521,7 +521,7 @@ static void bgmac_dma_tx_ring_free(struc
|
||||
struct bgmac_slot_info *slot;
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < ring->num_slots; i++) {
|
||||
+ for (i = 0; i < BGMAC_TX_RING_SLOTS; i++) {
|
||||
int len = dma_desc[i].ctl1 & BGMAC_DESC_CTL1_LEN;
|
||||
|
||||
slot = &ring->slots[i];
|
||||
@@ -546,7 +546,7 @@ static void bgmac_dma_rx_ring_free(struc
|
||||
struct bgmac_slot_info *slot;
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < ring->num_slots; i++) {
|
||||
+ for (i = 0; i < BGMAC_RX_RING_SLOTS; i++) {
|
||||
slot = &ring->slots[i];
|
||||
if (!slot->dma_addr)
|
||||
continue;
|
||||
@@ -560,7 +560,8 @@ static void bgmac_dma_rx_ring_free(struc
|
||||
}
|
||||
|
||||
static void bgmac_dma_ring_desc_free(struct bgmac *bgmac,
|
||||
- struct bgmac_dma_ring *ring)
|
||||
+ struct bgmac_dma_ring *ring,
|
||||
+ int num_slots)
|
||||
{
|
||||
struct device *dma_dev = bgmac->core->dma_dev;
|
||||
int size;
|
||||
@@ -569,7 +570,7 @@ static void bgmac_dma_ring_desc_free(str
|
||||
return;
|
||||
|
||||
/* Free ring of descriptors */
|
||||
- size = ring->num_slots * sizeof(struct bgmac_dma_desc);
|
||||
+ size = num_slots * sizeof(struct bgmac_dma_desc);
|
||||
dma_free_coherent(dma_dev, size, ring->cpu_base,
|
||||
ring->dma_base);
|
||||
}
|
||||
@@ -590,10 +591,12 @@ static void bgmac_dma_free(struct bgmac
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
|
||||
- bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i]);
|
||||
+ bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i],
|
||||
+ BGMAC_TX_RING_SLOTS);
|
||||
|
||||
for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
|
||||
- bgmac_dma_ring_desc_free(bgmac, &bgmac->rx_ring[i]);
|
||||
+ bgmac_dma_ring_desc_free(bgmac, &bgmac->rx_ring[i],
|
||||
+ BGMAC_RX_RING_SLOTS);
|
||||
}
|
||||
|
||||
static int bgmac_dma_alloc(struct bgmac *bgmac)
|
||||
@@ -616,11 +619,10 @@ static int bgmac_dma_alloc(struct bgmac
|
||||
|
||||
for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
|
||||
ring = &bgmac->tx_ring[i];
|
||||
- ring->num_slots = BGMAC_TX_RING_SLOTS;
|
||||
ring->mmio_base = ring_base[i];
|
||||
|
||||
/* Alloc ring of descriptors */
|
||||
- size = ring->num_slots * sizeof(struct bgmac_dma_desc);
|
||||
+ size = BGMAC_TX_RING_SLOTS * sizeof(struct bgmac_dma_desc);
|
||||
ring->cpu_base = dma_zalloc_coherent(dma_dev, size,
|
||||
&ring->dma_base,
|
||||
GFP_KERNEL);
|
||||
@@ -642,11 +644,10 @@ static int bgmac_dma_alloc(struct bgmac
|
||||
|
||||
for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
|
||||
ring = &bgmac->rx_ring[i];
|
||||
- ring->num_slots = BGMAC_RX_RING_SLOTS;
|
||||
ring->mmio_base = ring_base[i];
|
||||
|
||||
/* Alloc ring of descriptors */
|
||||
- size = ring->num_slots * sizeof(struct bgmac_dma_desc);
|
||||
+ size = BGMAC_RX_RING_SLOTS * sizeof(struct bgmac_dma_desc);
|
||||
ring->cpu_base = dma_zalloc_coherent(dma_dev, size,
|
||||
&ring->dma_base,
|
||||
GFP_KERNEL);
|
||||
@@ -709,7 +710,7 @@ static int bgmac_dma_init(struct bgmac *
|
||||
|
||||
ring->start = 0;
|
||||
ring->end = 0;
|
||||
- for (j = 0; j < ring->num_slots; j++) {
|
||||
+ for (j = 0; j < BGMAC_RX_RING_SLOTS; j++) {
|
||||
err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
|
||||
if (err)
|
||||
goto error;
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -419,11 +419,10 @@ struct bgmac_dma_ring {
|
||||
u32 start;
|
||||
u32 end;
|
||||
|
||||
- u16 num_slots;
|
||||
- u16 mmio_base;
|
||||
struct bgmac_dma_desc *cpu_base;
|
||||
dma_addr_t dma_base;
|
||||
u32 index_base; /* Used for unaligned rings only, otherwise 0 */
|
||||
+ u16 mmio_base;
|
||||
bool unaligned;
|
||||
|
||||
struct bgmac_slot_info slots[BGMAC_RX_RING_SLOTS];
|
|
@ -1,31 +0,0 @@
|
|||
From b4dfd8e92956b396d3438212bc9a0be6267b8b34 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
|
||||
Date: Tue, 12 Apr 2016 13:30:45 +0200
|
||||
Subject: [PATCH] bgmac: reset & enable Ethernet core before using it
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This fixes Ethernet on D-Link DIR-885L with BCM47094 SoC. Felix reported
|
||||
similar fix was needed for his BCM4709 device (Buffalo WXR-1900DHP?).
|
||||
I tested this for regressions on BCM4706, BCM4708A0 and BCM47081A0.
|
||||
|
||||
Cc: Felix Fietkau <nbd@nbd.name>
|
||||
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.c
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.c
|
||||
@@ -1569,6 +1569,11 @@ static int bgmac_probe(struct bcma_devic
|
||||
*/
|
||||
bcma_core_enable(core, 0);
|
||||
|
||||
+ /* This (reset &) enable is not preset in specs or reference driver but
|
||||
+ * Broadcom does it in arch PCI code when enabling fake PCI device.
|
||||
+ */
|
||||
+ bcma_core_enable(core, 0);
|
||||
+
|
||||
/* Allocation and references */
|
||||
net_dev = alloc_etherdev(sizeof(*bgmac));
|
||||
if (!net_dev)
|
|
@ -1,34 +0,0 @@
|
|||
From c02bc350f9dbce7d637c394a6e1c4d29dc5b28b2 Mon Sep 17 00:00:00 2001
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Tue, 12 Apr 2016 18:27:29 +0200
|
||||
Subject: [PATCH] bgmac: fix MAC soft-reset bit for corerev > 4
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Only core revisions older than 4 use BGMAC_CMDCFG_SR_REV0. This mainly
|
||||
fixes support for BCM4708A0KF SoCs with Ethernet core rev 5 (it means
|
||||
only some devices as most of BCM4708A0KF-s got core rev 4).
|
||||
This was tested for regressions on BCM47094 which doesn't seem to care
|
||||
which bit gets used.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/broadcom/bgmac.h
|
||||
+++ b/drivers/net/ethernet/broadcom/bgmac.h
|
||||
@@ -198,9 +198,9 @@
|
||||
#define BGMAC_CMDCFG_TAI 0x00000200
|
||||
#define BGMAC_CMDCFG_HD 0x00000400 /* Set if in half duplex mode */
|
||||
#define BGMAC_CMDCFG_HD_SHIFT 10
|
||||
-#define BGMAC_CMDCFG_SR_REV0 0x00000800 /* Set to reset mode, for other revs */
|
||||
-#define BGMAC_CMDCFG_SR_REV4 0x00002000 /* Set to reset mode, only for core rev 4 */
|
||||
-#define BGMAC_CMDCFG_SR(rev) ((rev == 4) ? BGMAC_CMDCFG_SR_REV4 : BGMAC_CMDCFG_SR_REV0)
|
||||
+#define BGMAC_CMDCFG_SR_REV0 0x00000800 /* Set to reset mode, for core rev 0-3 */
|
||||
+#define BGMAC_CMDCFG_SR_REV4 0x00002000 /* Set to reset mode, for core rev >= 4 */
|
||||
+#define BGMAC_CMDCFG_SR(rev) ((rev >= 4) ? BGMAC_CMDCFG_SR_REV4 : BGMAC_CMDCFG_SR_REV0)
|
||||
#define BGMAC_CMDCFG_ML 0x00008000 /* Set to activate mac loopback mode */
|
||||
#define BGMAC_CMDCFG_AE 0x00400000
|
||||
#define BGMAC_CMDCFG_CFE 0x00800000
|
|
@ -1,46 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Tue, 2 Dec 2014 10:58:21 -0800
|
||||
Subject: [PATCH] fib_trie: Fix /proc/net/fib_trie when
|
||||
CONFIG_IP_MULTIPLE_TABLES is not defined
|
||||
|
||||
In recent testing I had disabled CONFIG_IP_MULTIPLE_TABLES and as a result
|
||||
when I ran "cat /proc/net/fib_trie" the main trie was displayed multiple
|
||||
times. I found that the problem line of code was in the function
|
||||
fib_trie_seq_next. Specifically the line below caused the indexes to go in
|
||||
the opposite direction of our traversal:
|
||||
|
||||
h = tb->tb_id & (FIB_TABLE_HASHSZ - 1);
|
||||
|
||||
This issue was that the RT tables are defined such that RT_TABLE_LOCAL is ID
|
||||
255, while it is located at TABLE_LOCAL_INDEX of 0, and RT_TABLE_MAIN is 254
|
||||
with a TABLE_MAIN_INDEX of 1. This means that the above line will return 1
|
||||
for the local table and 0 for main. The result is that fib_trie_seq_next
|
||||
will return NULL at the end of the local table, fib_trie_seq_start will
|
||||
return the start of the main table, and then fib_trie_seq_next will loop on
|
||||
main forever as h will always return 0.
|
||||
|
||||
The fix for this is to reverse the ordering of the two tables. It has the
|
||||
advantage of making it so that the tables now print in the same order
|
||||
regardless of if multiple tables are enabled or not. In order to make the
|
||||
definition consistent with the multiple tables case I simply masked the to
|
||||
RT_TABLE_XXX values by (FIB_TABLE_HASHSZ - 1). This way the two table
|
||||
layouts should always stay consistent.
|
||||
|
||||
Fixes: 93456b6 ("[IPV4]: Unify access to the routing tables")
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/include/net/ip_fib.h
|
||||
+++ b/include/net/ip_fib.h
|
||||
@@ -201,8 +201,8 @@ void fib_free_table(struct fib_table *tb
|
||||
|
||||
#ifndef CONFIG_IP_MULTIPLE_TABLES
|
||||
|
||||
-#define TABLE_LOCAL_INDEX 0
|
||||
-#define TABLE_MAIN_INDEX 1
|
||||
+#define TABLE_LOCAL_INDEX (RT_TABLE_LOCAL & (FIB_TABLE_HASHSZ - 1))
|
||||
+#define TABLE_MAIN_INDEX (RT_TABLE_MAIN & (FIB_TABLE_HASHSZ - 1))
|
||||
|
||||
static inline struct fib_table *fib_get_table(struct net *net, u32 id)
|
||||
{
|
|
@ -1,72 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 10 Dec 2014 21:49:22 -0800
|
||||
Subject: [PATCH] fib_trie: Fix trie balancing issue if new node pushes down
|
||||
existing node
|
||||
|
||||
This patch addresses an issue with the level compression of the fib_trie.
|
||||
Specifically in the case of adding a new leaf that triggers a new node to
|
||||
be added that takes the place of the old node. The result is a trie where
|
||||
the 1 child tnode is on one side and one leaf is on the other which gives
|
||||
you a very deep trie. Below is the script I used to generate a trie on
|
||||
dummy0 with a 10.X.X.X family of addresses.
|
||||
|
||||
ip link add type dummy
|
||||
ipval=184549374
|
||||
bit=2
|
||||
for i in `seq 1 23`
|
||||
do
|
||||
ifconfig dummy0:$bit $ipval/8
|
||||
ipval=`expr $ipval - $bit`
|
||||
bit=`expr $bit \* 2`
|
||||
done
|
||||
cat /proc/net/fib_triestat
|
||||
|
||||
Running the script before the patch:
|
||||
|
||||
Local:
|
||||
Aver depth: 10.82
|
||||
Max depth: 23
|
||||
Leaves: 29
|
||||
Prefixes: 30
|
||||
Internal nodes: 27
|
||||
1: 26 2: 1
|
||||
Pointers: 56
|
||||
Null ptrs: 1
|
||||
Total size: 5 kB
|
||||
|
||||
After applying the patch and repeating:
|
||||
|
||||
Local:
|
||||
Aver depth: 4.72
|
||||
Max depth: 9
|
||||
Leaves: 29
|
||||
Prefixes: 30
|
||||
Internal nodes: 12
|
||||
1: 3 2: 2 3: 7
|
||||
Pointers: 70
|
||||
Null ptrs: 30
|
||||
Total size: 4 kB
|
||||
|
||||
What this fix does is start the rebalance at the newly created tnode
|
||||
instead of at the parent tnode. This way if there is a gap between the
|
||||
parent and the new node it doesn't prevent the new tnode from being
|
||||
coalesced with any pre-existing nodes that may have been pushed into one
|
||||
of the new nodes child branches.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -1143,8 +1143,9 @@ static struct list_head *fib_insert_node
|
||||
put_child(tp, cindex, (struct rt_trie_node *)tn);
|
||||
} else {
|
||||
rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);
|
||||
- tp = tn;
|
||||
}
|
||||
+
|
||||
+ tp = tn;
|
||||
}
|
||||
|
||||
if (tp && tp->pos + tp->bits > 32)
|
|
@ -1,200 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:55:29 -0800
|
||||
Subject: [PATCH] fib_trie: Update usage stats to be percpu instead of
|
||||
global variables
|
||||
|
||||
The trie usage stats were currently being shared by all threads that were
|
||||
calling fib_table_lookup. As a result when multiple threads were
|
||||
performing lookups simultaneously the trie would begin to cache bounce
|
||||
between those threads.
|
||||
|
||||
In order to prevent this I have updated the usage stats to use a set of
|
||||
percpu variables. By doing this we should be able to avoid the cache
|
||||
bouncing and still make use of these stats.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_frontend.c
|
||||
+++ b/net/ipv4/fib_frontend.c
|
||||
@@ -67,7 +67,7 @@ static int __net_init fib4_rules_init(st
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
- kfree(local_table);
|
||||
+ fib_free_table(local_table);
|
||||
return -ENOMEM;
|
||||
}
|
||||
#else
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -153,7 +153,7 @@ struct trie_stat {
|
||||
struct trie {
|
||||
struct rt_trie_node __rcu *trie;
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- struct trie_use_stats stats;
|
||||
+ struct trie_use_stats __percpu *stats;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -631,7 +631,7 @@ static struct rt_trie_node *resize(struc
|
||||
if (IS_ERR(tn)) {
|
||||
tn = old_tn;
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.resize_node_skipped++;
|
||||
+ this_cpu_inc(t->stats->resize_node_skipped);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -658,7 +658,7 @@ static struct rt_trie_node *resize(struc
|
||||
if (IS_ERR(tn)) {
|
||||
tn = old_tn;
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.resize_node_skipped++;
|
||||
+ this_cpu_inc(t->stats->resize_node_skipped);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -1357,7 +1357,7 @@ static int check_leaf(struct fib_table *
|
||||
err = fib_props[fa->fa_type].error;
|
||||
if (err) {
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.semantic_match_passed++;
|
||||
+ this_cpu_inc(t->stats->semantic_match_passed);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
@@ -1372,7 +1372,7 @@ static int check_leaf(struct fib_table *
|
||||
continue;
|
||||
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.semantic_match_passed++;
|
||||
+ this_cpu_inc(t->stats->semantic_match_passed);
|
||||
#endif
|
||||
res->prefixlen = li->plen;
|
||||
res->nh_sel = nhsel;
|
||||
@@ -1388,7 +1388,7 @@ static int check_leaf(struct fib_table *
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.semantic_match_miss++;
|
||||
+ this_cpu_inc(t->stats->semantic_match_miss);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1399,6 +1399,9 @@ int fib_table_lookup(struct fib_table *t
|
||||
struct fib_result *res, int fib_flags)
|
||||
{
|
||||
struct trie *t = (struct trie *) tb->tb_data;
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ struct trie_use_stats __percpu *stats = t->stats;
|
||||
+#endif
|
||||
int ret;
|
||||
struct rt_trie_node *n;
|
||||
struct tnode *pn;
|
||||
@@ -1417,7 +1420,7 @@ int fib_table_lookup(struct fib_table *t
|
||||
goto failed;
|
||||
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.gets++;
|
||||
+ this_cpu_inc(stats->gets);
|
||||
#endif
|
||||
|
||||
/* Just a leaf? */
|
||||
@@ -1441,7 +1444,7 @@ int fib_table_lookup(struct fib_table *t
|
||||
|
||||
if (n == NULL) {
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.null_node_hit++;
|
||||
+ this_cpu_inc(stats->null_node_hit);
|
||||
#endif
|
||||
goto backtrace;
|
||||
}
|
||||
@@ -1576,7 +1579,7 @@ backtrace:
|
||||
chopped_off = 0;
|
||||
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- t->stats.backtrack++;
|
||||
+ this_cpu_inc(stats->backtrack);
|
||||
#endif
|
||||
goto backtrace;
|
||||
}
|
||||
@@ -1830,6 +1833,11 @@ int fib_table_flush(struct fib_table *tb
|
||||
|
||||
void fib_free_table(struct fib_table *tb)
|
||||
{
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ struct trie *t = (struct trie *)tb->tb_data;
|
||||
+
|
||||
+ free_percpu(t->stats);
|
||||
+#endif /* CONFIG_IP_FIB_TRIE_STATS */
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
@@ -1973,7 +1981,14 @@ struct fib_table *fib_trie_table(u32 id)
|
||||
tb->tb_num_default = 0;
|
||||
|
||||
t = (struct trie *) tb->tb_data;
|
||||
- memset(t, 0, sizeof(*t));
|
||||
+ RCU_INIT_POINTER(t->trie, NULL);
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ t->stats = alloc_percpu(struct trie_use_stats);
|
||||
+ if (!t->stats) {
|
||||
+ kfree(tb);
|
||||
+ tb = NULL;
|
||||
+ }
|
||||
+#endif
|
||||
|
||||
return tb;
|
||||
}
|
||||
@@ -2139,18 +2154,31 @@ static void trie_show_stats(struct seq_f
|
||||
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
static void trie_show_usage(struct seq_file *seq,
|
||||
- const struct trie_use_stats *stats)
|
||||
+ const struct trie_use_stats __percpu *stats)
|
||||
{
|
||||
+ struct trie_use_stats s = { 0 };
|
||||
+ int cpu;
|
||||
+
|
||||
+ /* loop through all of the CPUs and gather up the stats */
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ const struct trie_use_stats *pcpu = per_cpu_ptr(stats, cpu);
|
||||
+
|
||||
+ s.gets += pcpu->gets;
|
||||
+ s.backtrack += pcpu->backtrack;
|
||||
+ s.semantic_match_passed += pcpu->semantic_match_passed;
|
||||
+ s.semantic_match_miss += pcpu->semantic_match_miss;
|
||||
+ s.null_node_hit += pcpu->null_node_hit;
|
||||
+ s.resize_node_skipped += pcpu->resize_node_skipped;
|
||||
+ }
|
||||
+
|
||||
seq_printf(seq, "\nCounters:\n---------\n");
|
||||
- seq_printf(seq, "gets = %u\n", stats->gets);
|
||||
- seq_printf(seq, "backtracks = %u\n", stats->backtrack);
|
||||
+ seq_printf(seq, "gets = %u\n", s.gets);
|
||||
+ seq_printf(seq, "backtracks = %u\n", s.backtrack);
|
||||
seq_printf(seq, "semantic match passed = %u\n",
|
||||
- stats->semantic_match_passed);
|
||||
- seq_printf(seq, "semantic match miss = %u\n",
|
||||
- stats->semantic_match_miss);
|
||||
- seq_printf(seq, "null node hit= %u\n", stats->null_node_hit);
|
||||
- seq_printf(seq, "skipped node resize = %u\n\n",
|
||||
- stats->resize_node_skipped);
|
||||
+ s.semantic_match_passed);
|
||||
+ seq_printf(seq, "semantic match miss = %u\n", s.semantic_match_miss);
|
||||
+ seq_printf(seq, "null node hit= %u\n", s.null_node_hit);
|
||||
+ seq_printf(seq, "skipped node resize = %u\n\n", s.resize_node_skipped);
|
||||
}
|
||||
#endif /* CONFIG_IP_FIB_TRIE_STATS */
|
||||
|
||||
@@ -2191,7 +2219,7 @@ static int fib_triestat_seq_show(struct
|
||||
trie_collect_stats(t, &stat);
|
||||
trie_show_stats(seq, &stat);
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- trie_show_usage(seq, &t->stats);
|
||||
+ trie_show_usage(seq, t->stats);
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,421 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:55:35 -0800
|
||||
Subject: [PATCH] fib_trie: Make leaf and tnode more uniform
|
||||
|
||||
This change makes some fundamental changes to the way leaves and tnodes are
|
||||
constructed. The big differences are:
|
||||
1. Leaves now populate pos and bits indicating their full key size.
|
||||
2. Trie nodes now mask out their lower bits to be consistent with the leaf
|
||||
3. Both structures have been reordered so that rt_trie_node now consisists
|
||||
of a much larger region including the pos, bits, and rcu portions of
|
||||
the tnode structure.
|
||||
|
||||
On 32b systems this will result in the leaf being 4B larger as the pos and
|
||||
bits values were added to a hole created by the key as it was only 4B in
|
||||
length.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -87,24 +87,38 @@
|
||||
|
||||
typedef unsigned int t_key;
|
||||
|
||||
-#define T_TNODE 0
|
||||
-#define T_LEAF 1
|
||||
-#define NODE_TYPE_MASK 0x1UL
|
||||
-#define NODE_TYPE(node) ((node)->parent & NODE_TYPE_MASK)
|
||||
+#define IS_TNODE(n) ((n)->bits)
|
||||
+#define IS_LEAF(n) (!(n)->bits)
|
||||
|
||||
-#define IS_TNODE(n) (!(n->parent & T_LEAF))
|
||||
-#define IS_LEAF(n) (n->parent & T_LEAF)
|
||||
+struct tnode {
|
||||
+ t_key key;
|
||||
+ unsigned char bits; /* 2log(KEYLENGTH) bits needed */
|
||||
+ unsigned char pos; /* 2log(KEYLENGTH) bits needed */
|
||||
+ struct tnode __rcu *parent;
|
||||
+ union {
|
||||
+ struct rcu_head rcu;
|
||||
+ struct tnode *tnode_free;
|
||||
+ };
|
||||
+ unsigned int full_children; /* KEYLENGTH bits needed */
|
||||
+ unsigned int empty_children; /* KEYLENGTH bits needed */
|
||||
+ struct rt_trie_node __rcu *child[0];
|
||||
+};
|
||||
|
||||
struct rt_trie_node {
|
||||
- unsigned long parent;
|
||||
t_key key;
|
||||
+ unsigned char bits;
|
||||
+ unsigned char pos;
|
||||
+ struct tnode __rcu *parent;
|
||||
+ struct rcu_head rcu;
|
||||
};
|
||||
|
||||
struct leaf {
|
||||
- unsigned long parent;
|
||||
t_key key;
|
||||
- struct hlist_head list;
|
||||
+ unsigned char bits;
|
||||
+ unsigned char pos;
|
||||
+ struct tnode __rcu *parent;
|
||||
struct rcu_head rcu;
|
||||
+ struct hlist_head list;
|
||||
};
|
||||
|
||||
struct leaf_info {
|
||||
@@ -115,20 +129,6 @@ struct leaf_info {
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
-struct tnode {
|
||||
- unsigned long parent;
|
||||
- t_key key;
|
||||
- unsigned char pos; /* 2log(KEYLENGTH) bits needed */
|
||||
- unsigned char bits; /* 2log(KEYLENGTH) bits needed */
|
||||
- unsigned int full_children; /* KEYLENGTH bits needed */
|
||||
- unsigned int empty_children; /* KEYLENGTH bits needed */
|
||||
- union {
|
||||
- struct rcu_head rcu;
|
||||
- struct tnode *tnode_free;
|
||||
- };
|
||||
- struct rt_trie_node __rcu *child[0];
|
||||
-};
|
||||
-
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
struct trie_use_stats {
|
||||
unsigned int gets;
|
||||
@@ -176,38 +176,27 @@ static const int sync_pages = 128;
|
||||
static struct kmem_cache *fn_alias_kmem __read_mostly;
|
||||
static struct kmem_cache *trie_leaf_kmem __read_mostly;
|
||||
|
||||
-/*
|
||||
- * caller must hold RTNL
|
||||
- */
|
||||
-static inline struct tnode *node_parent(const struct rt_trie_node *node)
|
||||
-{
|
||||
- unsigned long parent;
|
||||
+/* caller must hold RTNL */
|
||||
+#define node_parent(n) rtnl_dereference((n)->parent)
|
||||
|
||||
- parent = rcu_dereference_index_check(node->parent, lockdep_rtnl_is_held());
|
||||
+/* caller must hold RCU read lock or RTNL */
|
||||
+#define node_parent_rcu(n) rcu_dereference_rtnl((n)->parent)
|
||||
|
||||
- return (struct tnode *)(parent & ~NODE_TYPE_MASK);
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * caller must hold RCU read lock or RTNL
|
||||
- */
|
||||
-static inline struct tnode *node_parent_rcu(const struct rt_trie_node *node)
|
||||
+/* wrapper for rcu_assign_pointer */
|
||||
+static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr)
|
||||
{
|
||||
- unsigned long parent;
|
||||
-
|
||||
- parent = rcu_dereference_index_check(node->parent, rcu_read_lock_held() ||
|
||||
- lockdep_rtnl_is_held());
|
||||
-
|
||||
- return (struct tnode *)(parent & ~NODE_TYPE_MASK);
|
||||
+ if (node)
|
||||
+ rcu_assign_pointer(node->parent, ptr);
|
||||
}
|
||||
|
||||
-/* Same as rcu_assign_pointer
|
||||
- * but that macro() assumes that value is a pointer.
|
||||
+#define NODE_INIT_PARENT(n, p) RCU_INIT_POINTER((n)->parent, p)
|
||||
+
|
||||
+/* This provides us with the number of children in this node, in the case of a
|
||||
+ * leaf this will return 0 meaning none of the children are accessible.
|
||||
*/
|
||||
-static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr)
|
||||
+static inline int tnode_child_length(const struct tnode *tn)
|
||||
{
|
||||
- smp_wmb();
|
||||
- node->parent = (unsigned long)ptr | NODE_TYPE(node);
|
||||
+ return (1ul << tn->bits) & ~(1ul);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -215,7 +204,7 @@ static inline void node_set_parent(struc
|
||||
*/
|
||||
static inline struct rt_trie_node *tnode_get_child(const struct tnode *tn, unsigned int i)
|
||||
{
|
||||
- BUG_ON(i >= 1U << tn->bits);
|
||||
+ BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
return rtnl_dereference(tn->child[i]);
|
||||
}
|
||||
@@ -225,16 +214,11 @@ static inline struct rt_trie_node *tnode
|
||||
*/
|
||||
static inline struct rt_trie_node *tnode_get_child_rcu(const struct tnode *tn, unsigned int i)
|
||||
{
|
||||
- BUG_ON(i >= 1U << tn->bits);
|
||||
+ BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
return rcu_dereference_rtnl(tn->child[i]);
|
||||
}
|
||||
|
||||
-static inline int tnode_child_length(const struct tnode *tn)
|
||||
-{
|
||||
- return 1 << tn->bits;
|
||||
-}
|
||||
-
|
||||
static inline t_key mask_pfx(t_key k, unsigned int l)
|
||||
{
|
||||
return (l == 0) ? 0 : k >> (KEYLENGTH-l) << (KEYLENGTH-l);
|
||||
@@ -336,11 +320,6 @@ static inline int tkey_mismatch(t_key a,
|
||||
|
||||
*/
|
||||
|
||||
-static inline void check_tnode(const struct tnode *tn)
|
||||
-{
|
||||
- WARN_ON(tn && tn->pos+tn->bits > 32);
|
||||
-}
|
||||
-
|
||||
static const int halve_threshold = 25;
|
||||
static const int inflate_threshold = 50;
|
||||
static const int halve_threshold_root = 15;
|
||||
@@ -426,11 +405,20 @@ static void tnode_free_flush(void)
|
||||
}
|
||||
}
|
||||
|
||||
-static struct leaf *leaf_new(void)
|
||||
+static struct leaf *leaf_new(t_key key)
|
||||
{
|
||||
struct leaf *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL);
|
||||
if (l) {
|
||||
- l->parent = T_LEAF;
|
||||
+ l->parent = NULL;
|
||||
+ /* set key and pos to reflect full key value
|
||||
+ * any trailing zeros in the key should be ignored
|
||||
+ * as the nodes are searched
|
||||
+ */
|
||||
+ l->key = key;
|
||||
+ l->pos = KEYLENGTH;
|
||||
+ /* set bits to 0 indicating we are not a tnode */
|
||||
+ l->bits = 0;
|
||||
+
|
||||
INIT_HLIST_HEAD(&l->list);
|
||||
}
|
||||
return l;
|
||||
@@ -451,12 +439,16 @@ static struct tnode *tnode_new(t_key key
|
||||
{
|
||||
size_t sz = sizeof(struct tnode) + (sizeof(struct rt_trie_node *) << bits);
|
||||
struct tnode *tn = tnode_alloc(sz);
|
||||
+ unsigned int shift = pos + bits;
|
||||
+
|
||||
+ /* verify bits and pos their msb bits clear and values are valid */
|
||||
+ BUG_ON(!bits || (shift > KEYLENGTH));
|
||||
|
||||
if (tn) {
|
||||
- tn->parent = T_TNODE;
|
||||
+ tn->parent = NULL;
|
||||
tn->pos = pos;
|
||||
tn->bits = bits;
|
||||
- tn->key = key;
|
||||
+ tn->key = mask_pfx(key, pos);
|
||||
tn->full_children = 0;
|
||||
tn->empty_children = 1<<bits;
|
||||
}
|
||||
@@ -473,10 +465,7 @@ static struct tnode *tnode_new(t_key key
|
||||
|
||||
static inline int tnode_full(const struct tnode *tn, const struct rt_trie_node *n)
|
||||
{
|
||||
- if (n == NULL || IS_LEAF(n))
|
||||
- return 0;
|
||||
-
|
||||
- return ((struct tnode *) n)->pos == tn->pos + tn->bits;
|
||||
+ return n && IS_TNODE(n) && (n->pos == (tn->pos + tn->bits));
|
||||
}
|
||||
|
||||
static inline void put_child(struct tnode *tn, int i,
|
||||
@@ -514,8 +503,7 @@ static void tnode_put_child_reorg(struct
|
||||
else if (!wasfull && isfull)
|
||||
tn->full_children++;
|
||||
|
||||
- if (n)
|
||||
- node_set_parent(n, tn);
|
||||
+ node_set_parent(n, tn);
|
||||
|
||||
rcu_assign_pointer(tn->child[i], n);
|
||||
}
|
||||
@@ -523,7 +511,7 @@ static void tnode_put_child_reorg(struct
|
||||
#define MAX_WORK 10
|
||||
static struct rt_trie_node *resize(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
- int i;
|
||||
+ struct rt_trie_node *n = NULL;
|
||||
struct tnode *old_tn;
|
||||
int inflate_threshold_use;
|
||||
int halve_threshold_use;
|
||||
@@ -536,12 +524,11 @@ static struct rt_trie_node *resize(struc
|
||||
tn, inflate_threshold, halve_threshold);
|
||||
|
||||
/* No children */
|
||||
- if (tn->empty_children == tnode_child_length(tn)) {
|
||||
- tnode_free_safe(tn);
|
||||
- return NULL;
|
||||
- }
|
||||
+ if (tn->empty_children > (tnode_child_length(tn) - 1))
|
||||
+ goto no_children;
|
||||
+
|
||||
/* One child */
|
||||
- if (tn->empty_children == tnode_child_length(tn) - 1)
|
||||
+ if (tn->empty_children == (tnode_child_length(tn) - 1))
|
||||
goto one_child;
|
||||
/*
|
||||
* Double as long as the resulting node has a number of
|
||||
@@ -607,11 +594,9 @@ static struct rt_trie_node *resize(struc
|
||||
*
|
||||
*/
|
||||
|
||||
- check_tnode(tn);
|
||||
-
|
||||
/* Keep root node larger */
|
||||
|
||||
- if (!node_parent((struct rt_trie_node *)tn)) {
|
||||
+ if (!node_parent(tn)) {
|
||||
inflate_threshold_use = inflate_threshold_root;
|
||||
halve_threshold_use = halve_threshold_root;
|
||||
} else {
|
||||
@@ -637,8 +622,6 @@ static struct rt_trie_node *resize(struc
|
||||
}
|
||||
}
|
||||
|
||||
- check_tnode(tn);
|
||||
-
|
||||
/* Return if at least one inflate is run */
|
||||
if (max_work != MAX_WORK)
|
||||
return (struct rt_trie_node *) tn;
|
||||
@@ -666,21 +649,16 @@ static struct rt_trie_node *resize(struc
|
||||
|
||||
|
||||
/* Only one child remains */
|
||||
- if (tn->empty_children == tnode_child_length(tn) - 1) {
|
||||
+ if (tn->empty_children == (tnode_child_length(tn) - 1)) {
|
||||
+ unsigned long i;
|
||||
one_child:
|
||||
- for (i = 0; i < tnode_child_length(tn); i++) {
|
||||
- struct rt_trie_node *n;
|
||||
-
|
||||
- n = rtnl_dereference(tn->child[i]);
|
||||
- if (!n)
|
||||
- continue;
|
||||
-
|
||||
- /* compress one level */
|
||||
-
|
||||
- node_set_parent(n, NULL);
|
||||
- tnode_free_safe(tn);
|
||||
- return n;
|
||||
- }
|
||||
+ for (i = tnode_child_length(tn); !n && i;)
|
||||
+ n = tnode_get_child(tn, --i);
|
||||
+no_children:
|
||||
+ /* compress one level */
|
||||
+ node_set_parent(n, NULL);
|
||||
+ tnode_free_safe(tn);
|
||||
+ return n;
|
||||
}
|
||||
return (struct rt_trie_node *) tn;
|
||||
}
|
||||
@@ -760,8 +738,7 @@ static struct tnode *inflate(struct trie
|
||||
|
||||
/* A leaf or an internal node with skipped bits */
|
||||
|
||||
- if (IS_LEAF(node) || ((struct tnode *) node)->pos >
|
||||
- tn->pos + tn->bits - 1) {
|
||||
+ if (IS_LEAF(node) || (node->pos > (tn->pos + tn->bits - 1))) {
|
||||
put_child(tn,
|
||||
tkey_extract_bits(node->key, oldtnode->pos, oldtnode->bits + 1),
|
||||
node);
|
||||
@@ -958,11 +935,9 @@ fib_find_node(struct trie *t, u32 key)
|
||||
pos = 0;
|
||||
n = rcu_dereference_rtnl(t->trie);
|
||||
|
||||
- while (n != NULL && NODE_TYPE(n) == T_TNODE) {
|
||||
+ while (n && IS_TNODE(n)) {
|
||||
tn = (struct tnode *) n;
|
||||
|
||||
- check_tnode(tn);
|
||||
-
|
||||
if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
|
||||
pos = tn->pos + tn->bits;
|
||||
n = tnode_get_child_rcu(tn,
|
||||
@@ -988,7 +963,7 @@ static void trie_rebalance(struct trie *
|
||||
|
||||
key = tn->key;
|
||||
|
||||
- while (tn != NULL && (tp = node_parent((struct rt_trie_node *)tn)) != NULL) {
|
||||
+ while (tn != NULL && (tp = node_parent(tn)) != NULL) {
|
||||
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
||||
wasfull = tnode_full(tp, tnode_get_child(tp, cindex));
|
||||
tn = (struct tnode *)resize(t, tn);
|
||||
@@ -996,7 +971,7 @@ static void trie_rebalance(struct trie *
|
||||
tnode_put_child_reorg(tp, cindex,
|
||||
(struct rt_trie_node *)tn, wasfull);
|
||||
|
||||
- tp = node_parent((struct rt_trie_node *) tn);
|
||||
+ tp = node_parent(tn);
|
||||
if (!tp)
|
||||
rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);
|
||||
|
||||
@@ -1048,11 +1023,9 @@ static struct list_head *fib_insert_node
|
||||
* If it doesn't, we need to replace it with a T_TNODE.
|
||||
*/
|
||||
|
||||
- while (n != NULL && NODE_TYPE(n) == T_TNODE) {
|
||||
+ while (n && IS_TNODE(n)) {
|
||||
tn = (struct tnode *) n;
|
||||
|
||||
- check_tnode(tn);
|
||||
-
|
||||
if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
|
||||
tp = tn;
|
||||
pos = tn->pos + tn->bits;
|
||||
@@ -1087,12 +1060,11 @@ static struct list_head *fib_insert_node
|
||||
insert_leaf_info(&l->list, li);
|
||||
goto done;
|
||||
}
|
||||
- l = leaf_new();
|
||||
+ l = leaf_new(key);
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
- l->key = key;
|
||||
li = leaf_info_new(plen);
|
||||
|
||||
if (!li) {
|
||||
@@ -1569,7 +1541,7 @@ backtrace:
|
||||
if (chopped_off <= pn->bits) {
|
||||
cindex &= ~(1 << (chopped_off-1));
|
||||
} else {
|
||||
- struct tnode *parent = node_parent_rcu((struct rt_trie_node *) pn);
|
||||
+ struct tnode *parent = node_parent_rcu(pn);
|
||||
if (!parent)
|
||||
goto failed;
|
||||
|
||||
@@ -1597,7 +1569,7 @@ EXPORT_SYMBOL_GPL(fib_table_lookup);
|
||||
*/
|
||||
static void trie_leaf_remove(struct trie *t, struct leaf *l)
|
||||
{
|
||||
- struct tnode *tp = node_parent((struct rt_trie_node *) l);
|
||||
+ struct tnode *tp = node_parent(l);
|
||||
|
||||
pr_debug("entering trie_leaf_remove(%p)\n", l);
|
||||
|
||||
@@ -2374,7 +2346,7 @@ static int fib_trie_seq_show(struct seq_
|
||||
|
||||
if (IS_TNODE(n)) {
|
||||
struct tnode *tn = (struct tnode *) n;
|
||||
- __be32 prf = htonl(mask_pfx(tn->key, tn->pos));
|
||||
+ __be32 prf = htonl(tn->key);
|
||||
|
||||
seq_indent(seq, iter->depth-1);
|
||||
seq_printf(seq, " +-- %pI4/%d %d %d %d\n",
|
|
@ -1,209 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:55:41 -0800
|
||||
Subject: [PATCH] fib_trie: Merge tnode_free and leaf_free into node_free
|
||||
|
||||
Both the leaf and the tnode had an rcu_head in them, but they had them in
|
||||
slightly different places. Since we now have them in the same spot and
|
||||
know that any node with bits == 0 is a leaf and the rest are either vmalloc
|
||||
or kmalloc tnodes depending on the value of bits it makes it easy to combine
|
||||
the functions and reduce overhead.
|
||||
|
||||
In addition I have taken advantage of the rcu_head pointer to go ahead and
|
||||
put together a simple linked list instead of using the tnode pointer as
|
||||
this way we can merge either type of structure for freeing.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -95,15 +95,17 @@ struct tnode {
|
||||
unsigned char bits; /* 2log(KEYLENGTH) bits needed */
|
||||
unsigned char pos; /* 2log(KEYLENGTH) bits needed */
|
||||
struct tnode __rcu *parent;
|
||||
- union {
|
||||
- struct rcu_head rcu;
|
||||
- struct tnode *tnode_free;
|
||||
- };
|
||||
+ struct rcu_head rcu;
|
||||
+ /* everything above this comment must be the same as rt_trie_node */
|
||||
unsigned int full_children; /* KEYLENGTH bits needed */
|
||||
unsigned int empty_children; /* KEYLENGTH bits needed */
|
||||
struct rt_trie_node __rcu *child[0];
|
||||
};
|
||||
|
||||
+/* This struct represents the shared bits between tnode and leaf. If any
|
||||
+ * ordering is changed here is must also be updated in tnode and leaf as
|
||||
+ * well.
|
||||
+ */
|
||||
struct rt_trie_node {
|
||||
t_key key;
|
||||
unsigned char bits;
|
||||
@@ -118,6 +120,7 @@ struct leaf {
|
||||
unsigned char pos;
|
||||
struct tnode __rcu *parent;
|
||||
struct rcu_head rcu;
|
||||
+ /* everything above this comment must be the same as rt_trie_node */
|
||||
struct hlist_head list;
|
||||
};
|
||||
|
||||
@@ -163,7 +166,7 @@ static struct rt_trie_node *resize(struc
|
||||
static struct tnode *inflate(struct trie *t, struct tnode *tn);
|
||||
static struct tnode *halve(struct trie *t, struct tnode *tn);
|
||||
/* tnodes to free after resize(); protected by RTNL */
|
||||
-static struct tnode *tnode_free_head;
|
||||
+static struct callback_head *tnode_free_head;
|
||||
static size_t tnode_free_size;
|
||||
|
||||
/*
|
||||
@@ -336,17 +339,23 @@ static inline void alias_free_mem_rcu(st
|
||||
call_rcu(&fa->rcu, __alias_free_mem);
|
||||
}
|
||||
|
||||
-static void __leaf_free_rcu(struct rcu_head *head)
|
||||
-{
|
||||
- struct leaf *l = container_of(head, struct leaf, rcu);
|
||||
- kmem_cache_free(trie_leaf_kmem, l);
|
||||
-}
|
||||
+#define TNODE_KMALLOC_MAX \
|
||||
+ ilog2((PAGE_SIZE - sizeof(struct tnode)) / sizeof(struct rt_trie_node *))
|
||||
|
||||
-static inline void free_leaf(struct leaf *l)
|
||||
+static void __node_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
- call_rcu(&l->rcu, __leaf_free_rcu);
|
||||
+ struct rt_trie_node *n = container_of(head, struct rt_trie_node, rcu);
|
||||
+
|
||||
+ if (IS_LEAF(n))
|
||||
+ kmem_cache_free(trie_leaf_kmem, n);
|
||||
+ else if (n->bits <= TNODE_KMALLOC_MAX)
|
||||
+ kfree(n);
|
||||
+ else
|
||||
+ vfree(n);
|
||||
}
|
||||
|
||||
+#define node_free(n) call_rcu(&n->rcu, __node_free_rcu)
|
||||
+
|
||||
static inline void free_leaf_info(struct leaf_info *leaf)
|
||||
{
|
||||
kfree_rcu(leaf, rcu);
|
||||
@@ -360,43 +369,24 @@ static struct tnode *tnode_alloc(size_t
|
||||
return vzalloc(size);
|
||||
}
|
||||
|
||||
-static void __tnode_free_rcu(struct rcu_head *head)
|
||||
-{
|
||||
- struct tnode *tn = container_of(head, struct tnode, rcu);
|
||||
- size_t size = sizeof(struct tnode) +
|
||||
- (sizeof(struct rt_trie_node *) << tn->bits);
|
||||
-
|
||||
- if (size <= PAGE_SIZE)
|
||||
- kfree(tn);
|
||||
- else
|
||||
- vfree(tn);
|
||||
-}
|
||||
-
|
||||
-static inline void tnode_free(struct tnode *tn)
|
||||
-{
|
||||
- if (IS_LEAF(tn))
|
||||
- free_leaf((struct leaf *) tn);
|
||||
- else
|
||||
- call_rcu(&tn->rcu, __tnode_free_rcu);
|
||||
-}
|
||||
-
|
||||
static void tnode_free_safe(struct tnode *tn)
|
||||
{
|
||||
BUG_ON(IS_LEAF(tn));
|
||||
- tn->tnode_free = tnode_free_head;
|
||||
- tnode_free_head = tn;
|
||||
- tnode_free_size += sizeof(struct tnode) +
|
||||
- (sizeof(struct rt_trie_node *) << tn->bits);
|
||||
+ tn->rcu.next = tnode_free_head;
|
||||
+ tnode_free_head = &tn->rcu;
|
||||
}
|
||||
|
||||
static void tnode_free_flush(void)
|
||||
{
|
||||
- struct tnode *tn;
|
||||
+ struct callback_head *head;
|
||||
+
|
||||
+ while ((head = tnode_free_head)) {
|
||||
+ struct tnode *tn = container_of(head, struct tnode, rcu);
|
||||
+
|
||||
+ tnode_free_head = head->next;
|
||||
+ tnode_free_size += offsetof(struct tnode, child[1 << tn->bits]);
|
||||
|
||||
- while ((tn = tnode_free_head)) {
|
||||
- tnode_free_head = tn->tnode_free;
|
||||
- tn->tnode_free = NULL;
|
||||
- tnode_free(tn);
|
||||
+ node_free(tn);
|
||||
}
|
||||
|
||||
if (tnode_free_size >= PAGE_SIZE * sync_pages) {
|
||||
@@ -437,7 +427,7 @@ static struct leaf_info *leaf_info_new(i
|
||||
|
||||
static struct tnode *tnode_new(t_key key, int pos, int bits)
|
||||
{
|
||||
- size_t sz = sizeof(struct tnode) + (sizeof(struct rt_trie_node *) << bits);
|
||||
+ size_t sz = offsetof(struct tnode, child[1 << bits]);
|
||||
struct tnode *tn = tnode_alloc(sz);
|
||||
unsigned int shift = pos + bits;
|
||||
|
||||
@@ -666,15 +656,15 @@ no_children:
|
||||
|
||||
static void tnode_clean_free(struct tnode *tn)
|
||||
{
|
||||
+ struct rt_trie_node *tofree;
|
||||
int i;
|
||||
- struct tnode *tofree;
|
||||
|
||||
for (i = 0; i < tnode_child_length(tn); i++) {
|
||||
- tofree = (struct tnode *)rtnl_dereference(tn->child[i]);
|
||||
+ tofree = rtnl_dereference(tn->child[i]);
|
||||
if (tofree)
|
||||
- tnode_free(tofree);
|
||||
+ node_free(tofree);
|
||||
}
|
||||
- tnode_free(tn);
|
||||
+ node_free(tn);
|
||||
}
|
||||
|
||||
static struct tnode *inflate(struct trie *t, struct tnode *tn)
|
||||
@@ -717,7 +707,7 @@ static struct tnode *inflate(struct trie
|
||||
inode->bits - 1);
|
||||
|
||||
if (!right) {
|
||||
- tnode_free(left);
|
||||
+ node_free(left);
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
@@ -1068,7 +1058,7 @@ static struct list_head *fib_insert_node
|
||||
li = leaf_info_new(plen);
|
||||
|
||||
if (!li) {
|
||||
- free_leaf(l);
|
||||
+ node_free(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1100,7 +1090,7 @@ static struct list_head *fib_insert_node
|
||||
|
||||
if (!tn) {
|
||||
free_leaf_info(li);
|
||||
- free_leaf(l);
|
||||
+ node_free(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1580,7 +1570,7 @@ static void trie_leaf_remove(struct trie
|
||||
} else
|
||||
RCU_INIT_POINTER(t->trie, NULL);
|
||||
|
||||
- free_leaf(l);
|
||||
+ node_free(l);
|
||||
}
|
||||
|
||||
/*
|
|
@ -1,928 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:55:47 -0800
|
||||
Subject: [PATCH] fib_trie: Merge leaf into tnode
|
||||
|
||||
This change makes it so that leaf and tnode are the same struct. As a
|
||||
result there is no need for rt_trie_node anymore since everyting can be
|
||||
merged into tnode.
|
||||
|
||||
On 32b systems this results in the leaf being 4 bytes larger, however I
|
||||
don't know if that is really an issue as this and an eariler patch that
|
||||
added bits & pos have increased the size from 20 to 28. If I am not
|
||||
mistaken slub/slab allocate on power of 2 sizes so 20 was likely being
|
||||
rounded up to 32 anyway.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -96,32 +96,16 @@ struct tnode {
|
||||
unsigned char pos; /* 2log(KEYLENGTH) bits needed */
|
||||
struct tnode __rcu *parent;
|
||||
struct rcu_head rcu;
|
||||
- /* everything above this comment must be the same as rt_trie_node */
|
||||
- unsigned int full_children; /* KEYLENGTH bits needed */
|
||||
- unsigned int empty_children; /* KEYLENGTH bits needed */
|
||||
- struct rt_trie_node __rcu *child[0];
|
||||
-};
|
||||
-
|
||||
-/* This struct represents the shared bits between tnode and leaf. If any
|
||||
- * ordering is changed here is must also be updated in tnode and leaf as
|
||||
- * well.
|
||||
- */
|
||||
-struct rt_trie_node {
|
||||
- t_key key;
|
||||
- unsigned char bits;
|
||||
- unsigned char pos;
|
||||
- struct tnode __rcu *parent;
|
||||
- struct rcu_head rcu;
|
||||
-};
|
||||
-
|
||||
-struct leaf {
|
||||
- t_key key;
|
||||
- unsigned char bits;
|
||||
- unsigned char pos;
|
||||
- struct tnode __rcu *parent;
|
||||
- struct rcu_head rcu;
|
||||
- /* everything above this comment must be the same as rt_trie_node */
|
||||
- struct hlist_head list;
|
||||
+ union {
|
||||
+ /* The fields in this struct are valid if bits > 0 (TNODE) */
|
||||
+ struct {
|
||||
+ unsigned int full_children; /* KEYLENGTH bits needed */
|
||||
+ unsigned int empty_children; /* KEYLENGTH bits needed */
|
||||
+ struct tnode __rcu *child[0];
|
||||
+ };
|
||||
+ /* This list pointer if valid if bits == 0 (LEAF) */
|
||||
+ struct hlist_head list;
|
||||
+ };
|
||||
};
|
||||
|
||||
struct leaf_info {
|
||||
@@ -154,15 +138,15 @@ struct trie_stat {
|
||||
};
|
||||
|
||||
struct trie {
|
||||
- struct rt_trie_node __rcu *trie;
|
||||
+ struct tnode __rcu *trie;
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
struct trie_use_stats __percpu *stats;
|
||||
#endif
|
||||
};
|
||||
|
||||
-static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node *n,
|
||||
+static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n,
|
||||
int wasfull);
|
||||
-static struct rt_trie_node *resize(struct trie *t, struct tnode *tn);
|
||||
+static struct tnode *resize(struct trie *t, struct tnode *tn);
|
||||
static struct tnode *inflate(struct trie *t, struct tnode *tn);
|
||||
static struct tnode *halve(struct trie *t, struct tnode *tn);
|
||||
/* tnodes to free after resize(); protected by RTNL */
|
||||
@@ -186,10 +170,10 @@ static struct kmem_cache *trie_leaf_kmem
|
||||
#define node_parent_rcu(n) rcu_dereference_rtnl((n)->parent)
|
||||
|
||||
/* wrapper for rcu_assign_pointer */
|
||||
-static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr)
|
||||
+static inline void node_set_parent(struct tnode *n, struct tnode *tp)
|
||||
{
|
||||
- if (node)
|
||||
- rcu_assign_pointer(node->parent, ptr);
|
||||
+ if (n)
|
||||
+ rcu_assign_pointer(n->parent, tp);
|
||||
}
|
||||
|
||||
#define NODE_INIT_PARENT(n, p) RCU_INIT_POINTER((n)->parent, p)
|
||||
@@ -205,7 +189,7 @@ static inline int tnode_child_length(con
|
||||
/*
|
||||
* caller must hold RTNL
|
||||
*/
|
||||
-static inline struct rt_trie_node *tnode_get_child(const struct tnode *tn, unsigned int i)
|
||||
+static inline struct tnode *tnode_get_child(const struct tnode *tn, unsigned int i)
|
||||
{
|
||||
BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
@@ -215,7 +199,7 @@ static inline struct rt_trie_node *tnode
|
||||
/*
|
||||
* caller must hold RCU read lock or RTNL
|
||||
*/
|
||||
-static inline struct rt_trie_node *tnode_get_child_rcu(const struct tnode *tn, unsigned int i)
|
||||
+static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, unsigned int i)
|
||||
{
|
||||
BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
@@ -340,11 +324,11 @@ static inline void alias_free_mem_rcu(st
|
||||
}
|
||||
|
||||
#define TNODE_KMALLOC_MAX \
|
||||
- ilog2((PAGE_SIZE - sizeof(struct tnode)) / sizeof(struct rt_trie_node *))
|
||||
+ ilog2((PAGE_SIZE - sizeof(struct tnode)) / sizeof(struct tnode *))
|
||||
|
||||
static void __node_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
- struct rt_trie_node *n = container_of(head, struct rt_trie_node, rcu);
|
||||
+ struct tnode *n = container_of(head, struct tnode, rcu);
|
||||
|
||||
if (IS_LEAF(n))
|
||||
kmem_cache_free(trie_leaf_kmem, n);
|
||||
@@ -395,9 +379,9 @@ static void tnode_free_flush(void)
|
||||
}
|
||||
}
|
||||
|
||||
-static struct leaf *leaf_new(t_key key)
|
||||
+static struct tnode *leaf_new(t_key key)
|
||||
{
|
||||
- struct leaf *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL);
|
||||
+ struct tnode *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL);
|
||||
if (l) {
|
||||
l->parent = NULL;
|
||||
/* set key and pos to reflect full key value
|
||||
@@ -444,7 +428,7 @@ static struct tnode *tnode_new(t_key key
|
||||
}
|
||||
|
||||
pr_debug("AT %p s=%zu %zu\n", tn, sizeof(struct tnode),
|
||||
- sizeof(struct rt_trie_node *) << bits);
|
||||
+ sizeof(struct tnode *) << bits);
|
||||
return tn;
|
||||
}
|
||||
|
||||
@@ -453,13 +437,13 @@ static struct tnode *tnode_new(t_key key
|
||||
* and no bits are skipped. See discussion in dyntree paper p. 6
|
||||
*/
|
||||
|
||||
-static inline int tnode_full(const struct tnode *tn, const struct rt_trie_node *n)
|
||||
+static inline int tnode_full(const struct tnode *tn, const struct tnode *n)
|
||||
{
|
||||
return n && IS_TNODE(n) && (n->pos == (tn->pos + tn->bits));
|
||||
}
|
||||
|
||||
static inline void put_child(struct tnode *tn, int i,
|
||||
- struct rt_trie_node *n)
|
||||
+ struct tnode *n)
|
||||
{
|
||||
tnode_put_child_reorg(tn, i, n, -1);
|
||||
}
|
||||
@@ -469,10 +453,10 @@ static inline void put_child(struct tnod
|
||||
* Update the value of full_children and empty_children.
|
||||
*/
|
||||
|
||||
-static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node *n,
|
||||
+static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n,
|
||||
int wasfull)
|
||||
{
|
||||
- struct rt_trie_node *chi = rtnl_dereference(tn->child[i]);
|
||||
+ struct tnode *chi = rtnl_dereference(tn->child[i]);
|
||||
int isfull;
|
||||
|
||||
BUG_ON(i >= 1<<tn->bits);
|
||||
@@ -499,10 +483,9 @@ static void tnode_put_child_reorg(struct
|
||||
}
|
||||
|
||||
#define MAX_WORK 10
|
||||
-static struct rt_trie_node *resize(struct trie *t, struct tnode *tn)
|
||||
+static struct tnode *resize(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
- struct rt_trie_node *n = NULL;
|
||||
- struct tnode *old_tn;
|
||||
+ struct tnode *old_tn, *n = NULL;
|
||||
int inflate_threshold_use;
|
||||
int halve_threshold_use;
|
||||
int max_work;
|
||||
@@ -614,7 +597,7 @@ static struct rt_trie_node *resize(struc
|
||||
|
||||
/* Return if at least one inflate is run */
|
||||
if (max_work != MAX_WORK)
|
||||
- return (struct rt_trie_node *) tn;
|
||||
+ return tn;
|
||||
|
||||
/*
|
||||
* Halve as long as the number of empty children in this
|
||||
@@ -650,13 +633,13 @@ no_children:
|
||||
tnode_free_safe(tn);
|
||||
return n;
|
||||
}
|
||||
- return (struct rt_trie_node *) tn;
|
||||
+ return tn;
|
||||
}
|
||||
|
||||
|
||||
static void tnode_clean_free(struct tnode *tn)
|
||||
{
|
||||
- struct rt_trie_node *tofree;
|
||||
+ struct tnode *tofree;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tnode_child_length(tn); i++) {
|
||||
@@ -667,10 +650,10 @@ static void tnode_clean_free(struct tnod
|
||||
node_free(tn);
|
||||
}
|
||||
|
||||
-static struct tnode *inflate(struct trie *t, struct tnode *tn)
|
||||
+static struct tnode *inflate(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- struct tnode *oldtnode = tn;
|
||||
- int olen = tnode_child_length(tn);
|
||||
+ int olen = tnode_child_length(oldtnode);
|
||||
+ struct tnode *tn;
|
||||
int i;
|
||||
|
||||
pr_debug("In inflate\n");
|
||||
@@ -690,11 +673,8 @@ static struct tnode *inflate(struct trie
|
||||
for (i = 0; i < olen; i++) {
|
||||
struct tnode *inode;
|
||||
|
||||
- inode = (struct tnode *) tnode_get_child(oldtnode, i);
|
||||
- if (inode &&
|
||||
- IS_TNODE(inode) &&
|
||||
- inode->pos == oldtnode->pos + oldtnode->bits &&
|
||||
- inode->bits > 1) {
|
||||
+ inode = tnode_get_child(oldtnode, i);
|
||||
+ if (tnode_full(oldtnode, inode) && inode->bits > 1) {
|
||||
struct tnode *left, *right;
|
||||
t_key m = ~0U << (KEYLENGTH - 1) >> inode->pos;
|
||||
|
||||
@@ -711,33 +691,29 @@ static struct tnode *inflate(struct trie
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
- put_child(tn, 2*i, (struct rt_trie_node *) left);
|
||||
- put_child(tn, 2*i+1, (struct rt_trie_node *) right);
|
||||
+ put_child(tn, 2*i, left);
|
||||
+ put_child(tn, 2*i+1, right);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < olen; i++) {
|
||||
- struct tnode *inode;
|
||||
- struct rt_trie_node *node = tnode_get_child(oldtnode, i);
|
||||
+ struct tnode *inode = tnode_get_child(oldtnode, i);
|
||||
struct tnode *left, *right;
|
||||
int size, j;
|
||||
|
||||
/* An empty child */
|
||||
- if (node == NULL)
|
||||
+ if (inode == NULL)
|
||||
continue;
|
||||
|
||||
/* A leaf or an internal node with skipped bits */
|
||||
-
|
||||
- if (IS_LEAF(node) || (node->pos > (tn->pos + tn->bits - 1))) {
|
||||
+ if (!tnode_full(oldtnode, inode)) {
|
||||
put_child(tn,
|
||||
- tkey_extract_bits(node->key, oldtnode->pos, oldtnode->bits + 1),
|
||||
- node);
|
||||
+ tkey_extract_bits(inode->key, tn->pos, tn->bits),
|
||||
+ inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* An internal node with two children */
|
||||
- inode = (struct tnode *) node;
|
||||
-
|
||||
if (inode->bits == 1) {
|
||||
put_child(tn, 2*i, rtnl_dereference(inode->child[0]));
|
||||
put_child(tn, 2*i+1, rtnl_dereference(inode->child[1]));
|
||||
@@ -769,12 +745,12 @@ static struct tnode *inflate(struct trie
|
||||
* bit to zero.
|
||||
*/
|
||||
|
||||
- left = (struct tnode *) tnode_get_child(tn, 2*i);
|
||||
+ left = tnode_get_child(tn, 2*i);
|
||||
put_child(tn, 2*i, NULL);
|
||||
|
||||
BUG_ON(!left);
|
||||
|
||||
- right = (struct tnode *) tnode_get_child(tn, 2*i+1);
|
||||
+ right = tnode_get_child(tn, 2*i+1);
|
||||
put_child(tn, 2*i+1, NULL);
|
||||
|
||||
BUG_ON(!right);
|
||||
@@ -796,12 +772,11 @@ nomem:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
-static struct tnode *halve(struct trie *t, struct tnode *tn)
|
||||
+static struct tnode *halve(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- struct tnode *oldtnode = tn;
|
||||
- struct rt_trie_node *left, *right;
|
||||
+ int olen = tnode_child_length(oldtnode);
|
||||
+ struct tnode *tn, *left, *right;
|
||||
int i;
|
||||
- int olen = tnode_child_length(tn);
|
||||
|
||||
pr_debug("In halve\n");
|
||||
|
||||
@@ -830,7 +805,7 @@ static struct tnode *halve(struct trie *
|
||||
if (!newn)
|
||||
goto nomem;
|
||||
|
||||
- put_child(tn, i/2, (struct rt_trie_node *)newn);
|
||||
+ put_child(tn, i/2, newn);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -855,7 +830,7 @@ static struct tnode *halve(struct trie *
|
||||
}
|
||||
|
||||
/* Two nonempty children */
|
||||
- newBinNode = (struct tnode *) tnode_get_child(tn, i/2);
|
||||
+ newBinNode = tnode_get_child(tn, i/2);
|
||||
put_child(tn, i/2, NULL);
|
||||
put_child(newBinNode, 0, left);
|
||||
put_child(newBinNode, 1, right);
|
||||
@@ -871,7 +846,7 @@ nomem:
|
||||
/* readside must use rcu_read_lock currently dump routines
|
||||
via get_fa_head and dump */
|
||||
|
||||
-static struct leaf_info *find_leaf_info(struct leaf *l, int plen)
|
||||
+static struct leaf_info *find_leaf_info(struct tnode *l, int plen)
|
||||
{
|
||||
struct hlist_head *head = &l->list;
|
||||
struct leaf_info *li;
|
||||
@@ -883,7 +858,7 @@ static struct leaf_info *find_leaf_info(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
-static inline struct list_head *get_fa_head(struct leaf *l, int plen)
|
||||
+static inline struct list_head *get_fa_head(struct tnode *l, int plen)
|
||||
{
|
||||
struct leaf_info *li = find_leaf_info(l, plen);
|
||||
|
||||
@@ -915,32 +890,25 @@ static void insert_leaf_info(struct hlis
|
||||
|
||||
/* rcu_read_lock needs to be hold by caller from readside */
|
||||
|
||||
-static struct leaf *
|
||||
-fib_find_node(struct trie *t, u32 key)
|
||||
+static struct tnode *fib_find_node(struct trie *t, u32 key)
|
||||
{
|
||||
- int pos;
|
||||
- struct tnode *tn;
|
||||
- struct rt_trie_node *n;
|
||||
-
|
||||
- pos = 0;
|
||||
- n = rcu_dereference_rtnl(t->trie);
|
||||
+ struct tnode *n = rcu_dereference_rtnl(t->trie);
|
||||
+ int pos = 0;
|
||||
|
||||
while (n && IS_TNODE(n)) {
|
||||
- tn = (struct tnode *) n;
|
||||
-
|
||||
- if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
|
||||
- pos = tn->pos + tn->bits;
|
||||
- n = tnode_get_child_rcu(tn,
|
||||
+ if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) {
|
||||
+ pos = n->pos + n->bits;
|
||||
+ n = tnode_get_child_rcu(n,
|
||||
tkey_extract_bits(key,
|
||||
- tn->pos,
|
||||
- tn->bits));
|
||||
+ n->pos,
|
||||
+ n->bits));
|
||||
} else
|
||||
break;
|
||||
}
|
||||
/* Case we have found a leaf. Compare prefixes */
|
||||
|
||||
if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key))
|
||||
- return (struct leaf *)n;
|
||||
+ return n;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -956,14 +924,13 @@ static void trie_rebalance(struct trie *
|
||||
while (tn != NULL && (tp = node_parent(tn)) != NULL) {
|
||||
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
||||
wasfull = tnode_full(tp, tnode_get_child(tp, cindex));
|
||||
- tn = (struct tnode *)resize(t, tn);
|
||||
+ tn = resize(t, tn);
|
||||
|
||||
- tnode_put_child_reorg(tp, cindex,
|
||||
- (struct rt_trie_node *)tn, wasfull);
|
||||
+ tnode_put_child_reorg(tp, cindex, tn, wasfull);
|
||||
|
||||
tp = node_parent(tn);
|
||||
if (!tp)
|
||||
- rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);
|
||||
+ rcu_assign_pointer(t->trie, tn);
|
||||
|
||||
tnode_free_flush();
|
||||
if (!tp)
|
||||
@@ -973,9 +940,9 @@ static void trie_rebalance(struct trie *
|
||||
|
||||
/* Handle last (top) tnode */
|
||||
if (IS_TNODE(tn))
|
||||
- tn = (struct tnode *)resize(t, tn);
|
||||
+ tn = resize(t, tn);
|
||||
|
||||
- rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);
|
||||
+ rcu_assign_pointer(t->trie, tn);
|
||||
tnode_free_flush();
|
||||
}
|
||||
|
||||
@@ -985,8 +952,8 @@ static struct list_head *fib_insert_node
|
||||
{
|
||||
int pos, newpos;
|
||||
struct tnode *tp = NULL, *tn = NULL;
|
||||
- struct rt_trie_node *n;
|
||||
- struct leaf *l;
|
||||
+ struct tnode *n;
|
||||
+ struct tnode *l;
|
||||
int missbit;
|
||||
struct list_head *fa_head = NULL;
|
||||
struct leaf_info *li;
|
||||
@@ -1014,17 +981,15 @@ static struct list_head *fib_insert_node
|
||||
*/
|
||||
|
||||
while (n && IS_TNODE(n)) {
|
||||
- tn = (struct tnode *) n;
|
||||
-
|
||||
- if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
|
||||
- tp = tn;
|
||||
- pos = tn->pos + tn->bits;
|
||||
- n = tnode_get_child(tn,
|
||||
+ if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) {
|
||||
+ tp = n;
|
||||
+ pos = n->pos + n->bits;
|
||||
+ n = tnode_get_child(n,
|
||||
tkey_extract_bits(key,
|
||||
- tn->pos,
|
||||
- tn->bits));
|
||||
+ n->pos,
|
||||
+ n->bits));
|
||||
|
||||
- BUG_ON(n && node_parent(n) != tn);
|
||||
+ BUG_ON(n && node_parent(n) != tp);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
@@ -1040,14 +1005,13 @@ static struct list_head *fib_insert_node
|
||||
/* Case 1: n is a leaf. Compare prefixes */
|
||||
|
||||
if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) {
|
||||
- l = (struct leaf *) n;
|
||||
li = leaf_info_new(plen);
|
||||
|
||||
if (!li)
|
||||
return NULL;
|
||||
|
||||
fa_head = &li->falh;
|
||||
- insert_leaf_info(&l->list, li);
|
||||
+ insert_leaf_info(&n->list, li);
|
||||
goto done;
|
||||
}
|
||||
l = leaf_new(key);
|
||||
@@ -1068,10 +1032,10 @@ static struct list_head *fib_insert_node
|
||||
if (t->trie && n == NULL) {
|
||||
/* Case 2: n is NULL, and will just insert a new leaf */
|
||||
|
||||
- node_set_parent((struct rt_trie_node *)l, tp);
|
||||
+ node_set_parent(l, tp);
|
||||
|
||||
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
||||
- put_child(tp, cindex, (struct rt_trie_node *)l);
|
||||
+ put_child(tp, cindex, l);
|
||||
} else {
|
||||
/* Case 3: n is a LEAF or a TNODE and the key doesn't match. */
|
||||
/*
|
||||
@@ -1094,17 +1058,17 @@ static struct list_head *fib_insert_node
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- node_set_parent((struct rt_trie_node *)tn, tp);
|
||||
+ node_set_parent(tn, tp);
|
||||
|
||||
missbit = tkey_extract_bits(key, newpos, 1);
|
||||
- put_child(tn, missbit, (struct rt_trie_node *)l);
|
||||
+ put_child(tn, missbit, l);
|
||||
put_child(tn, 1-missbit, n);
|
||||
|
||||
if (tp) {
|
||||
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
||||
- put_child(tp, cindex, (struct rt_trie_node *)tn);
|
||||
+ put_child(tp, cindex, tn);
|
||||
} else {
|
||||
- rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);
|
||||
+ rcu_assign_pointer(t->trie, tn);
|
||||
}
|
||||
|
||||
tp = tn;
|
||||
@@ -1134,7 +1098,7 @@ int fib_table_insert(struct fib_table *t
|
||||
u8 tos = cfg->fc_tos;
|
||||
u32 key, mask;
|
||||
int err;
|
||||
- struct leaf *l;
|
||||
+ struct tnode *l;
|
||||
|
||||
if (plen > 32)
|
||||
return -EINVAL;
|
||||
@@ -1292,7 +1256,7 @@ err:
|
||||
}
|
||||
|
||||
/* should be called with rcu_read_lock */
|
||||
-static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
|
||||
+static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l,
|
||||
t_key key, const struct flowi4 *flp,
|
||||
struct fib_result *res, int fib_flags)
|
||||
{
|
||||
@@ -1365,7 +1329,7 @@ int fib_table_lookup(struct fib_table *t
|
||||
struct trie_use_stats __percpu *stats = t->stats;
|
||||
#endif
|
||||
int ret;
|
||||
- struct rt_trie_node *n;
|
||||
+ struct tnode *n;
|
||||
struct tnode *pn;
|
||||
unsigned int pos, bits;
|
||||
t_key key = ntohl(flp->daddr);
|
||||
@@ -1387,11 +1351,11 @@ int fib_table_lookup(struct fib_table *t
|
||||
|
||||
/* Just a leaf? */
|
||||
if (IS_LEAF(n)) {
|
||||
- ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags);
|
||||
+ ret = check_leaf(tb, t, n, key, flp, res, fib_flags);
|
||||
goto found;
|
||||
}
|
||||
|
||||
- pn = (struct tnode *) n;
|
||||
+ pn = n;
|
||||
chopped_off = 0;
|
||||
|
||||
while (pn) {
|
||||
@@ -1412,13 +1376,13 @@ int fib_table_lookup(struct fib_table *t
|
||||
}
|
||||
|
||||
if (IS_LEAF(n)) {
|
||||
- ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags);
|
||||
+ ret = check_leaf(tb, t, n, key, flp, res, fib_flags);
|
||||
if (ret > 0)
|
||||
goto backtrace;
|
||||
goto found;
|
||||
}
|
||||
|
||||
- cn = (struct tnode *)n;
|
||||
+ cn = n;
|
||||
|
||||
/*
|
||||
* It's a tnode, and we can do some extra checks here if we
|
||||
@@ -1506,7 +1470,7 @@ int fib_table_lookup(struct fib_table *t
|
||||
current_prefix_length = mp;
|
||||
}
|
||||
|
||||
- pn = (struct tnode *)n; /* Descend */
|
||||
+ pn = n; /* Descend */
|
||||
chopped_off = 0;
|
||||
continue;
|
||||
|
||||
@@ -1557,7 +1521,7 @@ EXPORT_SYMBOL_GPL(fib_table_lookup);
|
||||
/*
|
||||
* Remove the leaf and return parent.
|
||||
*/
|
||||
-static void trie_leaf_remove(struct trie *t, struct leaf *l)
|
||||
+static void trie_leaf_remove(struct trie *t, struct tnode *l)
|
||||
{
|
||||
struct tnode *tp = node_parent(l);
|
||||
|
||||
@@ -1584,7 +1548,7 @@ int fib_table_delete(struct fib_table *t
|
||||
u8 tos = cfg->fc_tos;
|
||||
struct fib_alias *fa, *fa_to_delete;
|
||||
struct list_head *fa_head;
|
||||
- struct leaf *l;
|
||||
+ struct tnode *l;
|
||||
struct leaf_info *li;
|
||||
|
||||
if (plen > 32)
|
||||
@@ -1682,7 +1646,7 @@ static int trie_flush_list(struct list_h
|
||||
return found;
|
||||
}
|
||||
|
||||
-static int trie_flush_leaf(struct leaf *l)
|
||||
+static int trie_flush_leaf(struct tnode *l)
|
||||
{
|
||||
int found = 0;
|
||||
struct hlist_head *lih = &l->list;
|
||||
@@ -1704,7 +1668,7 @@ static int trie_flush_leaf(struct leaf *
|
||||
* Scan for the next right leaf starting at node p->child[idx]
|
||||
* Since we have back pointer, no recursion necessary.
|
||||
*/
|
||||
-static struct leaf *leaf_walk_rcu(struct tnode *p, struct rt_trie_node *c)
|
||||
+static struct tnode *leaf_walk_rcu(struct tnode *p, struct tnode *c)
|
||||
{
|
||||
do {
|
||||
t_key idx;
|
||||
@@ -1720,47 +1684,46 @@ static struct leaf *leaf_walk_rcu(struct
|
||||
continue;
|
||||
|
||||
if (IS_LEAF(c))
|
||||
- return (struct leaf *) c;
|
||||
+ return c;
|
||||
|
||||
/* Rescan start scanning in new node */
|
||||
- p = (struct tnode *) c;
|
||||
+ p = c;
|
||||
idx = 0;
|
||||
}
|
||||
|
||||
/* Node empty, walk back up to parent */
|
||||
- c = (struct rt_trie_node *) p;
|
||||
+ c = p;
|
||||
} while ((p = node_parent_rcu(c)) != NULL);
|
||||
|
||||
return NULL; /* Root of trie */
|
||||
}
|
||||
|
||||
-static struct leaf *trie_firstleaf(struct trie *t)
|
||||
+static struct tnode *trie_firstleaf(struct trie *t)
|
||||
{
|
||||
- struct tnode *n = (struct tnode *)rcu_dereference_rtnl(t->trie);
|
||||
+ struct tnode *n = rcu_dereference_rtnl(t->trie);
|
||||
|
||||
if (!n)
|
||||
return NULL;
|
||||
|
||||
if (IS_LEAF(n)) /* trie is just a leaf */
|
||||
- return (struct leaf *) n;
|
||||
+ return n;
|
||||
|
||||
return leaf_walk_rcu(n, NULL);
|
||||
}
|
||||
|
||||
-static struct leaf *trie_nextleaf(struct leaf *l)
|
||||
+static struct tnode *trie_nextleaf(struct tnode *l)
|
||||
{
|
||||
- struct rt_trie_node *c = (struct rt_trie_node *) l;
|
||||
- struct tnode *p = node_parent_rcu(c);
|
||||
+ struct tnode *p = node_parent_rcu(l);
|
||||
|
||||
if (!p)
|
||||
return NULL; /* trie with just one leaf */
|
||||
|
||||
- return leaf_walk_rcu(p, c);
|
||||
+ return leaf_walk_rcu(p, l);
|
||||
}
|
||||
|
||||
-static struct leaf *trie_leafindex(struct trie *t, int index)
|
||||
+static struct tnode *trie_leafindex(struct trie *t, int index)
|
||||
{
|
||||
- struct leaf *l = trie_firstleaf(t);
|
||||
+ struct tnode *l = trie_firstleaf(t);
|
||||
|
||||
while (l && index-- > 0)
|
||||
l = trie_nextleaf(l);
|
||||
@@ -1775,7 +1738,7 @@ static struct leaf *trie_leafindex(struc
|
||||
int fib_table_flush(struct fib_table *tb)
|
||||
{
|
||||
struct trie *t = (struct trie *) tb->tb_data;
|
||||
- struct leaf *l, *ll = NULL;
|
||||
+ struct tnode *l, *ll = NULL;
|
||||
int found = 0;
|
||||
|
||||
for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) {
|
||||
@@ -1840,7 +1803,7 @@ static int fn_trie_dump_fa(t_key key, in
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
-static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
|
||||
+static int fn_trie_dump_leaf(struct tnode *l, struct fib_table *tb,
|
||||
struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct leaf_info *li;
|
||||
@@ -1876,7 +1839,7 @@ static int fn_trie_dump_leaf(struct leaf
|
||||
int fib_table_dump(struct fib_table *tb, struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
- struct leaf *l;
|
||||
+ struct tnode *l;
|
||||
struct trie *t = (struct trie *) tb->tb_data;
|
||||
t_key key = cb->args[2];
|
||||
int count = cb->args[3];
|
||||
@@ -1922,7 +1885,7 @@ void __init fib_trie_init(void)
|
||||
0, SLAB_PANIC, NULL);
|
||||
|
||||
trie_leaf_kmem = kmem_cache_create("ip_fib_trie",
|
||||
- max(sizeof(struct leaf),
|
||||
+ max(sizeof(struct tnode),
|
||||
sizeof(struct leaf_info)),
|
||||
0, SLAB_PANIC, NULL);
|
||||
}
|
||||
@@ -1965,7 +1928,7 @@ struct fib_trie_iter {
|
||||
unsigned int depth;
|
||||
};
|
||||
|
||||
-static struct rt_trie_node *fib_trie_get_next(struct fib_trie_iter *iter)
|
||||
+static struct tnode *fib_trie_get_next(struct fib_trie_iter *iter)
|
||||
{
|
||||
struct tnode *tn = iter->tnode;
|
||||
unsigned int cindex = iter->index;
|
||||
@@ -1979,7 +1942,7 @@ static struct rt_trie_node *fib_trie_get
|
||||
iter->tnode, iter->index, iter->depth);
|
||||
rescan:
|
||||
while (cindex < (1<<tn->bits)) {
|
||||
- struct rt_trie_node *n = tnode_get_child_rcu(tn, cindex);
|
||||
+ struct tnode *n = tnode_get_child_rcu(tn, cindex);
|
||||
|
||||
if (n) {
|
||||
if (IS_LEAF(n)) {
|
||||
@@ -1987,7 +1950,7 @@ rescan:
|
||||
iter->index = cindex + 1;
|
||||
} else {
|
||||
/* push down one level */
|
||||
- iter->tnode = (struct tnode *) n;
|
||||
+ iter->tnode = n;
|
||||
iter->index = 0;
|
||||
++iter->depth;
|
||||
}
|
||||
@@ -1998,7 +1961,7 @@ rescan:
|
||||
}
|
||||
|
||||
/* Current node exhausted, pop back up */
|
||||
- p = node_parent_rcu((struct rt_trie_node *)tn);
|
||||
+ p = node_parent_rcu(tn);
|
||||
if (p) {
|
||||
cindex = tkey_extract_bits(tn->key, p->pos, p->bits)+1;
|
||||
tn = p;
|
||||
@@ -2010,10 +1973,10 @@ rescan:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
-static struct rt_trie_node *fib_trie_get_first(struct fib_trie_iter *iter,
|
||||
+static struct tnode *fib_trie_get_first(struct fib_trie_iter *iter,
|
||||
struct trie *t)
|
||||
{
|
||||
- struct rt_trie_node *n;
|
||||
+ struct tnode *n;
|
||||
|
||||
if (!t)
|
||||
return NULL;
|
||||
@@ -2023,7 +1986,7 @@ static struct rt_trie_node *fib_trie_get
|
||||
return NULL;
|
||||
|
||||
if (IS_TNODE(n)) {
|
||||
- iter->tnode = (struct tnode *) n;
|
||||
+ iter->tnode = n;
|
||||
iter->index = 0;
|
||||
iter->depth = 1;
|
||||
} else {
|
||||
@@ -2037,7 +2000,7 @@ static struct rt_trie_node *fib_trie_get
|
||||
|
||||
static void trie_collect_stats(struct trie *t, struct trie_stat *s)
|
||||
{
|
||||
- struct rt_trie_node *n;
|
||||
+ struct tnode *n;
|
||||
struct fib_trie_iter iter;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
@@ -2045,7 +2008,6 @@ static void trie_collect_stats(struct tr
|
||||
rcu_read_lock();
|
||||
for (n = fib_trie_get_first(&iter, t); n; n = fib_trie_get_next(&iter)) {
|
||||
if (IS_LEAF(n)) {
|
||||
- struct leaf *l = (struct leaf *)n;
|
||||
struct leaf_info *li;
|
||||
|
||||
s->leaves++;
|
||||
@@ -2053,18 +2015,17 @@ static void trie_collect_stats(struct tr
|
||||
if (iter.depth > s->maxdepth)
|
||||
s->maxdepth = iter.depth;
|
||||
|
||||
- hlist_for_each_entry_rcu(li, &l->list, hlist)
|
||||
+ hlist_for_each_entry_rcu(li, &n->list, hlist)
|
||||
++s->prefixes;
|
||||
} else {
|
||||
- const struct tnode *tn = (const struct tnode *) n;
|
||||
int i;
|
||||
|
||||
s->tnodes++;
|
||||
- if (tn->bits < MAX_STAT_DEPTH)
|
||||
- s->nodesizes[tn->bits]++;
|
||||
+ if (n->bits < MAX_STAT_DEPTH)
|
||||
+ s->nodesizes[n->bits]++;
|
||||
|
||||
- for (i = 0; i < (1<<tn->bits); i++)
|
||||
- if (!tn->child[i])
|
||||
+ for (i = 0; i < tnode_child_length(n); i++)
|
||||
+ if (!rcu_access_pointer(n->child[i]))
|
||||
s->nullpointers++;
|
||||
}
|
||||
}
|
||||
@@ -2088,7 +2049,7 @@ static void trie_show_stats(struct seq_f
|
||||
seq_printf(seq, "\tMax depth: %u\n", stat->maxdepth);
|
||||
|
||||
seq_printf(seq, "\tLeaves: %u\n", stat->leaves);
|
||||
- bytes = sizeof(struct leaf) * stat->leaves;
|
||||
+ bytes = sizeof(struct tnode) * stat->leaves;
|
||||
|
||||
seq_printf(seq, "\tPrefixes: %u\n", stat->prefixes);
|
||||
bytes += sizeof(struct leaf_info) * stat->prefixes;
|
||||
@@ -2109,7 +2070,7 @@ static void trie_show_stats(struct seq_f
|
||||
seq_putc(seq, '\n');
|
||||
seq_printf(seq, "\tPointers: %u\n", pointers);
|
||||
|
||||
- bytes += sizeof(struct rt_trie_node *) * pointers;
|
||||
+ bytes += sizeof(struct tnode *) * pointers;
|
||||
seq_printf(seq, "Null ptrs: %u\n", stat->nullpointers);
|
||||
seq_printf(seq, "Total size: %u kB\n", (bytes + 1023) / 1024);
|
||||
}
|
||||
@@ -2163,7 +2124,7 @@ static int fib_triestat_seq_show(struct
|
||||
seq_printf(seq,
|
||||
"Basic info: size of leaf:"
|
||||
" %Zd bytes, size of tnode: %Zd bytes.\n",
|
||||
- sizeof(struct leaf), sizeof(struct tnode));
|
||||
+ sizeof(struct tnode), sizeof(struct tnode));
|
||||
|
||||
for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
|
||||
struct hlist_head *head = &net->ipv4.fib_table_hash[h];
|
||||
@@ -2202,7 +2163,7 @@ static const struct file_operations fib_
|
||||
.release = single_release_net,
|
||||
};
|
||||
|
||||
-static struct rt_trie_node *fib_trie_get_idx(struct seq_file *seq, loff_t pos)
|
||||
+static struct tnode *fib_trie_get_idx(struct seq_file *seq, loff_t pos)
|
||||
{
|
||||
struct fib_trie_iter *iter = seq->private;
|
||||
struct net *net = seq_file_net(seq);
|
||||
@@ -2214,7 +2175,7 @@ static struct rt_trie_node *fib_trie_get
|
||||
struct fib_table *tb;
|
||||
|
||||
hlist_for_each_entry_rcu(tb, head, tb_hlist) {
|
||||
- struct rt_trie_node *n;
|
||||
+ struct tnode *n;
|
||||
|
||||
for (n = fib_trie_get_first(iter,
|
||||
(struct trie *) tb->tb_data);
|
||||
@@ -2243,7 +2204,7 @@ static void *fib_trie_seq_next(struct se
|
||||
struct fib_table *tb = iter->tb;
|
||||
struct hlist_node *tb_node;
|
||||
unsigned int h;
|
||||
- struct rt_trie_node *n;
|
||||
+ struct tnode *n;
|
||||
|
||||
++*pos;
|
||||
/* next node in same table */
|
||||
@@ -2329,29 +2290,26 @@ static inline const char *rtn_type(char
|
||||
static int fib_trie_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
const struct fib_trie_iter *iter = seq->private;
|
||||
- struct rt_trie_node *n = v;
|
||||
+ struct tnode *n = v;
|
||||
|
||||
if (!node_parent_rcu(n))
|
||||
fib_table_print(seq, iter->tb);
|
||||
|
||||
if (IS_TNODE(n)) {
|
||||
- struct tnode *tn = (struct tnode *) n;
|
||||
- __be32 prf = htonl(tn->key);
|
||||
+ __be32 prf = htonl(n->key);
|
||||
|
||||
- seq_indent(seq, iter->depth-1);
|
||||
+ seq_indent(seq, iter->depth - 1);
|
||||
seq_printf(seq, " +-- %pI4/%d %d %d %d\n",
|
||||
- &prf, tn->pos, tn->bits, tn->full_children,
|
||||
- tn->empty_children);
|
||||
-
|
||||
+ &prf, n->pos, n->bits, n->full_children,
|
||||
+ n->empty_children);
|
||||
} else {
|
||||
- struct leaf *l = (struct leaf *) n;
|
||||
struct leaf_info *li;
|
||||
- __be32 val = htonl(l->key);
|
||||
+ __be32 val = htonl(n->key);
|
||||
|
||||
seq_indent(seq, iter->depth);
|
||||
seq_printf(seq, " |-- %pI4\n", &val);
|
||||
|
||||
- hlist_for_each_entry_rcu(li, &l->list, hlist) {
|
||||
+ hlist_for_each_entry_rcu(li, &n->list, hlist) {
|
||||
struct fib_alias *fa;
|
||||
|
||||
list_for_each_entry_rcu(fa, &li->falh, fa_list) {
|
||||
@@ -2401,9 +2359,9 @@ struct fib_route_iter {
|
||||
t_key key;
|
||||
};
|
||||
|
||||
-static struct leaf *fib_route_get_idx(struct fib_route_iter *iter, loff_t pos)
|
||||
+static struct tnode *fib_route_get_idx(struct fib_route_iter *iter, loff_t pos)
|
||||
{
|
||||
- struct leaf *l = NULL;
|
||||
+ struct tnode *l = NULL;
|
||||
struct trie *t = iter->main_trie;
|
||||
|
||||
/* use cache location of last found key */
|
||||
@@ -2448,7 +2406,7 @@ static void *fib_route_seq_start(struct
|
||||
static void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
struct fib_route_iter *iter = seq->private;
|
||||
- struct leaf *l = v;
|
||||
+ struct tnode *l = v;
|
||||
|
||||
++*pos;
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
@@ -2494,7 +2452,7 @@ static unsigned int fib_flag_trans(int t
|
||||
*/
|
||||
static int fib_route_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
- struct leaf *l = v;
|
||||
+ struct tnode *l = v;
|
||||
struct leaf_info *li;
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
|
@ -1,343 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:55:54 -0800
|
||||
Subject: [PATCH] fib_trie: Optimize fib_table_lookup to avoid wasting
|
||||
time on loops/variables
|
||||
|
||||
This patch is meant to reduce the complexity of fib_table_lookup by reducing
|
||||
the number of variables to the bare minimum while still keeping the same if
|
||||
not improved functionality versus the original.
|
||||
|
||||
Most of this change was started off by the desire to rid the function of
|
||||
chopped_off and current_prefix_length as they actually added very little to
|
||||
the function since they only applied when computing the cindex. I was able
|
||||
to replace them mostly with just a check for the prefix match. As long as
|
||||
the prefix between the key and the node being tested was the same we know
|
||||
we can search the tnode fully versus just testing cindex 0.
|
||||
|
||||
The second portion of the change ended up being a massive reordering.
|
||||
Originally the calls to check_leaf were up near the start of the loop, and
|
||||
the backtracing and descending into lower levels of tnodes was later. This
|
||||
didn't make much sense as the structure of the tree means the leaves are
|
||||
always the last thing to be tested. As such I reordered things so that we
|
||||
instead have a loop that will delve into the tree and only exit when we
|
||||
have either found a leaf or we have exhausted the tree. The advantage of
|
||||
rearranging things like this is that we can fully inline check_leaf since
|
||||
there is now only one reference to it in the function.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -90,6 +90,9 @@ typedef unsigned int t_key;
|
||||
#define IS_TNODE(n) ((n)->bits)
|
||||
#define IS_LEAF(n) (!(n)->bits)
|
||||
|
||||
+#define get_shift(_kv) (KEYLENGTH - (_kv)->pos - (_kv)->bits)
|
||||
+#define get_index(_key, _kv) (((_key) ^ (_kv)->key) >> get_shift(_kv))
|
||||
+
|
||||
struct tnode {
|
||||
t_key key;
|
||||
unsigned char bits; /* 2log(KEYLENGTH) bits needed */
|
||||
@@ -1281,7 +1284,7 @@ static int check_leaf(struct fib_table *
|
||||
continue;
|
||||
fib_alias_accessed(fa);
|
||||
err = fib_props[fa->fa_type].error;
|
||||
- if (err) {
|
||||
+ if (unlikely(err < 0)) {
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
this_cpu_inc(t->stats->semantic_match_passed);
|
||||
#endif
|
||||
@@ -1303,7 +1306,7 @@ static int check_leaf(struct fib_table *
|
||||
res->prefixlen = li->plen;
|
||||
res->nh_sel = nhsel;
|
||||
res->type = fa->fa_type;
|
||||
- res->scope = fa->fa_info->fib_scope;
|
||||
+ res->scope = fi->fib_scope;
|
||||
res->fi = fi;
|
||||
res->table = tb;
|
||||
res->fa_head = &li->falh;
|
||||
@@ -1321,23 +1324,24 @@ static int check_leaf(struct fib_table *
|
||||
return 1;
|
||||
}
|
||||
|
||||
+static inline t_key prefix_mismatch(t_key key, struct tnode *n)
|
||||
+{
|
||||
+ t_key prefix = n->key;
|
||||
+
|
||||
+ return (key ^ prefix) & (prefix | -prefix);
|
||||
+}
|
||||
+
|
||||
int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
|
||||
struct fib_result *res, int fib_flags)
|
||||
{
|
||||
- struct trie *t = (struct trie *) tb->tb_data;
|
||||
+ struct trie *t = (struct trie *)tb->tb_data;
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
struct trie_use_stats __percpu *stats = t->stats;
|
||||
#endif
|
||||
- int ret;
|
||||
- struct tnode *n;
|
||||
- struct tnode *pn;
|
||||
- unsigned int pos, bits;
|
||||
- t_key key = ntohl(flp->daddr);
|
||||
- unsigned int chopped_off;
|
||||
- t_key cindex = 0;
|
||||
- unsigned int current_prefix_length = KEYLENGTH;
|
||||
- struct tnode *cn;
|
||||
- t_key pref_mismatch;
|
||||
+ const t_key key = ntohl(flp->daddr);
|
||||
+ struct tnode *n, *pn;
|
||||
+ t_key cindex;
|
||||
+ int ret = 1;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
@@ -1349,170 +1353,102 @@ int fib_table_lookup(struct fib_table *t
|
||||
this_cpu_inc(stats->gets);
|
||||
#endif
|
||||
|
||||
- /* Just a leaf? */
|
||||
- if (IS_LEAF(n)) {
|
||||
- ret = check_leaf(tb, t, n, key, flp, res, fib_flags);
|
||||
- goto found;
|
||||
- }
|
||||
-
|
||||
pn = n;
|
||||
- chopped_off = 0;
|
||||
-
|
||||
- while (pn) {
|
||||
- pos = pn->pos;
|
||||
- bits = pn->bits;
|
||||
+ cindex = 0;
|
||||
|
||||
- if (!chopped_off)
|
||||
- cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length),
|
||||
- pos, bits);
|
||||
-
|
||||
- n = tnode_get_child_rcu(pn, cindex);
|
||||
-
|
||||
- if (n == NULL) {
|
||||
-#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- this_cpu_inc(stats->null_node_hit);
|
||||
-#endif
|
||||
- goto backtrace;
|
||||
- }
|
||||
+ /* Step 1: Travel to the longest prefix match in the trie */
|
||||
+ for (;;) {
|
||||
+ unsigned long index = get_index(key, n);
|
||||
+
|
||||
+ /* This bit of code is a bit tricky but it combines multiple
|
||||
+ * checks into a single check. The prefix consists of the
|
||||
+ * prefix plus zeros for the "bits" in the prefix. The index
|
||||
+ * is the difference between the key and this value. From
|
||||
+ * this we can actually derive several pieces of data.
|
||||
+ * if !(index >> bits)
|
||||
+ * we know the value is child index
|
||||
+ * else
|
||||
+ * we have a mismatch in skip bits and failed
|
||||
+ */
|
||||
+ if (index >> n->bits)
|
||||
+ break;
|
||||
|
||||
- if (IS_LEAF(n)) {
|
||||
- ret = check_leaf(tb, t, n, key, flp, res, fib_flags);
|
||||
- if (ret > 0)
|
||||
- goto backtrace;
|
||||
+ /* we have found a leaf. Prefixes have already been compared */
|
||||
+ if (IS_LEAF(n))
|
||||
goto found;
|
||||
- }
|
||||
-
|
||||
- cn = n;
|
||||
|
||||
- /*
|
||||
- * It's a tnode, and we can do some extra checks here if we
|
||||
- * like, to avoid descending into a dead-end branch.
|
||||
- * This tnode is in the parent's child array at index
|
||||
- * key[p_pos..p_pos+p_bits] but potentially with some bits
|
||||
- * chopped off, so in reality the index may be just a
|
||||
- * subprefix, padded with zero at the end.
|
||||
- * We can also take a look at any skipped bits in this
|
||||
- * tnode - everything up to p_pos is supposed to be ok,
|
||||
- * and the non-chopped bits of the index (se previous
|
||||
- * paragraph) are also guaranteed ok, but the rest is
|
||||
- * considered unknown.
|
||||
- *
|
||||
- * The skipped bits are key[pos+bits..cn->pos].
|
||||
- */
|
||||
-
|
||||
- /* If current_prefix_length < pos+bits, we are already doing
|
||||
- * actual prefix matching, which means everything from
|
||||
- * pos+(bits-chopped_off) onward must be zero along some
|
||||
- * branch of this subtree - otherwise there is *no* valid
|
||||
- * prefix present. Here we can only check the skipped
|
||||
- * bits. Remember, since we have already indexed into the
|
||||
- * parent's child array, we know that the bits we chopped of
|
||||
- * *are* zero.
|
||||
+ /* only record pn and cindex if we are going to be chopping
|
||||
+ * bits later. Otherwise we are just wasting cycles.
|
||||
*/
|
||||
-
|
||||
- /* NOTA BENE: Checking only skipped bits
|
||||
- for the new node here */
|
||||
-
|
||||
- if (current_prefix_length < pos+bits) {
|
||||
- if (tkey_extract_bits(cn->key, current_prefix_length,
|
||||
- cn->pos - current_prefix_length)
|
||||
- || !(cn->child[0]))
|
||||
- goto backtrace;
|
||||
+ if (index) {
|
||||
+ pn = n;
|
||||
+ cindex = index;
|
||||
}
|
||||
|
||||
- /*
|
||||
- * If chopped_off=0, the index is fully validated and we
|
||||
- * only need to look at the skipped bits for this, the new,
|
||||
- * tnode. What we actually want to do is to find out if
|
||||
- * these skipped bits match our key perfectly, or if we will
|
||||
- * have to count on finding a matching prefix further down,
|
||||
- * because if we do, we would like to have some way of
|
||||
- * verifying the existence of such a prefix at this point.
|
||||
- */
|
||||
-
|
||||
- /* The only thing we can do at this point is to verify that
|
||||
- * any such matching prefix can indeed be a prefix to our
|
||||
- * key, and if the bits in the node we are inspecting that
|
||||
- * do not match our key are not ZERO, this cannot be true.
|
||||
- * Thus, find out where there is a mismatch (before cn->pos)
|
||||
- * and verify that all the mismatching bits are zero in the
|
||||
- * new tnode's key.
|
||||
- */
|
||||
+ n = rcu_dereference(n->child[index]);
|
||||
+ if (unlikely(!n))
|
||||
+ goto backtrace;
|
||||
+ }
|
||||
|
||||
- /*
|
||||
- * Note: We aren't very concerned about the piece of
|
||||
- * the key that precede pn->pos+pn->bits, since these
|
||||
- * have already been checked. The bits after cn->pos
|
||||
- * aren't checked since these are by definition
|
||||
- * "unknown" at this point. Thus, what we want to see
|
||||
- * is if we are about to enter the "prefix matching"
|
||||
- * state, and in that case verify that the skipped
|
||||
- * bits that will prevail throughout this subtree are
|
||||
- * zero, as they have to be if we are to find a
|
||||
- * matching prefix.
|
||||
+ /* Step 2: Sort out leaves and begin backtracing for longest prefix */
|
||||
+ for (;;) {
|
||||
+ /* record the pointer where our next node pointer is stored */
|
||||
+ struct tnode __rcu **cptr = n->child;
|
||||
+
|
||||
+ /* This test verifies that none of the bits that differ
|
||||
+ * between the key and the prefix exist in the region of
|
||||
+ * the lsb and higher in the prefix.
|
||||
*/
|
||||
+ if (unlikely(prefix_mismatch(key, n)))
|
||||
+ goto backtrace;
|
||||
|
||||
- pref_mismatch = mask_pfx(cn->key ^ key, cn->pos);
|
||||
+ /* exit out and process leaf */
|
||||
+ if (unlikely(IS_LEAF(n)))
|
||||
+ break;
|
||||
|
||||
- /*
|
||||
- * In short: If skipped bits in this node do not match
|
||||
- * the search key, enter the "prefix matching"
|
||||
- * state.directly.
|
||||
+ /* Don't bother recording parent info. Since we are in
|
||||
+ * prefix match mode we will have to come back to wherever
|
||||
+ * we started this traversal anyway
|
||||
*/
|
||||
- if (pref_mismatch) {
|
||||
- /* fls(x) = __fls(x) + 1 */
|
||||
- int mp = KEYLENGTH - __fls(pref_mismatch) - 1;
|
||||
-
|
||||
- if (tkey_extract_bits(cn->key, mp, cn->pos - mp) != 0)
|
||||
- goto backtrace;
|
||||
-
|
||||
- if (current_prefix_length >= cn->pos)
|
||||
- current_prefix_length = mp;
|
||||
- }
|
||||
-
|
||||
- pn = n; /* Descend */
|
||||
- chopped_off = 0;
|
||||
- continue;
|
||||
|
||||
+ while ((n = rcu_dereference(*cptr)) == NULL) {
|
||||
backtrace:
|
||||
- chopped_off++;
|
||||
-
|
||||
- /* As zero don't change the child key (cindex) */
|
||||
- while ((chopped_off <= pn->bits)
|
||||
- && !(cindex & (1<<(chopped_off-1))))
|
||||
- chopped_off++;
|
||||
-
|
||||
- /* Decrease current_... with bits chopped off */
|
||||
- if (current_prefix_length > pn->pos + pn->bits - chopped_off)
|
||||
- current_prefix_length = pn->pos + pn->bits
|
||||
- - chopped_off;
|
||||
-
|
||||
- /*
|
||||
- * Either we do the actual chop off according or if we have
|
||||
- * chopped off all bits in this tnode walk up to our parent.
|
||||
- */
|
||||
-
|
||||
- if (chopped_off <= pn->bits) {
|
||||
- cindex &= ~(1 << (chopped_off-1));
|
||||
- } else {
|
||||
- struct tnode *parent = node_parent_rcu(pn);
|
||||
- if (!parent)
|
||||
- goto failed;
|
||||
-
|
||||
- /* Get Child's index */
|
||||
- cindex = tkey_extract_bits(pn->key, parent->pos, parent->bits);
|
||||
- pn = parent;
|
||||
- chopped_off = 0;
|
||||
-
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- this_cpu_inc(stats->backtrack);
|
||||
+ if (!n)
|
||||
+ this_cpu_inc(stats->null_node_hit);
|
||||
#endif
|
||||
- goto backtrace;
|
||||
+ /* If we are at cindex 0 there are no more bits for
|
||||
+ * us to strip at this level so we must ascend back
|
||||
+ * up one level to see if there are any more bits to
|
||||
+ * be stripped there.
|
||||
+ */
|
||||
+ while (!cindex) {
|
||||
+ t_key pkey = pn->key;
|
||||
+
|
||||
+ pn = node_parent_rcu(pn);
|
||||
+ if (unlikely(!pn))
|
||||
+ goto failed;
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ this_cpu_inc(stats->backtrack);
|
||||
+#endif
|
||||
+ /* Get Child's index */
|
||||
+ cindex = get_index(pkey, pn);
|
||||
+ }
|
||||
+
|
||||
+ /* strip the least significant bit from the cindex */
|
||||
+ cindex &= cindex - 1;
|
||||
+
|
||||
+ /* grab pointer for next child node */
|
||||
+ cptr = &pn->child[cindex];
|
||||
}
|
||||
}
|
||||
-failed:
|
||||
- ret = 1;
|
||||
+
|
||||
found:
|
||||
+ /* Step 3: Process the leaf, if that fails fall back to backtracing */
|
||||
+ ret = check_leaf(tb, t, n, key, flp, res, fib_flags);
|
||||
+ if (unlikely(ret > 0))
|
||||
+ goto backtrace;
|
||||
+failed:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:00 -0800
|
||||
Subject: [PATCH] fib_trie: Optimize fib_find_node
|
||||
|
||||
This patch makes use of the same features I made use of for
|
||||
fib_table_lookup to streamline fib_find_node. The resultant code should be
|
||||
smaller and run faster than the original.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -892,28 +892,34 @@ static void insert_leaf_info(struct hlis
|
||||
}
|
||||
|
||||
/* rcu_read_lock needs to be hold by caller from readside */
|
||||
-
|
||||
static struct tnode *fib_find_node(struct trie *t, u32 key)
|
||||
{
|
||||
struct tnode *n = rcu_dereference_rtnl(t->trie);
|
||||
- int pos = 0;
|
||||
|
||||
- while (n && IS_TNODE(n)) {
|
||||
- if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) {
|
||||
- pos = n->pos + n->bits;
|
||||
- n = tnode_get_child_rcu(n,
|
||||
- tkey_extract_bits(key,
|
||||
- n->pos,
|
||||
- n->bits));
|
||||
- } else
|
||||
+ while (n) {
|
||||
+ unsigned long index = get_index(key, n);
|
||||
+
|
||||
+ /* This bit of code is a bit tricky but it combines multiple
|
||||
+ * checks into a single check. The prefix consists of the
|
||||
+ * prefix plus zeros for the bits in the cindex. The index
|
||||
+ * is the difference between the key and this value. From
|
||||
+ * this we can actually derive several pieces of data.
|
||||
+ * if !(index >> bits)
|
||||
+ * we know the value is cindex
|
||||
+ * else
|
||||
+ * we have a mismatch in skip bits and failed
|
||||
+ */
|
||||
+ if (index >> n->bits)
|
||||
+ return NULL;
|
||||
+
|
||||
+ /* we have found a leaf. Prefixes have already been compared */
|
||||
+ if (IS_LEAF(n))
|
||||
break;
|
||||
- }
|
||||
- /* Case we have found a leaf. Compare prefixes */
|
||||
|
||||
- if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key))
|
||||
- return n;
|
||||
+ n = rcu_dereference_rtnl(n->child[index]);
|
||||
+ }
|
||||
|
||||
- return NULL;
|
||||
+ return n;
|
||||
}
|
||||
|
||||
static void trie_rebalance(struct trie *t, struct tnode *tn)
|
|
@ -1,276 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:06 -0800
|
||||
Subject: [PATCH] fib_trie: Optimize fib_table_insert
|
||||
|
||||
This patch updates the fib_table_insert function to take advantage of the
|
||||
changes made to improve the performance of fib_table_lookup. As a result
|
||||
the code should be smaller and run faster then the original.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -222,31 +222,6 @@ static inline t_key tkey_extract_bits(t_
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static inline int tkey_equals(t_key a, t_key b)
|
||||
-{
|
||||
- return a == b;
|
||||
-}
|
||||
-
|
||||
-static inline int tkey_sub_equals(t_key a, int offset, int bits, t_key b)
|
||||
-{
|
||||
- if (bits == 0 || offset >= KEYLENGTH)
|
||||
- return 1;
|
||||
- bits = bits > KEYLENGTH ? KEYLENGTH : bits;
|
||||
- return ((a ^ b) << offset) >> (KEYLENGTH - bits) == 0;
|
||||
-}
|
||||
-
|
||||
-static inline int tkey_mismatch(t_key a, int offset, t_key b)
|
||||
-{
|
||||
- t_key diff = a ^ b;
|
||||
- int i = offset;
|
||||
-
|
||||
- if (!diff)
|
||||
- return 0;
|
||||
- while ((diff << i) >> (KEYLENGTH-1) == 0)
|
||||
- i++;
|
||||
- return i;
|
||||
-}
|
||||
-
|
||||
/*
|
||||
To understand this stuff, an understanding of keys and all their bits is
|
||||
necessary. Every node in the trie has a key associated with it, but not
|
||||
@@ -485,6 +460,15 @@ static void tnode_put_child_reorg(struct
|
||||
rcu_assign_pointer(tn->child[i], n);
|
||||
}
|
||||
|
||||
+static void put_child_root(struct tnode *tp, struct trie *t,
|
||||
+ t_key key, struct tnode *n)
|
||||
+{
|
||||
+ if (tp)
|
||||
+ put_child(tp, get_index(key, tp), n);
|
||||
+ else
|
||||
+ rcu_assign_pointer(t->trie, n);
|
||||
+}
|
||||
+
|
||||
#define MAX_WORK 10
|
||||
static struct tnode *resize(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
@@ -959,138 +943,100 @@ static void trie_rebalance(struct trie *
|
||||
|
||||
static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
|
||||
{
|
||||
- int pos, newpos;
|
||||
- struct tnode *tp = NULL, *tn = NULL;
|
||||
- struct tnode *n;
|
||||
- struct tnode *l;
|
||||
- int missbit;
|
||||
struct list_head *fa_head = NULL;
|
||||
+ struct tnode *l, *n, *tp = NULL;
|
||||
struct leaf_info *li;
|
||||
- t_key cindex;
|
||||
|
||||
- pos = 0;
|
||||
+ li = leaf_info_new(plen);
|
||||
+ if (!li)
|
||||
+ return NULL;
|
||||
+ fa_head = &li->falh;
|
||||
+
|
||||
n = rtnl_dereference(t->trie);
|
||||
|
||||
/* If we point to NULL, stop. Either the tree is empty and we should
|
||||
* just put a new leaf in if, or we have reached an empty child slot,
|
||||
* and we should just put our new leaf in that.
|
||||
- * If we point to a T_TNODE, check if it matches our key. Note that
|
||||
- * a T_TNODE might be skipping any number of bits - its 'pos' need
|
||||
- * not be the parent's 'pos'+'bits'!
|
||||
- *
|
||||
- * If it does match the current key, get pos/bits from it, extract
|
||||
- * the index from our key, push the T_TNODE and walk the tree.
|
||||
- *
|
||||
- * If it doesn't, we have to replace it with a new T_TNODE.
|
||||
*
|
||||
- * If we point to a T_LEAF, it might or might not have the same key
|
||||
- * as we do. If it does, just change the value, update the T_LEAF's
|
||||
- * value, and return it.
|
||||
- * If it doesn't, we need to replace it with a T_TNODE.
|
||||
+ * If we hit a node with a key that does't match then we should stop
|
||||
+ * and create a new tnode to replace that node and insert ourselves
|
||||
+ * and the other node into the new tnode.
|
||||
*/
|
||||
+ while (n) {
|
||||
+ unsigned long index = get_index(key, n);
|
||||
|
||||
- while (n && IS_TNODE(n)) {
|
||||
- if (tkey_sub_equals(n->key, pos, n->pos-pos, key)) {
|
||||
- tp = n;
|
||||
- pos = n->pos + n->bits;
|
||||
- n = tnode_get_child(n,
|
||||
- tkey_extract_bits(key,
|
||||
- n->pos,
|
||||
- n->bits));
|
||||
-
|
||||
- BUG_ON(n && node_parent(n) != tp);
|
||||
- } else
|
||||
+ /* This bit of code is a bit tricky but it combines multiple
|
||||
+ * checks into a single check. The prefix consists of the
|
||||
+ * prefix plus zeros for the "bits" in the prefix. The index
|
||||
+ * is the difference between the key and this value. From
|
||||
+ * this we can actually derive several pieces of data.
|
||||
+ * if !(index >> bits)
|
||||
+ * we know the value is child index
|
||||
+ * else
|
||||
+ * we have a mismatch in skip bits and failed
|
||||
+ */
|
||||
+ if (index >> n->bits)
|
||||
break;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * n ----> NULL, LEAF or TNODE
|
||||
- *
|
||||
- * tp is n's (parent) ----> NULL or TNODE
|
||||
- */
|
||||
|
||||
- BUG_ON(tp && IS_LEAF(tp));
|
||||
-
|
||||
- /* Case 1: n is a leaf. Compare prefixes */
|
||||
-
|
||||
- if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) {
|
||||
- li = leaf_info_new(plen);
|
||||
-
|
||||
- if (!li)
|
||||
- return NULL;
|
||||
+ /* we have found a leaf. Prefixes have already been compared */
|
||||
+ if (IS_LEAF(n)) {
|
||||
+ /* Case 1: n is a leaf, and prefixes match*/
|
||||
+ insert_leaf_info(&n->list, li);
|
||||
+ return fa_head;
|
||||
+ }
|
||||
|
||||
- fa_head = &li->falh;
|
||||
- insert_leaf_info(&n->list, li);
|
||||
- goto done;
|
||||
+ tp = n;
|
||||
+ n = rcu_dereference_rtnl(n->child[index]);
|
||||
}
|
||||
- l = leaf_new(key);
|
||||
-
|
||||
- if (!l)
|
||||
- return NULL;
|
||||
|
||||
- li = leaf_info_new(plen);
|
||||
-
|
||||
- if (!li) {
|
||||
- node_free(l);
|
||||
+ l = leaf_new(key);
|
||||
+ if (!l) {
|
||||
+ free_leaf_info(li);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- fa_head = &li->falh;
|
||||
insert_leaf_info(&l->list, li);
|
||||
|
||||
- if (t->trie && n == NULL) {
|
||||
- /* Case 2: n is NULL, and will just insert a new leaf */
|
||||
-
|
||||
- node_set_parent(l, tp);
|
||||
-
|
||||
- cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
||||
- put_child(tp, cindex, l);
|
||||
- } else {
|
||||
- /* Case 3: n is a LEAF or a TNODE and the key doesn't match. */
|
||||
- /*
|
||||
- * Add a new tnode here
|
||||
- * first tnode need some special handling
|
||||
- */
|
||||
+ /* Case 2: n is a LEAF or a TNODE and the key doesn't match.
|
||||
+ *
|
||||
+ * Add a new tnode here
|
||||
+ * first tnode need some special handling
|
||||
+ * leaves us in position for handling as case 3
|
||||
+ */
|
||||
+ if (n) {
|
||||
+ struct tnode *tn;
|
||||
+ int newpos;
|
||||
|
||||
- if (n) {
|
||||
- pos = tp ? tp->pos+tp->bits : 0;
|
||||
- newpos = tkey_mismatch(key, pos, n->key);
|
||||
- tn = tnode_new(n->key, newpos, 1);
|
||||
- } else {
|
||||
- newpos = 0;
|
||||
- tn = tnode_new(key, newpos, 1); /* First tnode */
|
||||
- }
|
||||
+ newpos = KEYLENGTH - __fls(n->key ^ key) - 1;
|
||||
|
||||
+ tn = tnode_new(key, newpos, 1);
|
||||
if (!tn) {
|
||||
free_leaf_info(li);
|
||||
node_free(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- node_set_parent(tn, tp);
|
||||
-
|
||||
- missbit = tkey_extract_bits(key, newpos, 1);
|
||||
- put_child(tn, missbit, l);
|
||||
- put_child(tn, 1-missbit, n);
|
||||
-
|
||||
- if (tp) {
|
||||
- cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
||||
- put_child(tp, cindex, tn);
|
||||
- } else {
|
||||
- rcu_assign_pointer(t->trie, tn);
|
||||
- }
|
||||
+ /* initialize routes out of node */
|
||||
+ NODE_INIT_PARENT(tn, tp);
|
||||
+ put_child(tn, get_index(key, tn) ^ 1, n);
|
||||
+
|
||||
+ /* start adding routes into the node */
|
||||
+ put_child_root(tp, t, key, tn);
|
||||
+ node_set_parent(n, tn);
|
||||
|
||||
+ /* parent now has a NULL spot where the leaf can go */
|
||||
tp = tn;
|
||||
}
|
||||
|
||||
- if (tp && tp->pos + tp->bits > 32)
|
||||
- pr_warn("fib_trie tp=%p pos=%d, bits=%d, key=%0x plen=%d\n",
|
||||
- tp, tp->pos, tp->bits, key, plen);
|
||||
-
|
||||
- /* Rebalance the trie */
|
||||
+ /* Case 3: n is NULL, and will just insert a new leaf */
|
||||
+ if (tp) {
|
||||
+ NODE_INIT_PARENT(l, tp);
|
||||
+ put_child(tp, get_index(key, tp), l);
|
||||
+ trie_rebalance(t, tp);
|
||||
+ } else {
|
||||
+ rcu_assign_pointer(t->trie, l);
|
||||
+ }
|
||||
|
||||
- trie_rebalance(t, tp);
|
||||
-done:
|
||||
return fa_head;
|
||||
}
|
||||
|
||||
@@ -1470,11 +1416,11 @@ static void trie_leaf_remove(struct trie
|
||||
pr_debug("entering trie_leaf_remove(%p)\n", l);
|
||||
|
||||
if (tp) {
|
||||
- t_key cindex = tkey_extract_bits(l->key, tp->pos, tp->bits);
|
||||
- put_child(tp, cindex, NULL);
|
||||
+ put_child(tp, get_index(l->key, tp), NULL);
|
||||
trie_rebalance(t, tp);
|
||||
- } else
|
||||
+ } else {
|
||||
RCU_INIT_POINTER(t->trie, NULL);
|
||||
+ }
|
||||
|
||||
node_free(l);
|
||||
}
|
|
@ -1,346 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:12 -0800
|
||||
Subject: [PATCH] fib_trie: Update meaning of pos to represent unchecked
|
||||
bits
|
||||
|
||||
This change moves the pos value to the other side of the "bits" field. By
|
||||
doing this it actually simplifies a significant amount of code in the trie.
|
||||
|
||||
For example when halving a tree we know that the bit lost exists at
|
||||
oldnode->pos, and if we inflate the tree the new bit being add is at
|
||||
tn->pos. Previously to find those bits you would have to subtract pos and
|
||||
bits from the keylength or start with a value of (1 << 31) and then shift
|
||||
that.
|
||||
|
||||
There are a number of spots throughout the code that benefit from this. In
|
||||
the case of the hot-path searches the main advantage is that we can drop 2
|
||||
or more operations from the search path as we no longer need to compute the
|
||||
value for the index to be shifted by and can instead just use the raw pos
|
||||
value.
|
||||
|
||||
In addition the tkey_extract_bits is now defunct and can be replaced by
|
||||
get_index since the two operations were doing the same thing, but now
|
||||
get_index does it much more quickly as it is only an xor and shift versus a
|
||||
pair of shifts and a subtraction.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -90,8 +90,7 @@ typedef unsigned int t_key;
|
||||
#define IS_TNODE(n) ((n)->bits)
|
||||
#define IS_LEAF(n) (!(n)->bits)
|
||||
|
||||
-#define get_shift(_kv) (KEYLENGTH - (_kv)->pos - (_kv)->bits)
|
||||
-#define get_index(_key, _kv) (((_key) ^ (_kv)->key) >> get_shift(_kv))
|
||||
+#define get_index(_key, _kv) (((_key) ^ (_kv)->key) >> (_kv)->pos)
|
||||
|
||||
struct tnode {
|
||||
t_key key;
|
||||
@@ -209,81 +208,64 @@ static inline struct tnode *tnode_get_ch
|
||||
return rcu_dereference_rtnl(tn->child[i]);
|
||||
}
|
||||
|
||||
-static inline t_key mask_pfx(t_key k, unsigned int l)
|
||||
-{
|
||||
- return (l == 0) ? 0 : k >> (KEYLENGTH-l) << (KEYLENGTH-l);
|
||||
-}
|
||||
-
|
||||
-static inline t_key tkey_extract_bits(t_key a, unsigned int offset, unsigned int bits)
|
||||
-{
|
||||
- if (offset < KEYLENGTH)
|
||||
- return ((t_key)(a << offset)) >> (KEYLENGTH - bits);
|
||||
- else
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- To understand this stuff, an understanding of keys and all their bits is
|
||||
- necessary. Every node in the trie has a key associated with it, but not
|
||||
- all of the bits in that key are significant.
|
||||
-
|
||||
- Consider a node 'n' and its parent 'tp'.
|
||||
-
|
||||
- If n is a leaf, every bit in its key is significant. Its presence is
|
||||
- necessitated by path compression, since during a tree traversal (when
|
||||
- searching for a leaf - unless we are doing an insertion) we will completely
|
||||
- ignore all skipped bits we encounter. Thus we need to verify, at the end of
|
||||
- a potentially successful search, that we have indeed been walking the
|
||||
- correct key path.
|
||||
-
|
||||
- Note that we can never "miss" the correct key in the tree if present by
|
||||
- following the wrong path. Path compression ensures that segments of the key
|
||||
- that are the same for all keys with a given prefix are skipped, but the
|
||||
- skipped part *is* identical for each node in the subtrie below the skipped
|
||||
- bit! trie_insert() in this implementation takes care of that - note the
|
||||
- call to tkey_sub_equals() in trie_insert().
|
||||
-
|
||||
- if n is an internal node - a 'tnode' here, the various parts of its key
|
||||
- have many different meanings.
|
||||
-
|
||||
- Example:
|
||||
- _________________________________________________________________
|
||||
- | i | i | i | i | i | i | i | N | N | N | S | S | S | S | S | C |
|
||||
- -----------------------------------------------------------------
|
||||
- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
-
|
||||
- _________________________________________________________________
|
||||
- | C | C | C | u | u | u | u | u | u | u | u | u | u | u | u | u |
|
||||
- -----------------------------------------------------------------
|
||||
- 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
-
|
||||
- tp->pos = 7
|
||||
- tp->bits = 3
|
||||
- n->pos = 15
|
||||
- n->bits = 4
|
||||
-
|
||||
- First, let's just ignore the bits that come before the parent tp, that is
|
||||
- the bits from 0 to (tp->pos-1). They are *known* but at this point we do
|
||||
- not use them for anything.
|
||||
-
|
||||
- The bits from (tp->pos) to (tp->pos + tp->bits - 1) - "N", above - are the
|
||||
- index into the parent's child array. That is, they will be used to find
|
||||
- 'n' among tp's children.
|
||||
-
|
||||
- The bits from (tp->pos + tp->bits) to (n->pos - 1) - "S" - are skipped bits
|
||||
- for the node n.
|
||||
-
|
||||
- All the bits we have seen so far are significant to the node n. The rest
|
||||
- of the bits are really not needed or indeed known in n->key.
|
||||
-
|
||||
- The bits from (n->pos) to (n->pos + n->bits - 1) - "C" - are the index into
|
||||
- n's child array, and will of course be different for each child.
|
||||
-
|
||||
-
|
||||
- The rest of the bits, from (n->pos + n->bits) onward, are completely unknown
|
||||
- at this point.
|
||||
-
|
||||
-*/
|
||||
+/* To understand this stuff, an understanding of keys and all their bits is
|
||||
+ * necessary. Every node in the trie has a key associated with it, but not
|
||||
+ * all of the bits in that key are significant.
|
||||
+ *
|
||||
+ * Consider a node 'n' and its parent 'tp'.
|
||||
+ *
|
||||
+ * If n is a leaf, every bit in its key is significant. Its presence is
|
||||
+ * necessitated by path compression, since during a tree traversal (when
|
||||
+ * searching for a leaf - unless we are doing an insertion) we will completely
|
||||
+ * ignore all skipped bits we encounter. Thus we need to verify, at the end of
|
||||
+ * a potentially successful search, that we have indeed been walking the
|
||||
+ * correct key path.
|
||||
+ *
|
||||
+ * Note that we can never "miss" the correct key in the tree if present by
|
||||
+ * following the wrong path. Path compression ensures that segments of the key
|
||||
+ * that are the same for all keys with a given prefix are skipped, but the
|
||||
+ * skipped part *is* identical for each node in the subtrie below the skipped
|
||||
+ * bit! trie_insert() in this implementation takes care of that.
|
||||
+ *
|
||||
+ * if n is an internal node - a 'tnode' here, the various parts of its key
|
||||
+ * have many different meanings.
|
||||
+ *
|
||||
+ * Example:
|
||||
+ * _________________________________________________________________
|
||||
+ * | i | i | i | i | i | i | i | N | N | N | S | S | S | S | S | C |
|
||||
+ * -----------------------------------------------------------------
|
||||
+ * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
|
||||
+ *
|
||||
+ * _________________________________________________________________
|
||||
+ * | C | C | C | u | u | u | u | u | u | u | u | u | u | u | u | u |
|
||||
+ * -----------------------------------------------------------------
|
||||
+ * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
+ *
|
||||
+ * tp->pos = 22
|
||||
+ * tp->bits = 3
|
||||
+ * n->pos = 13
|
||||
+ * n->bits = 4
|
||||
+ *
|
||||
+ * First, let's just ignore the bits that come before the parent tp, that is
|
||||
+ * the bits from (tp->pos + tp->bits) to 31. They are *known* but at this
|
||||
+ * point we do not use them for anything.
|
||||
+ *
|
||||
+ * The bits from (tp->pos) to (tp->pos + tp->bits - 1) - "N", above - are the
|
||||
+ * index into the parent's child array. That is, they will be used to find
|
||||
+ * 'n' among tp's children.
|
||||
+ *
|
||||
+ * The bits from (n->pos + n->bits) to (tn->pos - 1) - "S" - are skipped bits
|
||||
+ * for the node n.
|
||||
+ *
|
||||
+ * All the bits we have seen so far are significant to the node n. The rest
|
||||
+ * of the bits are really not needed or indeed known in n->key.
|
||||
+ *
|
||||
+ * The bits from (n->pos) to (n->pos + n->bits - 1) - "C" - are the index into
|
||||
+ * n's child array, and will of course be different for each child.
|
||||
+ *
|
||||
+ * The rest of the bits, from 0 to (n->pos + n->bits), are completely unknown
|
||||
+ * at this point.
|
||||
+ */
|
||||
|
||||
static const int halve_threshold = 25;
|
||||
static const int inflate_threshold = 50;
|
||||
@@ -367,7 +349,7 @@ static struct tnode *leaf_new(t_key key)
|
||||
* as the nodes are searched
|
||||
*/
|
||||
l->key = key;
|
||||
- l->pos = KEYLENGTH;
|
||||
+ l->pos = 0;
|
||||
/* set bits to 0 indicating we are not a tnode */
|
||||
l->bits = 0;
|
||||
|
||||
@@ -400,7 +382,7 @@ static struct tnode *tnode_new(t_key key
|
||||
tn->parent = NULL;
|
||||
tn->pos = pos;
|
||||
tn->bits = bits;
|
||||
- tn->key = mask_pfx(key, pos);
|
||||
+ tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0;
|
||||
tn->full_children = 0;
|
||||
tn->empty_children = 1<<bits;
|
||||
}
|
||||
@@ -410,14 +392,12 @@ static struct tnode *tnode_new(t_key key
|
||||
return tn;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Check whether a tnode 'n' is "full", i.e. it is an internal node
|
||||
+/* Check whether a tnode 'n' is "full", i.e. it is an internal node
|
||||
* and no bits are skipped. See discussion in dyntree paper p. 6
|
||||
*/
|
||||
-
|
||||
static inline int tnode_full(const struct tnode *tn, const struct tnode *n)
|
||||
{
|
||||
- return n && IS_TNODE(n) && (n->pos == (tn->pos + tn->bits));
|
||||
+ return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n);
|
||||
}
|
||||
|
||||
static inline void put_child(struct tnode *tn, int i,
|
||||
@@ -641,11 +621,12 @@ static struct tnode *inflate(struct trie
|
||||
{
|
||||
int olen = tnode_child_length(oldtnode);
|
||||
struct tnode *tn;
|
||||
+ t_key m;
|
||||
int i;
|
||||
|
||||
pr_debug("In inflate\n");
|
||||
|
||||
- tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits + 1);
|
||||
+ tn = tnode_new(oldtnode->key, oldtnode->pos - 1, oldtnode->bits + 1);
|
||||
|
||||
if (!tn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@@ -656,21 +637,18 @@ static struct tnode *inflate(struct trie
|
||||
* fails. In case of failure we return the oldnode and inflate
|
||||
* of tnode is ignored.
|
||||
*/
|
||||
+ for (i = 0, m = 1u << tn->pos; i < olen; i++) {
|
||||
+ struct tnode *inode = tnode_get_child(oldtnode, i);
|
||||
|
||||
- for (i = 0; i < olen; i++) {
|
||||
- struct tnode *inode;
|
||||
-
|
||||
- inode = tnode_get_child(oldtnode, i);
|
||||
- if (tnode_full(oldtnode, inode) && inode->bits > 1) {
|
||||
+ if (tnode_full(oldtnode, inode) && (inode->bits > 1)) {
|
||||
struct tnode *left, *right;
|
||||
- t_key m = ~0U << (KEYLENGTH - 1) >> inode->pos;
|
||||
|
||||
- left = tnode_new(inode->key&(~m), inode->pos + 1,
|
||||
+ left = tnode_new(inode->key & ~m, inode->pos,
|
||||
inode->bits - 1);
|
||||
if (!left)
|
||||
goto nomem;
|
||||
|
||||
- right = tnode_new(inode->key|m, inode->pos + 1,
|
||||
+ right = tnode_new(inode->key | m, inode->pos,
|
||||
inode->bits - 1);
|
||||
|
||||
if (!right) {
|
||||
@@ -694,9 +672,7 @@ static struct tnode *inflate(struct trie
|
||||
|
||||
/* A leaf or an internal node with skipped bits */
|
||||
if (!tnode_full(oldtnode, inode)) {
|
||||
- put_child(tn,
|
||||
- tkey_extract_bits(inode->key, tn->pos, tn->bits),
|
||||
- inode);
|
||||
+ put_child(tn, get_index(inode->key, tn), inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -767,7 +743,7 @@ static struct tnode *halve(struct trie *
|
||||
|
||||
pr_debug("In halve\n");
|
||||
|
||||
- tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits - 1);
|
||||
+ tn = tnode_new(oldtnode->key, oldtnode->pos + 1, oldtnode->bits - 1);
|
||||
|
||||
if (!tn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@@ -787,7 +763,7 @@ static struct tnode *halve(struct trie *
|
||||
if (left && right) {
|
||||
struct tnode *newn;
|
||||
|
||||
- newn = tnode_new(left->key, tn->pos + tn->bits, 1);
|
||||
+ newn = tnode_new(left->key, oldtnode->pos, 1);
|
||||
|
||||
if (!newn)
|
||||
goto nomem;
|
||||
@@ -915,7 +891,7 @@ static void trie_rebalance(struct trie *
|
||||
key = tn->key;
|
||||
|
||||
while (tn != NULL && (tp = node_parent(tn)) != NULL) {
|
||||
- cindex = tkey_extract_bits(key, tp->pos, tp->bits);
|
||||
+ cindex = get_index(key, tp);
|
||||
wasfull = tnode_full(tp, tnode_get_child(tp, cindex));
|
||||
tn = resize(t, tn);
|
||||
|
||||
@@ -1005,11 +981,8 @@ static struct list_head *fib_insert_node
|
||||
*/
|
||||
if (n) {
|
||||
struct tnode *tn;
|
||||
- int newpos;
|
||||
-
|
||||
- newpos = KEYLENGTH - __fls(n->key ^ key) - 1;
|
||||
|
||||
- tn = tnode_new(key, newpos, 1);
|
||||
+ tn = tnode_new(key, __fls(key ^ n->key), 1);
|
||||
if (!tn) {
|
||||
free_leaf_info(li);
|
||||
node_free(l);
|
||||
@@ -1559,12 +1532,7 @@ static int trie_flush_leaf(struct tnode
|
||||
static struct tnode *leaf_walk_rcu(struct tnode *p, struct tnode *c)
|
||||
{
|
||||
do {
|
||||
- t_key idx;
|
||||
-
|
||||
- if (c)
|
||||
- idx = tkey_extract_bits(c->key, p->pos, p->bits) + 1;
|
||||
- else
|
||||
- idx = 0;
|
||||
+ t_key idx = c ? idx = get_index(c->key, p) + 1 : 0;
|
||||
|
||||
while (idx < 1u << p->bits) {
|
||||
c = tnode_get_child_rcu(p, idx++);
|
||||
@@ -1851,7 +1819,7 @@ rescan:
|
||||
/* Current node exhausted, pop back up */
|
||||
p = node_parent_rcu(tn);
|
||||
if (p) {
|
||||
- cindex = tkey_extract_bits(tn->key, p->pos, p->bits)+1;
|
||||
+ cindex = get_index(tn->key, p) + 1;
|
||||
tn = p;
|
||||
--iter->depth;
|
||||
goto rescan;
|
||||
@@ -2186,10 +2154,10 @@ static int fib_trie_seq_show(struct seq_
|
||||
if (IS_TNODE(n)) {
|
||||
__be32 prf = htonl(n->key);
|
||||
|
||||
- seq_indent(seq, iter->depth - 1);
|
||||
- seq_printf(seq, " +-- %pI4/%d %d %d %d\n",
|
||||
- &prf, n->pos, n->bits, n->full_children,
|
||||
- n->empty_children);
|
||||
+ seq_indent(seq, iter->depth-1);
|
||||
+ seq_printf(seq, " +-- %pI4/%zu %u %u %u\n",
|
||||
+ &prf, KEYLENGTH - n->pos - n->bits, n->bits,
|
||||
+ n->full_children, n->empty_children);
|
||||
} else {
|
||||
struct leaf_info *li;
|
||||
__be32 val = htonl(n->key);
|
|
@ -1,186 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:18 -0800
|
||||
Subject: [PATCH] fib_trie: Use unsigned long for anything dealing with a
|
||||
shift by bits
|
||||
|
||||
This change makes it so that anything that can be shifted by, or compared
|
||||
to a value shifted by bits is updated to be an unsigned long. This is
|
||||
mostly a precaution against an insanely huge address space that somehow
|
||||
starts coming close to the 2^32 root node size which would require
|
||||
something like 1.5 billion addresses.
|
||||
|
||||
I chose unsigned long instead of unsigned long long since I do not believe
|
||||
it is possible to allocate a 32 bit tnode on a 32 bit system as the memory
|
||||
consumed would be 16GB + 28B which exceeds the addressible space for any
|
||||
one process.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -146,8 +146,8 @@ struct trie {
|
||||
#endif
|
||||
};
|
||||
|
||||
-static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n,
|
||||
- int wasfull);
|
||||
+static void tnode_put_child_reorg(struct tnode *tn, unsigned long i,
|
||||
+ struct tnode *n, int wasfull);
|
||||
static struct tnode *resize(struct trie *t, struct tnode *tn);
|
||||
static struct tnode *inflate(struct trie *t, struct tnode *tn);
|
||||
static struct tnode *halve(struct trie *t, struct tnode *tn);
|
||||
@@ -183,25 +183,23 @@ static inline void node_set_parent(struc
|
||||
/* This provides us with the number of children in this node, in the case of a
|
||||
* leaf this will return 0 meaning none of the children are accessible.
|
||||
*/
|
||||
-static inline int tnode_child_length(const struct tnode *tn)
|
||||
+static inline unsigned long tnode_child_length(const struct tnode *tn)
|
||||
{
|
||||
return (1ul << tn->bits) & ~(1ul);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * caller must hold RTNL
|
||||
- */
|
||||
-static inline struct tnode *tnode_get_child(const struct tnode *tn, unsigned int i)
|
||||
+/* caller must hold RTNL */
|
||||
+static inline struct tnode *tnode_get_child(const struct tnode *tn,
|
||||
+ unsigned long i)
|
||||
{
|
||||
BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
return rtnl_dereference(tn->child[i]);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * caller must hold RCU read lock or RTNL
|
||||
- */
|
||||
-static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn, unsigned int i)
|
||||
+/* caller must hold RCU read lock or RTNL */
|
||||
+static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn,
|
||||
+ unsigned long i)
|
||||
{
|
||||
BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
@@ -400,7 +398,7 @@ static inline int tnode_full(const struc
|
||||
return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n);
|
||||
}
|
||||
|
||||
-static inline void put_child(struct tnode *tn, int i,
|
||||
+static inline void put_child(struct tnode *tn, unsigned long i,
|
||||
struct tnode *n)
|
||||
{
|
||||
tnode_put_child_reorg(tn, i, n, -1);
|
||||
@@ -411,13 +409,13 @@ static inline void put_child(struct tnod
|
||||
* Update the value of full_children and empty_children.
|
||||
*/
|
||||
|
||||
-static void tnode_put_child_reorg(struct tnode *tn, int i, struct tnode *n,
|
||||
- int wasfull)
|
||||
+static void tnode_put_child_reorg(struct tnode *tn, unsigned long i,
|
||||
+ struct tnode *n, int wasfull)
|
||||
{
|
||||
struct tnode *chi = rtnl_dereference(tn->child[i]);
|
||||
int isfull;
|
||||
|
||||
- BUG_ON(i >= 1<<tn->bits);
|
||||
+ BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
/* update emptyChildren */
|
||||
if (n == NULL && chi != NULL)
|
||||
@@ -607,10 +605,10 @@ no_children:
|
||||
static void tnode_clean_free(struct tnode *tn)
|
||||
{
|
||||
struct tnode *tofree;
|
||||
- int i;
|
||||
+ unsigned long i;
|
||||
|
||||
for (i = 0; i < tnode_child_length(tn); i++) {
|
||||
- tofree = rtnl_dereference(tn->child[i]);
|
||||
+ tofree = tnode_get_child(tn, i);
|
||||
if (tofree)
|
||||
node_free(tofree);
|
||||
}
|
||||
@@ -619,10 +617,10 @@ static void tnode_clean_free(struct tnod
|
||||
|
||||
static struct tnode *inflate(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- int olen = tnode_child_length(oldtnode);
|
||||
+ unsigned long olen = tnode_child_length(oldtnode);
|
||||
struct tnode *tn;
|
||||
+ unsigned long i;
|
||||
t_key m;
|
||||
- int i;
|
||||
|
||||
pr_debug("In inflate\n");
|
||||
|
||||
@@ -664,7 +662,7 @@ static struct tnode *inflate(struct trie
|
||||
for (i = 0; i < olen; i++) {
|
||||
struct tnode *inode = tnode_get_child(oldtnode, i);
|
||||
struct tnode *left, *right;
|
||||
- int size, j;
|
||||
+ unsigned long size, j;
|
||||
|
||||
/* An empty child */
|
||||
if (inode == NULL)
|
||||
@@ -737,7 +735,7 @@ nomem:
|
||||
|
||||
static struct tnode *halve(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- int olen = tnode_child_length(oldtnode);
|
||||
+ unsigned long olen = tnode_child_length(oldtnode);
|
||||
struct tnode *tn, *left, *right;
|
||||
int i;
|
||||
|
||||
@@ -1532,9 +1530,9 @@ static int trie_flush_leaf(struct tnode
|
||||
static struct tnode *leaf_walk_rcu(struct tnode *p, struct tnode *c)
|
||||
{
|
||||
do {
|
||||
- t_key idx = c ? idx = get_index(c->key, p) + 1 : 0;
|
||||
+ unsigned long idx = c ? idx = get_index(c->key, p) + 1 : 0;
|
||||
|
||||
- while (idx < 1u << p->bits) {
|
||||
+ while (idx < tnode_child_length(p)) {
|
||||
c = tnode_get_child_rcu(p, idx++);
|
||||
if (!c)
|
||||
continue;
|
||||
@@ -1786,8 +1784,8 @@ struct fib_trie_iter {
|
||||
|
||||
static struct tnode *fib_trie_get_next(struct fib_trie_iter *iter)
|
||||
{
|
||||
+ unsigned long cindex = iter->index;
|
||||
struct tnode *tn = iter->tnode;
|
||||
- unsigned int cindex = iter->index;
|
||||
struct tnode *p;
|
||||
|
||||
/* A single entry routing table */
|
||||
@@ -1797,7 +1795,7 @@ static struct tnode *fib_trie_get_next(s
|
||||
pr_debug("get_next iter={node=%p index=%d depth=%d}\n",
|
||||
iter->tnode, iter->index, iter->depth);
|
||||
rescan:
|
||||
- while (cindex < (1<<tn->bits)) {
|
||||
+ while (cindex < tnode_child_length(tn)) {
|
||||
struct tnode *n = tnode_get_child_rcu(tn, cindex);
|
||||
|
||||
if (n) {
|
||||
@@ -1874,15 +1872,16 @@ static void trie_collect_stats(struct tr
|
||||
hlist_for_each_entry_rcu(li, &n->list, hlist)
|
||||
++s->prefixes;
|
||||
} else {
|
||||
- int i;
|
||||
+ unsigned long i;
|
||||
|
||||
s->tnodes++;
|
||||
if (n->bits < MAX_STAT_DEPTH)
|
||||
s->nodesizes[n->bits]++;
|
||||
|
||||
- for (i = 0; i < tnode_child_length(n); i++)
|
||||
+ for (i = 0; i < tnode_child_length(n); i++) {
|
||||
if (!rcu_access_pointer(n->child[i]))
|
||||
s->nullpointers++;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
|
@ -1,403 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:24 -0800
|
||||
Subject: [PATCH] fib_trie: Push rcu_read_lock/unlock to callers
|
||||
|
||||
This change is to start cleaning up some of the rcu_read_lock/unlock
|
||||
handling. I realized while reviewing the code there are several spots that
|
||||
I don't believe are being handled correctly or are masking warnings by
|
||||
locally calling rcu_read_lock/unlock instead of calling them at the correct
|
||||
level.
|
||||
|
||||
A common example is a call to fib_get_table followed by fib_table_lookup.
|
||||
The rcu_read_lock/unlock ought to wrap both but there are several spots where
|
||||
they were not wrapped.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/include/net/ip_fib.h
|
||||
+++ b/include/net/ip_fib.h
|
||||
@@ -222,16 +222,19 @@ static inline struct fib_table *fib_new_
|
||||
static inline int fib_lookup(struct net *net, const struct flowi4 *flp,
|
||||
struct fib_result *res)
|
||||
{
|
||||
- struct fib_table *table;
|
||||
+ int err = -ENETUNREACH;
|
||||
|
||||
- table = fib_get_table(net, RT_TABLE_LOCAL);
|
||||
- if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
|
||||
- return 0;
|
||||
-
|
||||
- table = fib_get_table(net, RT_TABLE_MAIN);
|
||||
- if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
|
||||
- return 0;
|
||||
- return -ENETUNREACH;
|
||||
+ rcu_read_lock();
|
||||
+
|
||||
+ if (!fib_table_lookup(fib_get_table(net, RT_TABLE_LOCAL), flp, res,
|
||||
+ FIB_LOOKUP_NOREF) ||
|
||||
+ !fib_table_lookup(fib_get_table(net, RT_TABLE_MAIN), flp, res,
|
||||
+ FIB_LOOKUP_NOREF))
|
||||
+ err = 0;
|
||||
+
|
||||
+ rcu_read_unlock();
|
||||
+
|
||||
+ return err;
|
||||
}
|
||||
|
||||
#else /* CONFIG_IP_MULTIPLE_TABLES */
|
||||
@@ -247,20 +250,25 @@ static inline int fib_lookup(struct net
|
||||
struct fib_result *res)
|
||||
{
|
||||
if (!net->ipv4.fib_has_custom_rules) {
|
||||
+ int err = -ENETUNREACH;
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+
|
||||
res->tclassid = 0;
|
||||
- if (net->ipv4.fib_local &&
|
||||
- !fib_table_lookup(net->ipv4.fib_local, flp, res,
|
||||
- FIB_LOOKUP_NOREF))
|
||||
- return 0;
|
||||
- if (net->ipv4.fib_main &&
|
||||
- !fib_table_lookup(net->ipv4.fib_main, flp, res,
|
||||
- FIB_LOOKUP_NOREF))
|
||||
- return 0;
|
||||
- if (net->ipv4.fib_default &&
|
||||
- !fib_table_lookup(net->ipv4.fib_default, flp, res,
|
||||
- FIB_LOOKUP_NOREF))
|
||||
- return 0;
|
||||
- return -ENETUNREACH;
|
||||
+ if ((net->ipv4.fib_local &&
|
||||
+ !fib_table_lookup(net->ipv4.fib_local, flp, res,
|
||||
+ FIB_LOOKUP_NOREF)) ||
|
||||
+ (net->ipv4.fib_main &&
|
||||
+ !fib_table_lookup(net->ipv4.fib_main, flp, res,
|
||||
+ FIB_LOOKUP_NOREF)) ||
|
||||
+ (net->ipv4.fib_default &&
|
||||
+ !fib_table_lookup(net->ipv4.fib_default, flp, res,
|
||||
+ FIB_LOOKUP_NOREF)))
|
||||
+ err = 0;
|
||||
+
|
||||
+ rcu_read_unlock();
|
||||
+
|
||||
+ return err;
|
||||
}
|
||||
return __fib_lookup(net, flp, res);
|
||||
}
|
||||
--- a/net/ipv4/fib_frontend.c
|
||||
+++ b/net/ipv4/fib_frontend.c
|
||||
@@ -109,6 +109,7 @@ struct fib_table *fib_new_table(struct n
|
||||
return tb;
|
||||
}
|
||||
|
||||
+/* caller must hold either rtnl or rcu read lock */
|
||||
struct fib_table *fib_get_table(struct net *net, u32 id)
|
||||
{
|
||||
struct fib_table *tb;
|
||||
@@ -119,15 +120,11 @@ struct fib_table *fib_get_table(struct n
|
||||
id = RT_TABLE_MAIN;
|
||||
h = id & (FIB_TABLE_HASHSZ - 1);
|
||||
|
||||
- rcu_read_lock();
|
||||
head = &net->ipv4.fib_table_hash[h];
|
||||
hlist_for_each_entry_rcu(tb, head, tb_hlist) {
|
||||
- if (tb->tb_id == id) {
|
||||
- rcu_read_unlock();
|
||||
+ if (tb->tb_id == id)
|
||||
return tb;
|
||||
- }
|
||||
}
|
||||
- rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_IP_MULTIPLE_TABLES */
|
||||
@@ -167,16 +164,18 @@ static inline unsigned int __inet_dev_ad
|
||||
if (ipv4_is_multicast(addr))
|
||||
return RTN_MULTICAST;
|
||||
|
||||
+ rcu_read_lock();
|
||||
+
|
||||
local_table = fib_get_table(net, RT_TABLE_LOCAL);
|
||||
if (local_table) {
|
||||
ret = RTN_UNICAST;
|
||||
- rcu_read_lock();
|
||||
if (!fib_table_lookup(local_table, &fl4, &res, FIB_LOOKUP_NOREF)) {
|
||||
if (!dev || dev == res.fi->fib_dev)
|
||||
ret = res.type;
|
||||
}
|
||||
- rcu_read_unlock();
|
||||
}
|
||||
+
|
||||
+ rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -923,7 +922,7 @@ no_promotions:
|
||||
#undef BRD1_OK
|
||||
}
|
||||
|
||||
-static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb)
|
||||
+static void nl_fib_lookup(struct net *net, struct fib_result_nl *frn)
|
||||
{
|
||||
|
||||
struct fib_result res;
|
||||
@@ -933,6 +932,11 @@ static void nl_fib_lookup(struct fib_res
|
||||
.flowi4_tos = frn->fl_tos,
|
||||
.flowi4_scope = frn->fl_scope,
|
||||
};
|
||||
+ struct fib_table *tb;
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+
|
||||
+ tb = fib_get_table(net, frn->tb_id_in);
|
||||
|
||||
frn->err = -ENOENT;
|
||||
if (tb) {
|
||||
@@ -949,6 +953,8 @@ static void nl_fib_lookup(struct fib_res
|
||||
}
|
||||
local_bh_enable();
|
||||
}
|
||||
+
|
||||
+ rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void nl_fib_input(struct sk_buff *skb)
|
||||
@@ -956,7 +962,6 @@ static void nl_fib_input(struct sk_buff
|
||||
struct net *net;
|
||||
struct fib_result_nl *frn;
|
||||
struct nlmsghdr *nlh;
|
||||
- struct fib_table *tb;
|
||||
u32 portid;
|
||||
|
||||
net = sock_net(skb->sk);
|
||||
@@ -972,9 +977,7 @@ static void nl_fib_input(struct sk_buff
|
||||
nlh = nlmsg_hdr(skb);
|
||||
|
||||
frn = (struct fib_result_nl *) nlmsg_data(nlh);
|
||||
- tb = fib_get_table(net, frn->tb_id_in);
|
||||
-
|
||||
- nl_fib_lookup(frn, tb);
|
||||
+ nl_fib_lookup(net, frn);
|
||||
|
||||
portid = NETLINK_CB(skb).portid; /* netlink portid */
|
||||
NETLINK_CB(skb).portid = 0; /* from kernel */
|
||||
--- a/net/ipv4/fib_rules.c
|
||||
+++ b/net/ipv4/fib_rules.c
|
||||
@@ -81,27 +81,25 @@ static int fib4_rule_action(struct fib_r
|
||||
break;
|
||||
|
||||
case FR_ACT_UNREACHABLE:
|
||||
- err = -ENETUNREACH;
|
||||
- goto errout;
|
||||
+ return -ENETUNREACH;
|
||||
|
||||
case FR_ACT_PROHIBIT:
|
||||
- err = -EACCES;
|
||||
- goto errout;
|
||||
+ return -EACCES;
|
||||
|
||||
case FR_ACT_BLACKHOLE:
|
||||
default:
|
||||
- err = -EINVAL;
|
||||
- goto errout;
|
||||
+ return -EINVAL;
|
||||
}
|
||||
|
||||
+ rcu_read_lock();
|
||||
+
|
||||
tbl = fib_get_table(rule->fr_net, rule->table);
|
||||
- if (!tbl)
|
||||
- goto errout;
|
||||
+ if (tbl)
|
||||
+ err = fib_table_lookup(tbl, &flp->u.ip4,
|
||||
+ (struct fib_result *)arg->result,
|
||||
+ arg->flags);
|
||||
|
||||
- err = fib_table_lookup(tbl, &flp->u.ip4, (struct fib_result *) arg->result, arg->flags);
|
||||
- if (err > 0)
|
||||
- err = -EAGAIN;
|
||||
-errout:
|
||||
+ rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -1181,72 +1181,6 @@ err:
|
||||
return err;
|
||||
}
|
||||
|
||||
-/* should be called with rcu_read_lock */
|
||||
-static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l,
|
||||
- t_key key, const struct flowi4 *flp,
|
||||
- struct fib_result *res, int fib_flags)
|
||||
-{
|
||||
- struct leaf_info *li;
|
||||
- struct hlist_head *hhead = &l->list;
|
||||
-
|
||||
- hlist_for_each_entry_rcu(li, hhead, hlist) {
|
||||
- struct fib_alias *fa;
|
||||
-
|
||||
- if (l->key != (key & li->mask_plen))
|
||||
- continue;
|
||||
-
|
||||
- list_for_each_entry_rcu(fa, &li->falh, fa_list) {
|
||||
- struct fib_info *fi = fa->fa_info;
|
||||
- int nhsel, err;
|
||||
-
|
||||
- if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos)
|
||||
- continue;
|
||||
- if (fi->fib_dead)
|
||||
- continue;
|
||||
- if (fa->fa_info->fib_scope < flp->flowi4_scope)
|
||||
- continue;
|
||||
- fib_alias_accessed(fa);
|
||||
- err = fib_props[fa->fa_type].error;
|
||||
- if (unlikely(err < 0)) {
|
||||
-#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- this_cpu_inc(t->stats->semantic_match_passed);
|
||||
-#endif
|
||||
- return err;
|
||||
- }
|
||||
- if (fi->fib_flags & RTNH_F_DEAD)
|
||||
- continue;
|
||||
- for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
|
||||
- const struct fib_nh *nh = &fi->fib_nh[nhsel];
|
||||
-
|
||||
- if (nh->nh_flags & RTNH_F_DEAD)
|
||||
- continue;
|
||||
- if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif)
|
||||
- continue;
|
||||
-
|
||||
-#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- this_cpu_inc(t->stats->semantic_match_passed);
|
||||
-#endif
|
||||
- res->prefixlen = li->plen;
|
||||
- res->nh_sel = nhsel;
|
||||
- res->type = fa->fa_type;
|
||||
- res->scope = fi->fib_scope;
|
||||
- res->fi = fi;
|
||||
- res->table = tb;
|
||||
- res->fa_head = &li->falh;
|
||||
- if (!(fib_flags & FIB_LOOKUP_NOREF))
|
||||
- atomic_inc(&fi->fib_clntref);
|
||||
- return 0;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
-#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- this_cpu_inc(t->stats->semantic_match_miss);
|
||||
-#endif
|
||||
- }
|
||||
-
|
||||
- return 1;
|
||||
-}
|
||||
-
|
||||
static inline t_key prefix_mismatch(t_key key, struct tnode *n)
|
||||
{
|
||||
t_key prefix = n->key;
|
||||
@@ -1254,6 +1188,7 @@ static inline t_key prefix_mismatch(t_ke
|
||||
return (key ^ prefix) & (prefix | -prefix);
|
||||
}
|
||||
|
||||
+/* should be called with rcu_read_lock */
|
||||
int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
|
||||
struct fib_result *res, int fib_flags)
|
||||
{
|
||||
@@ -1263,14 +1198,12 @@ int fib_table_lookup(struct fib_table *t
|
||||
#endif
|
||||
const t_key key = ntohl(flp->daddr);
|
||||
struct tnode *n, *pn;
|
||||
+ struct leaf_info *li;
|
||||
t_key cindex;
|
||||
- int ret = 1;
|
||||
-
|
||||
- rcu_read_lock();
|
||||
|
||||
n = rcu_dereference(t->trie);
|
||||
if (!n)
|
||||
- goto failed;
|
||||
+ return -EAGAIN;
|
||||
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
this_cpu_inc(stats->gets);
|
||||
@@ -1350,7 +1283,7 @@ backtrace:
|
||||
|
||||
pn = node_parent_rcu(pn);
|
||||
if (unlikely(!pn))
|
||||
- goto failed;
|
||||
+ return -EAGAIN;
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
this_cpu_inc(stats->backtrack);
|
||||
#endif
|
||||
@@ -1368,12 +1301,62 @@ backtrace:
|
||||
|
||||
found:
|
||||
/* Step 3: Process the leaf, if that fails fall back to backtracing */
|
||||
- ret = check_leaf(tb, t, n, key, flp, res, fib_flags);
|
||||
- if (unlikely(ret > 0))
|
||||
- goto backtrace;
|
||||
-failed:
|
||||
- rcu_read_unlock();
|
||||
- return ret;
|
||||
+ hlist_for_each_entry_rcu(li, &n->list, hlist) {
|
||||
+ struct fib_alias *fa;
|
||||
+
|
||||
+ if ((key ^ n->key) & li->mask_plen)
|
||||
+ continue;
|
||||
+
|
||||
+ list_for_each_entry_rcu(fa, &li->falh, fa_list) {
|
||||
+ struct fib_info *fi = fa->fa_info;
|
||||
+ int nhsel, err;
|
||||
+
|
||||
+ if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos)
|
||||
+ continue;
|
||||
+ if (fi->fib_dead)
|
||||
+ continue;
|
||||
+ if (fa->fa_info->fib_scope < flp->flowi4_scope)
|
||||
+ continue;
|
||||
+ fib_alias_accessed(fa);
|
||||
+ err = fib_props[fa->fa_type].error;
|
||||
+ if (unlikely(err < 0)) {
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ this_cpu_inc(stats->semantic_match_passed);
|
||||
+#endif
|
||||
+ return err;
|
||||
+ }
|
||||
+ if (fi->fib_flags & RTNH_F_DEAD)
|
||||
+ continue;
|
||||
+ for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
|
||||
+ const struct fib_nh *nh = &fi->fib_nh[nhsel];
|
||||
+
|
||||
+ if (nh->nh_flags & RTNH_F_DEAD)
|
||||
+ continue;
|
||||
+ if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif)
|
||||
+ continue;
|
||||
+
|
||||
+ if (!(fib_flags & FIB_LOOKUP_NOREF))
|
||||
+ atomic_inc(&fi->fib_clntref);
|
||||
+
|
||||
+ res->prefixlen = li->plen;
|
||||
+ res->nh_sel = nhsel;
|
||||
+ res->type = fa->fa_type;
|
||||
+ res->scope = fi->fib_scope;
|
||||
+ res->fi = fi;
|
||||
+ res->table = tb;
|
||||
+ res->fa_head = &li->falh;
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ this_cpu_inc(stats->semantic_match_passed);
|
||||
+#endif
|
||||
+ return err;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ this_cpu_inc(stats->semantic_match_miss);
|
||||
+#endif
|
||||
+ }
|
||||
+ goto backtrace;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fib_table_lookup);
|
||||
|
|
@ -1,345 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:31 -0800
|
||||
Subject: [PATCH] fib_trie: Move resize to after inflate/halve
|
||||
|
||||
This change consists of a cut/paste of resize to behind inflate and halve
|
||||
so that I could remove the two function prototypes.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -149,8 +149,6 @@ struct trie {
|
||||
static void tnode_put_child_reorg(struct tnode *tn, unsigned long i,
|
||||
struct tnode *n, int wasfull);
|
||||
static struct tnode *resize(struct trie *t, struct tnode *tn);
|
||||
-static struct tnode *inflate(struct trie *t, struct tnode *tn);
|
||||
-static struct tnode *halve(struct trie *t, struct tnode *tn);
|
||||
/* tnodes to free after resize(); protected by RTNL */
|
||||
static struct callback_head *tnode_free_head;
|
||||
static size_t tnode_free_size;
|
||||
@@ -447,161 +445,6 @@ static void put_child_root(struct tnode
|
||||
rcu_assign_pointer(t->trie, n);
|
||||
}
|
||||
|
||||
-#define MAX_WORK 10
|
||||
-static struct tnode *resize(struct trie *t, struct tnode *tn)
|
||||
-{
|
||||
- struct tnode *old_tn, *n = NULL;
|
||||
- int inflate_threshold_use;
|
||||
- int halve_threshold_use;
|
||||
- int max_work;
|
||||
-
|
||||
- if (!tn)
|
||||
- return NULL;
|
||||
-
|
||||
- pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n",
|
||||
- tn, inflate_threshold, halve_threshold);
|
||||
-
|
||||
- /* No children */
|
||||
- if (tn->empty_children > (tnode_child_length(tn) - 1))
|
||||
- goto no_children;
|
||||
-
|
||||
- /* One child */
|
||||
- if (tn->empty_children == (tnode_child_length(tn) - 1))
|
||||
- goto one_child;
|
||||
- /*
|
||||
- * Double as long as the resulting node has a number of
|
||||
- * nonempty nodes that are above the threshold.
|
||||
- */
|
||||
-
|
||||
- /*
|
||||
- * From "Implementing a dynamic compressed trie" by Stefan Nilsson of
|
||||
- * the Helsinki University of Technology and Matti Tikkanen of Nokia
|
||||
- * Telecommunications, page 6:
|
||||
- * "A node is doubled if the ratio of non-empty children to all
|
||||
- * children in the *doubled* node is at least 'high'."
|
||||
- *
|
||||
- * 'high' in this instance is the variable 'inflate_threshold'. It
|
||||
- * is expressed as a percentage, so we multiply it with
|
||||
- * tnode_child_length() and instead of multiplying by 2 (since the
|
||||
- * child array will be doubled by inflate()) and multiplying
|
||||
- * the left-hand side by 100 (to handle the percentage thing) we
|
||||
- * multiply the left-hand side by 50.
|
||||
- *
|
||||
- * The left-hand side may look a bit weird: tnode_child_length(tn)
|
||||
- * - tn->empty_children is of course the number of non-null children
|
||||
- * in the current node. tn->full_children is the number of "full"
|
||||
- * children, that is non-null tnodes with a skip value of 0.
|
||||
- * All of those will be doubled in the resulting inflated tnode, so
|
||||
- * we just count them one extra time here.
|
||||
- *
|
||||
- * A clearer way to write this would be:
|
||||
- *
|
||||
- * to_be_doubled = tn->full_children;
|
||||
- * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children -
|
||||
- * tn->full_children;
|
||||
- *
|
||||
- * new_child_length = tnode_child_length(tn) * 2;
|
||||
- *
|
||||
- * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) /
|
||||
- * new_child_length;
|
||||
- * if (new_fill_factor >= inflate_threshold)
|
||||
- *
|
||||
- * ...and so on, tho it would mess up the while () loop.
|
||||
- *
|
||||
- * anyway,
|
||||
- * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >=
|
||||
- * inflate_threshold
|
||||
- *
|
||||
- * avoid a division:
|
||||
- * 100 * (not_to_be_doubled + 2*to_be_doubled) >=
|
||||
- * inflate_threshold * new_child_length
|
||||
- *
|
||||
- * expand not_to_be_doubled and to_be_doubled, and shorten:
|
||||
- * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
- * tn->full_children) >= inflate_threshold * new_child_length
|
||||
- *
|
||||
- * expand new_child_length:
|
||||
- * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
- * tn->full_children) >=
|
||||
- * inflate_threshold * tnode_child_length(tn) * 2
|
||||
- *
|
||||
- * shorten again:
|
||||
- * 50 * (tn->full_children + tnode_child_length(tn) -
|
||||
- * tn->empty_children) >= inflate_threshold *
|
||||
- * tnode_child_length(tn)
|
||||
- *
|
||||
- */
|
||||
-
|
||||
- /* Keep root node larger */
|
||||
-
|
||||
- if (!node_parent(tn)) {
|
||||
- inflate_threshold_use = inflate_threshold_root;
|
||||
- halve_threshold_use = halve_threshold_root;
|
||||
- } else {
|
||||
- inflate_threshold_use = inflate_threshold;
|
||||
- halve_threshold_use = halve_threshold;
|
||||
- }
|
||||
-
|
||||
- max_work = MAX_WORK;
|
||||
- while ((tn->full_children > 0 && max_work-- &&
|
||||
- 50 * (tn->full_children + tnode_child_length(tn)
|
||||
- - tn->empty_children)
|
||||
- >= inflate_threshold_use * tnode_child_length(tn))) {
|
||||
-
|
||||
- old_tn = tn;
|
||||
- tn = inflate(t, tn);
|
||||
-
|
||||
- if (IS_ERR(tn)) {
|
||||
- tn = old_tn;
|
||||
-#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- this_cpu_inc(t->stats->resize_node_skipped);
|
||||
-#endif
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- /* Return if at least one inflate is run */
|
||||
- if (max_work != MAX_WORK)
|
||||
- return tn;
|
||||
-
|
||||
- /*
|
||||
- * Halve as long as the number of empty children in this
|
||||
- * node is above threshold.
|
||||
- */
|
||||
-
|
||||
- max_work = MAX_WORK;
|
||||
- while (tn->bits > 1 && max_work-- &&
|
||||
- 100 * (tnode_child_length(tn) - tn->empty_children) <
|
||||
- halve_threshold_use * tnode_child_length(tn)) {
|
||||
-
|
||||
- old_tn = tn;
|
||||
- tn = halve(t, tn);
|
||||
- if (IS_ERR(tn)) {
|
||||
- tn = old_tn;
|
||||
-#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
- this_cpu_inc(t->stats->resize_node_skipped);
|
||||
-#endif
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
-
|
||||
- /* Only one child remains */
|
||||
- if (tn->empty_children == (tnode_child_length(tn) - 1)) {
|
||||
- unsigned long i;
|
||||
-one_child:
|
||||
- for (i = tnode_child_length(tn); !n && i;)
|
||||
- n = tnode_get_child(tn, --i);
|
||||
-no_children:
|
||||
- /* compress one level */
|
||||
- node_set_parent(n, NULL);
|
||||
- tnode_free_safe(tn);
|
||||
- return n;
|
||||
- }
|
||||
- return tn;
|
||||
-}
|
||||
-
|
||||
-
|
||||
static void tnode_clean_free(struct tnode *tn)
|
||||
{
|
||||
struct tnode *tofree;
|
||||
@@ -804,6 +647,160 @@ nomem:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
+#define MAX_WORK 10
|
||||
+static struct tnode *resize(struct trie *t, struct tnode *tn)
|
||||
+{
|
||||
+ struct tnode *old_tn, *n = NULL;
|
||||
+ int inflate_threshold_use;
|
||||
+ int halve_threshold_use;
|
||||
+ int max_work;
|
||||
+
|
||||
+ if (!tn)
|
||||
+ return NULL;
|
||||
+
|
||||
+ pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n",
|
||||
+ tn, inflate_threshold, halve_threshold);
|
||||
+
|
||||
+ /* No children */
|
||||
+ if (tn->empty_children > (tnode_child_length(tn) - 1))
|
||||
+ goto no_children;
|
||||
+
|
||||
+ /* One child */
|
||||
+ if (tn->empty_children == (tnode_child_length(tn) - 1))
|
||||
+ goto one_child;
|
||||
+ /*
|
||||
+ * Double as long as the resulting node has a number of
|
||||
+ * nonempty nodes that are above the threshold.
|
||||
+ */
|
||||
+
|
||||
+ /*
|
||||
+ * From "Implementing a dynamic compressed trie" by Stefan Nilsson of
|
||||
+ * the Helsinki University of Technology and Matti Tikkanen of Nokia
|
||||
+ * Telecommunications, page 6:
|
||||
+ * "A node is doubled if the ratio of non-empty children to all
|
||||
+ * children in the *doubled* node is at least 'high'."
|
||||
+ *
|
||||
+ * 'high' in this instance is the variable 'inflate_threshold'. It
|
||||
+ * is expressed as a percentage, so we multiply it with
|
||||
+ * tnode_child_length() and instead of multiplying by 2 (since the
|
||||
+ * child array will be doubled by inflate()) and multiplying
|
||||
+ * the left-hand side by 100 (to handle the percentage thing) we
|
||||
+ * multiply the left-hand side by 50.
|
||||
+ *
|
||||
+ * The left-hand side may look a bit weird: tnode_child_length(tn)
|
||||
+ * - tn->empty_children is of course the number of non-null children
|
||||
+ * in the current node. tn->full_children is the number of "full"
|
||||
+ * children, that is non-null tnodes with a skip value of 0.
|
||||
+ * All of those will be doubled in the resulting inflated tnode, so
|
||||
+ * we just count them one extra time here.
|
||||
+ *
|
||||
+ * A clearer way to write this would be:
|
||||
+ *
|
||||
+ * to_be_doubled = tn->full_children;
|
||||
+ * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children -
|
||||
+ * tn->full_children;
|
||||
+ *
|
||||
+ * new_child_length = tnode_child_length(tn) * 2;
|
||||
+ *
|
||||
+ * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) /
|
||||
+ * new_child_length;
|
||||
+ * if (new_fill_factor >= inflate_threshold)
|
||||
+ *
|
||||
+ * ...and so on, tho it would mess up the while () loop.
|
||||
+ *
|
||||
+ * anyway,
|
||||
+ * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >=
|
||||
+ * inflate_threshold
|
||||
+ *
|
||||
+ * avoid a division:
|
||||
+ * 100 * (not_to_be_doubled + 2*to_be_doubled) >=
|
||||
+ * inflate_threshold * new_child_length
|
||||
+ *
|
||||
+ * expand not_to_be_doubled and to_be_doubled, and shorten:
|
||||
+ * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
+ * tn->full_children) >= inflate_threshold * new_child_length
|
||||
+ *
|
||||
+ * expand new_child_length:
|
||||
+ * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
+ * tn->full_children) >=
|
||||
+ * inflate_threshold * tnode_child_length(tn) * 2
|
||||
+ *
|
||||
+ * shorten again:
|
||||
+ * 50 * (tn->full_children + tnode_child_length(tn) -
|
||||
+ * tn->empty_children) >= inflate_threshold *
|
||||
+ * tnode_child_length(tn)
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+ /* Keep root node larger */
|
||||
+
|
||||
+ if (!node_parent(tn)) {
|
||||
+ inflate_threshold_use = inflate_threshold_root;
|
||||
+ halve_threshold_use = halve_threshold_root;
|
||||
+ } else {
|
||||
+ inflate_threshold_use = inflate_threshold;
|
||||
+ halve_threshold_use = halve_threshold;
|
||||
+ }
|
||||
+
|
||||
+ max_work = MAX_WORK;
|
||||
+ while ((tn->full_children > 0 && max_work-- &&
|
||||
+ 50 * (tn->full_children + tnode_child_length(tn)
|
||||
+ - tn->empty_children)
|
||||
+ >= inflate_threshold_use * tnode_child_length(tn))) {
|
||||
+
|
||||
+ old_tn = tn;
|
||||
+ tn = inflate(t, tn);
|
||||
+
|
||||
+ if (IS_ERR(tn)) {
|
||||
+ tn = old_tn;
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ this_cpu_inc(t->stats->resize_node_skipped);
|
||||
+#endif
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Return if at least one inflate is run */
|
||||
+ if (max_work != MAX_WORK)
|
||||
+ return tn;
|
||||
+
|
||||
+ /*
|
||||
+ * Halve as long as the number of empty children in this
|
||||
+ * node is above threshold.
|
||||
+ */
|
||||
+
|
||||
+ max_work = MAX_WORK;
|
||||
+ while (tn->bits > 1 && max_work-- &&
|
||||
+ 100 * (tnode_child_length(tn) - tn->empty_children) <
|
||||
+ halve_threshold_use * tnode_child_length(tn)) {
|
||||
+
|
||||
+ old_tn = tn;
|
||||
+ tn = halve(t, tn);
|
||||
+ if (IS_ERR(tn)) {
|
||||
+ tn = old_tn;
|
||||
+#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
+ this_cpu_inc(t->stats->resize_node_skipped);
|
||||
+#endif
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ /* Only one child remains */
|
||||
+ if (tn->empty_children == (tnode_child_length(tn) - 1)) {
|
||||
+ unsigned long i;
|
||||
+one_child:
|
||||
+ for (i = tnode_child_length(tn); !n && i;)
|
||||
+ n = tnode_get_child(tn, --i);
|
||||
+no_children:
|
||||
+ /* compress one level */
|
||||
+ node_set_parent(n, NULL);
|
||||
+ tnode_free_safe(tn);
|
||||
+ return n;
|
||||
+ }
|
||||
+ return tn;
|
||||
+}
|
||||
+
|
||||
/* readside must use rcu_read_lock currently dump routines
|
||||
via get_fa_head and dump */
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:37 -0800
|
||||
Subject: [PATCH] fib_trie: Add functions should_inflate and should_halve
|
||||
|
||||
This change pulls the logic for if we should inflate/halve the nodes out
|
||||
into separate functions. It also addresses what I believe is a bug where 1
|
||||
full node is all that is needed to keep a node from ever being halved.
|
||||
|
||||
Simple script to reproduce the issue:
|
||||
modprobe dummy; ifconfig dummy0 up
|
||||
for i in `seq 0 255`; do ifconfig dummy0:$i 10.0.${i}.1/24 up; done
|
||||
ifconfig dummy0:256 10.0.255.33/16 up
|
||||
for i in `seq 0 254`; do ifconfig dummy0:$i down; done
|
||||
|
||||
Results from /proc/net/fib_triestat
|
||||
Before:
|
||||
Local:
|
||||
Aver depth: 3.00
|
||||
Max depth: 4
|
||||
Leaves: 17
|
||||
Prefixes: 18
|
||||
Internal nodes: 11
|
||||
1: 8 2: 2 10: 1
|
||||
Pointers: 1048
|
||||
Null ptrs: 1021
|
||||
Total size: 11 kB
|
||||
After:
|
||||
Local:
|
||||
Aver depth: 3.41
|
||||
Max depth: 5
|
||||
Leaves: 17
|
||||
Prefixes: 18
|
||||
Internal nodes: 12
|
||||
1: 8 2: 3 3: 1
|
||||
Pointers: 36
|
||||
Null ptrs: 8
|
||||
Total size: 3 kB
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -647,12 +647,94 @@ nomem:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
+/* From "Implementing a dynamic compressed trie" by Stefan Nilsson of
|
||||
+ * the Helsinki University of Technology and Matti Tikkanen of Nokia
|
||||
+ * Telecommunications, page 6:
|
||||
+ * "A node is doubled if the ratio of non-empty children to all
|
||||
+ * children in the *doubled* node is at least 'high'."
|
||||
+ *
|
||||
+ * 'high' in this instance is the variable 'inflate_threshold'. It
|
||||
+ * is expressed as a percentage, so we multiply it with
|
||||
+ * tnode_child_length() and instead of multiplying by 2 (since the
|
||||
+ * child array will be doubled by inflate()) and multiplying
|
||||
+ * the left-hand side by 100 (to handle the percentage thing) we
|
||||
+ * multiply the left-hand side by 50.
|
||||
+ *
|
||||
+ * The left-hand side may look a bit weird: tnode_child_length(tn)
|
||||
+ * - tn->empty_children is of course the number of non-null children
|
||||
+ * in the current node. tn->full_children is the number of "full"
|
||||
+ * children, that is non-null tnodes with a skip value of 0.
|
||||
+ * All of those will be doubled in the resulting inflated tnode, so
|
||||
+ * we just count them one extra time here.
|
||||
+ *
|
||||
+ * A clearer way to write this would be:
|
||||
+ *
|
||||
+ * to_be_doubled = tn->full_children;
|
||||
+ * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children -
|
||||
+ * tn->full_children;
|
||||
+ *
|
||||
+ * new_child_length = tnode_child_length(tn) * 2;
|
||||
+ *
|
||||
+ * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) /
|
||||
+ * new_child_length;
|
||||
+ * if (new_fill_factor >= inflate_threshold)
|
||||
+ *
|
||||
+ * ...and so on, tho it would mess up the while () loop.
|
||||
+ *
|
||||
+ * anyway,
|
||||
+ * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >=
|
||||
+ * inflate_threshold
|
||||
+ *
|
||||
+ * avoid a division:
|
||||
+ * 100 * (not_to_be_doubled + 2*to_be_doubled) >=
|
||||
+ * inflate_threshold * new_child_length
|
||||
+ *
|
||||
+ * expand not_to_be_doubled and to_be_doubled, and shorten:
|
||||
+ * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
+ * tn->full_children) >= inflate_threshold * new_child_length
|
||||
+ *
|
||||
+ * expand new_child_length:
|
||||
+ * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
+ * tn->full_children) >=
|
||||
+ * inflate_threshold * tnode_child_length(tn) * 2
|
||||
+ *
|
||||
+ * shorten again:
|
||||
+ * 50 * (tn->full_children + tnode_child_length(tn) -
|
||||
+ * tn->empty_children) >= inflate_threshold *
|
||||
+ * tnode_child_length(tn)
|
||||
+ *
|
||||
+ */
|
||||
+static bool should_inflate(const struct tnode *tn)
|
||||
+{
|
||||
+ unsigned long used = tnode_child_length(tn);
|
||||
+ unsigned long threshold = used;
|
||||
+
|
||||
+ /* Keep root node larger */
|
||||
+ threshold *= node_parent(tn) ? inflate_threshold :
|
||||
+ inflate_threshold_root;
|
||||
+ used += tn->full_children;
|
||||
+ used -= tn->empty_children;
|
||||
+
|
||||
+ return tn->pos && ((50 * used) >= threshold);
|
||||
+}
|
||||
+
|
||||
+static bool should_halve(const struct tnode *tn)
|
||||
+{
|
||||
+ unsigned long used = tnode_child_length(tn);
|
||||
+ unsigned long threshold = used;
|
||||
+
|
||||
+ /* Keep root node larger */
|
||||
+ threshold *= node_parent(tn) ? halve_threshold :
|
||||
+ halve_threshold_root;
|
||||
+ used -= tn->empty_children;
|
||||
+
|
||||
+ return (tn->bits > 1) && ((100 * used) < threshold);
|
||||
+}
|
||||
+
|
||||
#define MAX_WORK 10
|
||||
static struct tnode *resize(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
struct tnode *old_tn, *n = NULL;
|
||||
- int inflate_threshold_use;
|
||||
- int halve_threshold_use;
|
||||
int max_work;
|
||||
|
||||
if (!tn)
|
||||
@@ -668,86 +750,12 @@ static struct tnode *resize(struct trie
|
||||
/* One child */
|
||||
if (tn->empty_children == (tnode_child_length(tn) - 1))
|
||||
goto one_child;
|
||||
- /*
|
||||
- * Double as long as the resulting node has a number of
|
||||
- * nonempty nodes that are above the threshold.
|
||||
- */
|
||||
|
||||
- /*
|
||||
- * From "Implementing a dynamic compressed trie" by Stefan Nilsson of
|
||||
- * the Helsinki University of Technology and Matti Tikkanen of Nokia
|
||||
- * Telecommunications, page 6:
|
||||
- * "A node is doubled if the ratio of non-empty children to all
|
||||
- * children in the *doubled* node is at least 'high'."
|
||||
- *
|
||||
- * 'high' in this instance is the variable 'inflate_threshold'. It
|
||||
- * is expressed as a percentage, so we multiply it with
|
||||
- * tnode_child_length() and instead of multiplying by 2 (since the
|
||||
- * child array will be doubled by inflate()) and multiplying
|
||||
- * the left-hand side by 100 (to handle the percentage thing) we
|
||||
- * multiply the left-hand side by 50.
|
||||
- *
|
||||
- * The left-hand side may look a bit weird: tnode_child_length(tn)
|
||||
- * - tn->empty_children is of course the number of non-null children
|
||||
- * in the current node. tn->full_children is the number of "full"
|
||||
- * children, that is non-null tnodes with a skip value of 0.
|
||||
- * All of those will be doubled in the resulting inflated tnode, so
|
||||
- * we just count them one extra time here.
|
||||
- *
|
||||
- * A clearer way to write this would be:
|
||||
- *
|
||||
- * to_be_doubled = tn->full_children;
|
||||
- * not_to_be_doubled = tnode_child_length(tn) - tn->empty_children -
|
||||
- * tn->full_children;
|
||||
- *
|
||||
- * new_child_length = tnode_child_length(tn) * 2;
|
||||
- *
|
||||
- * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) /
|
||||
- * new_child_length;
|
||||
- * if (new_fill_factor >= inflate_threshold)
|
||||
- *
|
||||
- * ...and so on, tho it would mess up the while () loop.
|
||||
- *
|
||||
- * anyway,
|
||||
- * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >=
|
||||
- * inflate_threshold
|
||||
- *
|
||||
- * avoid a division:
|
||||
- * 100 * (not_to_be_doubled + 2*to_be_doubled) >=
|
||||
- * inflate_threshold * new_child_length
|
||||
- *
|
||||
- * expand not_to_be_doubled and to_be_doubled, and shorten:
|
||||
- * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
- * tn->full_children) >= inflate_threshold * new_child_length
|
||||
- *
|
||||
- * expand new_child_length:
|
||||
- * 100 * (tnode_child_length(tn) - tn->empty_children +
|
||||
- * tn->full_children) >=
|
||||
- * inflate_threshold * tnode_child_length(tn) * 2
|
||||
- *
|
||||
- * shorten again:
|
||||
- * 50 * (tn->full_children + tnode_child_length(tn) -
|
||||
- * tn->empty_children) >= inflate_threshold *
|
||||
- * tnode_child_length(tn)
|
||||
- *
|
||||
+ /* Double as long as the resulting node has a number of
|
||||
+ * nonempty nodes that are above the threshold.
|
||||
*/
|
||||
-
|
||||
- /* Keep root node larger */
|
||||
-
|
||||
- if (!node_parent(tn)) {
|
||||
- inflate_threshold_use = inflate_threshold_root;
|
||||
- halve_threshold_use = halve_threshold_root;
|
||||
- } else {
|
||||
- inflate_threshold_use = inflate_threshold;
|
||||
- halve_threshold_use = halve_threshold;
|
||||
- }
|
||||
-
|
||||
max_work = MAX_WORK;
|
||||
- while ((tn->full_children > 0 && max_work-- &&
|
||||
- 50 * (tn->full_children + tnode_child_length(tn)
|
||||
- - tn->empty_children)
|
||||
- >= inflate_threshold_use * tnode_child_length(tn))) {
|
||||
-
|
||||
+ while (should_inflate(tn) && max_work--) {
|
||||
old_tn = tn;
|
||||
tn = inflate(t, tn);
|
||||
|
||||
@@ -764,16 +772,11 @@ static struct tnode *resize(struct trie
|
||||
if (max_work != MAX_WORK)
|
||||
return tn;
|
||||
|
||||
- /*
|
||||
- * Halve as long as the number of empty children in this
|
||||
+ /* Halve as long as the number of empty children in this
|
||||
* node is above threshold.
|
||||
*/
|
||||
-
|
||||
max_work = MAX_WORK;
|
||||
- while (tn->bits > 1 && max_work-- &&
|
||||
- 100 * (tnode_child_length(tn) - tn->empty_children) <
|
||||
- halve_threshold_use * tnode_child_length(tn)) {
|
||||
-
|
||||
+ while (should_halve(tn) && max_work--) {
|
||||
old_tn = tn;
|
||||
tn = halve(t, tn);
|
||||
if (IS_ERR(tn)) {
|
|
@ -1,336 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:43 -0800
|
||||
Subject: [PATCH] fib_trie: Push assignment of child to parent down into
|
||||
inflate/halve
|
||||
|
||||
This change makes it so that the assignment of the tnode to the parent is
|
||||
handled directly within whatever function is currently handling the node be
|
||||
it inflate, halve, or resize. By doing this we can avoid some of the need
|
||||
to set NULL pointers in the tree while we are resizing the subnodes.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -146,9 +146,7 @@ struct trie {
|
||||
#endif
|
||||
};
|
||||
|
||||
-static void tnode_put_child_reorg(struct tnode *tn, unsigned long i,
|
||||
- struct tnode *n, int wasfull);
|
||||
-static struct tnode *resize(struct trie *t, struct tnode *tn);
|
||||
+static void resize(struct trie *t, struct tnode *tn);
|
||||
/* tnodes to free after resize(); protected by RTNL */
|
||||
static struct callback_head *tnode_free_head;
|
||||
static size_t tnode_free_size;
|
||||
@@ -396,22 +394,13 @@ static inline int tnode_full(const struc
|
||||
return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n);
|
||||
}
|
||||
|
||||
-static inline void put_child(struct tnode *tn, unsigned long i,
|
||||
- struct tnode *n)
|
||||
-{
|
||||
- tnode_put_child_reorg(tn, i, n, -1);
|
||||
-}
|
||||
-
|
||||
- /*
|
||||
- * Add a child at position i overwriting the old value.
|
||||
- * Update the value of full_children and empty_children.
|
||||
- */
|
||||
-
|
||||
-static void tnode_put_child_reorg(struct tnode *tn, unsigned long i,
|
||||
- struct tnode *n, int wasfull)
|
||||
+/* Add a child at position i overwriting the old value.
|
||||
+ * Update the value of full_children and empty_children.
|
||||
+ */
|
||||
+static void put_child(struct tnode *tn, unsigned long i, struct tnode *n)
|
||||
{
|
||||
struct tnode *chi = rtnl_dereference(tn->child[i]);
|
||||
- int isfull;
|
||||
+ int isfull, wasfull;
|
||||
|
||||
BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
@@ -422,10 +411,9 @@ static void tnode_put_child_reorg(struct
|
||||
tn->empty_children--;
|
||||
|
||||
/* update fullChildren */
|
||||
- if (wasfull == -1)
|
||||
- wasfull = tnode_full(tn, chi);
|
||||
-
|
||||
+ wasfull = tnode_full(tn, chi);
|
||||
isfull = tnode_full(tn, n);
|
||||
+
|
||||
if (wasfull && !isfull)
|
||||
tn->full_children--;
|
||||
else if (!wasfull && isfull)
|
||||
@@ -458,9 +446,10 @@ static void tnode_clean_free(struct tnod
|
||||
node_free(tn);
|
||||
}
|
||||
|
||||
-static struct tnode *inflate(struct trie *t, struct tnode *oldtnode)
|
||||
+static int inflate(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
unsigned long olen = tnode_child_length(oldtnode);
|
||||
+ struct tnode *tp = node_parent(oldtnode);
|
||||
struct tnode *tn;
|
||||
unsigned long i;
|
||||
t_key m;
|
||||
@@ -468,9 +457,8 @@ static struct tnode *inflate(struct trie
|
||||
pr_debug("In inflate\n");
|
||||
|
||||
tn = tnode_new(oldtnode->key, oldtnode->pos - 1, oldtnode->bits + 1);
|
||||
-
|
||||
if (!tn)
|
||||
- return ERR_PTR(-ENOMEM);
|
||||
+ return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Preallocate and store tnodes before the actual work so we
|
||||
@@ -564,30 +552,36 @@ static struct tnode *inflate(struct trie
|
||||
put_child(left, j, rtnl_dereference(inode->child[j]));
|
||||
put_child(right, j, rtnl_dereference(inode->child[j + size]));
|
||||
}
|
||||
- put_child(tn, 2*i, resize(t, left));
|
||||
- put_child(tn, 2*i+1, resize(t, right));
|
||||
+
|
||||
+ put_child(tn, 2 * i, left);
|
||||
+ put_child(tn, 2 * i + 1, right);
|
||||
|
||||
tnode_free_safe(inode);
|
||||
+
|
||||
+ resize(t, left);
|
||||
+ resize(t, right);
|
||||
}
|
||||
+
|
||||
+ put_child_root(tp, t, tn->key, tn);
|
||||
tnode_free_safe(oldtnode);
|
||||
- return tn;
|
||||
+ return 0;
|
||||
nomem:
|
||||
tnode_clean_free(tn);
|
||||
- return ERR_PTR(-ENOMEM);
|
||||
+ return -ENOMEM;
|
||||
}
|
||||
|
||||
-static struct tnode *halve(struct trie *t, struct tnode *oldtnode)
|
||||
+static int halve(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
unsigned long olen = tnode_child_length(oldtnode);
|
||||
+ struct tnode *tp = node_parent(oldtnode);
|
||||
struct tnode *tn, *left, *right;
|
||||
int i;
|
||||
|
||||
pr_debug("In halve\n");
|
||||
|
||||
tn = tnode_new(oldtnode->key, oldtnode->pos + 1, oldtnode->bits - 1);
|
||||
-
|
||||
if (!tn)
|
||||
- return ERR_PTR(-ENOMEM);
|
||||
+ return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Preallocate and store tnodes before the actual work so we
|
||||
@@ -606,8 +600,10 @@ static struct tnode *halve(struct trie *
|
||||
|
||||
newn = tnode_new(left->key, oldtnode->pos, 1);
|
||||
|
||||
- if (!newn)
|
||||
- goto nomem;
|
||||
+ if (!newn) {
|
||||
+ tnode_clean_free(tn);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
|
||||
put_child(tn, i/2, newn);
|
||||
}
|
||||
@@ -635,16 +631,18 @@ static struct tnode *halve(struct trie *
|
||||
|
||||
/* Two nonempty children */
|
||||
newBinNode = tnode_get_child(tn, i/2);
|
||||
- put_child(tn, i/2, NULL);
|
||||
put_child(newBinNode, 0, left);
|
||||
put_child(newBinNode, 1, right);
|
||||
- put_child(tn, i/2, resize(t, newBinNode));
|
||||
+
|
||||
+ put_child(tn, i / 2, newBinNode);
|
||||
+
|
||||
+ resize(t, newBinNode);
|
||||
}
|
||||
+
|
||||
+ put_child_root(tp, t, tn->key, tn);
|
||||
tnode_free_safe(oldtnode);
|
||||
- return tn;
|
||||
-nomem:
|
||||
- tnode_clean_free(tn);
|
||||
- return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
/* From "Implementing a dynamic compressed trie" by Stefan Nilsson of
|
||||
@@ -704,45 +702,48 @@ nomem:
|
||||
* tnode_child_length(tn)
|
||||
*
|
||||
*/
|
||||
-static bool should_inflate(const struct tnode *tn)
|
||||
+static bool should_inflate(const struct tnode *tp, const struct tnode *tn)
|
||||
{
|
||||
unsigned long used = tnode_child_length(tn);
|
||||
unsigned long threshold = used;
|
||||
|
||||
/* Keep root node larger */
|
||||
- threshold *= node_parent(tn) ? inflate_threshold :
|
||||
- inflate_threshold_root;
|
||||
+ threshold *= tp ? inflate_threshold : inflate_threshold_root;
|
||||
used += tn->full_children;
|
||||
used -= tn->empty_children;
|
||||
|
||||
return tn->pos && ((50 * used) >= threshold);
|
||||
}
|
||||
|
||||
-static bool should_halve(const struct tnode *tn)
|
||||
+static bool should_halve(const struct tnode *tp, const struct tnode *tn)
|
||||
{
|
||||
unsigned long used = tnode_child_length(tn);
|
||||
unsigned long threshold = used;
|
||||
|
||||
/* Keep root node larger */
|
||||
- threshold *= node_parent(tn) ? halve_threshold :
|
||||
- halve_threshold_root;
|
||||
+ threshold *= tp ? halve_threshold : halve_threshold_root;
|
||||
used -= tn->empty_children;
|
||||
|
||||
return (tn->bits > 1) && ((100 * used) < threshold);
|
||||
}
|
||||
|
||||
#define MAX_WORK 10
|
||||
-static struct tnode *resize(struct trie *t, struct tnode *tn)
|
||||
+static void resize(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
- struct tnode *old_tn, *n = NULL;
|
||||
+ struct tnode *tp = node_parent(tn), *n = NULL;
|
||||
+ struct tnode __rcu **cptr;
|
||||
int max_work;
|
||||
|
||||
- if (!tn)
|
||||
- return NULL;
|
||||
-
|
||||
pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n",
|
||||
tn, inflate_threshold, halve_threshold);
|
||||
|
||||
+ /* track the tnode via the pointer from the parent instead of
|
||||
+ * doing it ourselves. This way we can let RCU fully do its
|
||||
+ * thing without us interfering
|
||||
+ */
|
||||
+ cptr = tp ? &tp->child[get_index(tn->key, tp)] : &t->trie;
|
||||
+ BUG_ON(tn != rtnl_dereference(*cptr));
|
||||
+
|
||||
/* No children */
|
||||
if (tn->empty_children > (tnode_child_length(tn) - 1))
|
||||
goto no_children;
|
||||
@@ -755,39 +756,35 @@ static struct tnode *resize(struct trie
|
||||
* nonempty nodes that are above the threshold.
|
||||
*/
|
||||
max_work = MAX_WORK;
|
||||
- while (should_inflate(tn) && max_work--) {
|
||||
- old_tn = tn;
|
||||
- tn = inflate(t, tn);
|
||||
-
|
||||
- if (IS_ERR(tn)) {
|
||||
- tn = old_tn;
|
||||
+ while (should_inflate(tp, tn) && max_work--) {
|
||||
+ if (inflate(t, tn)) {
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
this_cpu_inc(t->stats->resize_node_skipped);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
+
|
||||
+ tn = rtnl_dereference(*cptr);
|
||||
}
|
||||
|
||||
/* Return if at least one inflate is run */
|
||||
if (max_work != MAX_WORK)
|
||||
- return tn;
|
||||
+ return;
|
||||
|
||||
/* Halve as long as the number of empty children in this
|
||||
* node is above threshold.
|
||||
*/
|
||||
max_work = MAX_WORK;
|
||||
- while (should_halve(tn) && max_work--) {
|
||||
- old_tn = tn;
|
||||
- tn = halve(t, tn);
|
||||
- if (IS_ERR(tn)) {
|
||||
- tn = old_tn;
|
||||
+ while (should_halve(tp, tn) && max_work--) {
|
||||
+ if (halve(t, tn)) {
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
this_cpu_inc(t->stats->resize_node_skipped);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
- }
|
||||
|
||||
+ tn = rtnl_dereference(*cptr);
|
||||
+ }
|
||||
|
||||
/* Only one child remains */
|
||||
if (tn->empty_children == (tnode_child_length(tn) - 1)) {
|
||||
@@ -797,11 +794,12 @@ one_child:
|
||||
n = tnode_get_child(tn, --i);
|
||||
no_children:
|
||||
/* compress one level */
|
||||
- node_set_parent(n, NULL);
|
||||
+ put_child_root(tp, t, tn->key, n);
|
||||
+ node_set_parent(n, tp);
|
||||
+
|
||||
+ /* drop dead node */
|
||||
tnode_free_safe(tn);
|
||||
- return n;
|
||||
}
|
||||
- return tn;
|
||||
}
|
||||
|
||||
/* readside must use rcu_read_lock currently dump routines
|
||||
@@ -882,34 +880,19 @@ static struct tnode *fib_find_node(struc
|
||||
|
||||
static void trie_rebalance(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
- int wasfull;
|
||||
- t_key cindex, key;
|
||||
struct tnode *tp;
|
||||
|
||||
- key = tn->key;
|
||||
-
|
||||
- while (tn != NULL && (tp = node_parent(tn)) != NULL) {
|
||||
- cindex = get_index(key, tp);
|
||||
- wasfull = tnode_full(tp, tnode_get_child(tp, cindex));
|
||||
- tn = resize(t, tn);
|
||||
-
|
||||
- tnode_put_child_reorg(tp, cindex, tn, wasfull);
|
||||
-
|
||||
- tp = node_parent(tn);
|
||||
- if (!tp)
|
||||
- rcu_assign_pointer(t->trie, tn);
|
||||
+ while ((tp = node_parent(tn)) != NULL) {
|
||||
+ resize(t, tn);
|
||||
|
||||
tnode_free_flush();
|
||||
- if (!tp)
|
||||
- break;
|
||||
tn = tp;
|
||||
}
|
||||
|
||||
/* Handle last (top) tnode */
|
||||
if (IS_TNODE(tn))
|
||||
- tn = resize(t, tn);
|
||||
+ resize(t, tn);
|
||||
|
||||
- rcu_assign_pointer(t->trie, tn);
|
||||
tnode_free_flush();
|
||||
}
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:49 -0800
|
||||
Subject: [PATCH] fib_trie: Push tnode flushing down to inflate/halve
|
||||
|
||||
This change pushes the tnode freeing down into the inflate and halve
|
||||
functions. It makes more sense here as we have a better grasp of what is
|
||||
going on and when a given cluster of nodes is ready to be freed.
|
||||
|
||||
I believe this may address a bug in the freeing logic as well. For some
|
||||
reason if the freelist got to a certain size we would call
|
||||
synchronize_rcu(). I'm assuming that what they meant to do is call
|
||||
synchronize_rcu() after they had handed off that much memory via
|
||||
call_rcu(). As such that is what I have updated the behavior to be.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -147,8 +147,6 @@ struct trie {
|
||||
};
|
||||
|
||||
static void resize(struct trie *t, struct tnode *tn);
|
||||
-/* tnodes to free after resize(); protected by RTNL */
|
||||
-static struct callback_head *tnode_free_head;
|
||||
static size_t tnode_free_size;
|
||||
|
||||
/*
|
||||
@@ -307,32 +305,6 @@ static struct tnode *tnode_alloc(size_t
|
||||
return vzalloc(size);
|
||||
}
|
||||
|
||||
-static void tnode_free_safe(struct tnode *tn)
|
||||
-{
|
||||
- BUG_ON(IS_LEAF(tn));
|
||||
- tn->rcu.next = tnode_free_head;
|
||||
- tnode_free_head = &tn->rcu;
|
||||
-}
|
||||
-
|
||||
-static void tnode_free_flush(void)
|
||||
-{
|
||||
- struct callback_head *head;
|
||||
-
|
||||
- while ((head = tnode_free_head)) {
|
||||
- struct tnode *tn = container_of(head, struct tnode, rcu);
|
||||
-
|
||||
- tnode_free_head = head->next;
|
||||
- tnode_free_size += offsetof(struct tnode, child[1 << tn->bits]);
|
||||
-
|
||||
- node_free(tn);
|
||||
- }
|
||||
-
|
||||
- if (tnode_free_size >= PAGE_SIZE * sync_pages) {
|
||||
- tnode_free_size = 0;
|
||||
- synchronize_rcu();
|
||||
- }
|
||||
-}
|
||||
-
|
||||
static struct tnode *leaf_new(t_key key)
|
||||
{
|
||||
struct tnode *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL);
|
||||
@@ -433,17 +405,33 @@ static void put_child_root(struct tnode
|
||||
rcu_assign_pointer(t->trie, n);
|
||||
}
|
||||
|
||||
-static void tnode_clean_free(struct tnode *tn)
|
||||
+static inline void tnode_free_init(struct tnode *tn)
|
||||
{
|
||||
- struct tnode *tofree;
|
||||
- unsigned long i;
|
||||
+ tn->rcu.next = NULL;
|
||||
+}
|
||||
+
|
||||
+static inline void tnode_free_append(struct tnode *tn, struct tnode *n)
|
||||
+{
|
||||
+ n->rcu.next = tn->rcu.next;
|
||||
+ tn->rcu.next = &n->rcu;
|
||||
+}
|
||||
|
||||
- for (i = 0; i < tnode_child_length(tn); i++) {
|
||||
- tofree = tnode_get_child(tn, i);
|
||||
- if (tofree)
|
||||
- node_free(tofree);
|
||||
+static void tnode_free(struct tnode *tn)
|
||||
+{
|
||||
+ struct callback_head *head = &tn->rcu;
|
||||
+
|
||||
+ while (head) {
|
||||
+ head = head->next;
|
||||
+ tnode_free_size += offsetof(struct tnode, child[1 << tn->bits]);
|
||||
+ node_free(tn);
|
||||
+
|
||||
+ tn = container_of(head, struct tnode, rcu);
|
||||
+ }
|
||||
+
|
||||
+ if (tnode_free_size >= PAGE_SIZE * sync_pages) {
|
||||
+ tnode_free_size = 0;
|
||||
+ synchronize_rcu();
|
||||
}
|
||||
- node_free(tn);
|
||||
}
|
||||
|
||||
static int inflate(struct trie *t, struct tnode *oldtnode)
|
||||
@@ -476,20 +464,23 @@ static int inflate(struct trie *t, struc
|
||||
inode->bits - 1);
|
||||
if (!left)
|
||||
goto nomem;
|
||||
+ tnode_free_append(tn, left);
|
||||
|
||||
right = tnode_new(inode->key | m, inode->pos,
|
||||
inode->bits - 1);
|
||||
|
||||
- if (!right) {
|
||||
- node_free(left);
|
||||
+ if (!right)
|
||||
goto nomem;
|
||||
- }
|
||||
+ tnode_free_append(tn, right);
|
||||
|
||||
put_child(tn, 2*i, left);
|
||||
put_child(tn, 2*i+1, right);
|
||||
}
|
||||
}
|
||||
|
||||
+ /* prepare oldtnode to be freed */
|
||||
+ tnode_free_init(oldtnode);
|
||||
+
|
||||
for (i = 0; i < olen; i++) {
|
||||
struct tnode *inode = tnode_get_child(oldtnode, i);
|
||||
struct tnode *left, *right;
|
||||
@@ -505,12 +496,13 @@ static int inflate(struct trie *t, struc
|
||||
continue;
|
||||
}
|
||||
|
||||
+ /* drop the node in the old tnode free list */
|
||||
+ tnode_free_append(oldtnode, inode);
|
||||
+
|
||||
/* An internal node with two children */
|
||||
if (inode->bits == 1) {
|
||||
put_child(tn, 2*i, rtnl_dereference(inode->child[0]));
|
||||
put_child(tn, 2*i+1, rtnl_dereference(inode->child[1]));
|
||||
-
|
||||
- tnode_free_safe(inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -556,17 +548,19 @@ static int inflate(struct trie *t, struc
|
||||
put_child(tn, 2 * i, left);
|
||||
put_child(tn, 2 * i + 1, right);
|
||||
|
||||
- tnode_free_safe(inode);
|
||||
-
|
||||
+ /* resize child nodes */
|
||||
resize(t, left);
|
||||
resize(t, right);
|
||||
}
|
||||
|
||||
put_child_root(tp, t, tn->key, tn);
|
||||
- tnode_free_safe(oldtnode);
|
||||
+
|
||||
+ /* we completed without error, prepare to free old node */
|
||||
+ tnode_free(oldtnode);
|
||||
return 0;
|
||||
nomem:
|
||||
- tnode_clean_free(tn);
|
||||
+ /* all pointers should be clean so we are done */
|
||||
+ tnode_free(tn);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -599,17 +593,20 @@ static int halve(struct trie *t, struct
|
||||
struct tnode *newn;
|
||||
|
||||
newn = tnode_new(left->key, oldtnode->pos, 1);
|
||||
-
|
||||
if (!newn) {
|
||||
- tnode_clean_free(tn);
|
||||
+ tnode_free(tn);
|
||||
return -ENOMEM;
|
||||
}
|
||||
+ tnode_free_append(tn, newn);
|
||||
|
||||
put_child(tn, i/2, newn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+ /* prepare oldtnode to be freed */
|
||||
+ tnode_free_init(oldtnode);
|
||||
+
|
||||
for (i = 0; i < olen; i += 2) {
|
||||
struct tnode *newBinNode;
|
||||
|
||||
@@ -636,11 +633,14 @@ static int halve(struct trie *t, struct
|
||||
|
||||
put_child(tn, i / 2, newBinNode);
|
||||
|
||||
+ /* resize child node */
|
||||
resize(t, newBinNode);
|
||||
}
|
||||
|
||||
put_child_root(tp, t, tn->key, tn);
|
||||
- tnode_free_safe(oldtnode);
|
||||
+
|
||||
+ /* all pointers should be clean so we are done */
|
||||
+ tnode_free(oldtnode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -798,7 +798,8 @@ no_children:
|
||||
node_set_parent(n, tp);
|
||||
|
||||
/* drop dead node */
|
||||
- tnode_free_safe(tn);
|
||||
+ tnode_free_init(tn);
|
||||
+ tnode_free(tn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -884,16 +885,12 @@ static void trie_rebalance(struct trie *
|
||||
|
||||
while ((tp = node_parent(tn)) != NULL) {
|
||||
resize(t, tn);
|
||||
-
|
||||
- tnode_free_flush();
|
||||
tn = tp;
|
||||
}
|
||||
|
||||
/* Handle last (top) tnode */
|
||||
if (IS_TNODE(tn))
|
||||
resize(t, tn);
|
||||
-
|
||||
- tnode_free_flush();
|
||||
}
|
||||
|
||||
/* only used from updater-side */
|
|
@ -1,345 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:56:55 -0800
|
||||
Subject: [PATCH] fib_trie: inflate/halve nodes in a more RCU friendly
|
||||
way
|
||||
|
||||
This change pulls the node_set_parent functionality out of put_child_reorg
|
||||
and instead leaves that to the function to take care of as well. By doing
|
||||
this we can fully construct the new cluster of tnodes and all of the
|
||||
pointers out of it before we start routing pointers into it.
|
||||
|
||||
I am suspecting this will likely fix some concurency issues though I don't
|
||||
have a good test to show as such.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -391,8 +391,6 @@ static void put_child(struct tnode *tn,
|
||||
else if (!wasfull && isfull)
|
||||
tn->full_children++;
|
||||
|
||||
- node_set_parent(n, tn);
|
||||
-
|
||||
rcu_assign_pointer(tn->child[i], n);
|
||||
}
|
||||
|
||||
@@ -436,10 +434,8 @@ static void tnode_free(struct tnode *tn)
|
||||
|
||||
static int inflate(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- unsigned long olen = tnode_child_length(oldtnode);
|
||||
- struct tnode *tp = node_parent(oldtnode);
|
||||
- struct tnode *tn;
|
||||
- unsigned long i;
|
||||
+ struct tnode *inode, *node0, *node1, *tn, *tp;
|
||||
+ unsigned long i, j, k;
|
||||
t_key m;
|
||||
|
||||
pr_debug("In inflate\n");
|
||||
@@ -448,43 +444,13 @@ static int inflate(struct trie *t, struc
|
||||
if (!tn)
|
||||
return -ENOMEM;
|
||||
|
||||
- /*
|
||||
- * Preallocate and store tnodes before the actual work so we
|
||||
- * don't get into an inconsistent state if memory allocation
|
||||
- * fails. In case of failure we return the oldnode and inflate
|
||||
- * of tnode is ignored.
|
||||
+ /* Assemble all of the pointers in our cluster, in this case that
|
||||
+ * represents all of the pointers out of our allocated nodes that
|
||||
+ * point to existing tnodes and the links between our allocated
|
||||
+ * nodes.
|
||||
*/
|
||||
- for (i = 0, m = 1u << tn->pos; i < olen; i++) {
|
||||
- struct tnode *inode = tnode_get_child(oldtnode, i);
|
||||
-
|
||||
- if (tnode_full(oldtnode, inode) && (inode->bits > 1)) {
|
||||
- struct tnode *left, *right;
|
||||
-
|
||||
- left = tnode_new(inode->key & ~m, inode->pos,
|
||||
- inode->bits - 1);
|
||||
- if (!left)
|
||||
- goto nomem;
|
||||
- tnode_free_append(tn, left);
|
||||
-
|
||||
- right = tnode_new(inode->key | m, inode->pos,
|
||||
- inode->bits - 1);
|
||||
-
|
||||
- if (!right)
|
||||
- goto nomem;
|
||||
- tnode_free_append(tn, right);
|
||||
-
|
||||
- put_child(tn, 2*i, left);
|
||||
- put_child(tn, 2*i+1, right);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- /* prepare oldtnode to be freed */
|
||||
- tnode_free_init(oldtnode);
|
||||
-
|
||||
- for (i = 0; i < olen; i++) {
|
||||
- struct tnode *inode = tnode_get_child(oldtnode, i);
|
||||
- struct tnode *left, *right;
|
||||
- unsigned long size, j;
|
||||
+ for (i = tnode_child_length(oldtnode), m = 1u << tn->pos; i;) {
|
||||
+ inode = tnode_get_child(oldtnode, --i);
|
||||
|
||||
/* An empty child */
|
||||
if (inode == NULL)
|
||||
@@ -496,65 +462,99 @@ static int inflate(struct trie *t, struc
|
||||
continue;
|
||||
}
|
||||
|
||||
- /* drop the node in the old tnode free list */
|
||||
- tnode_free_append(oldtnode, inode);
|
||||
-
|
||||
/* An internal node with two children */
|
||||
if (inode->bits == 1) {
|
||||
- put_child(tn, 2*i, rtnl_dereference(inode->child[0]));
|
||||
- put_child(tn, 2*i+1, rtnl_dereference(inode->child[1]));
|
||||
+ put_child(tn, 2 * i + 1, tnode_get_child(inode, 1));
|
||||
+ put_child(tn, 2 * i, tnode_get_child(inode, 0));
|
||||
continue;
|
||||
}
|
||||
|
||||
- /* An internal node with more than two children */
|
||||
-
|
||||
/* We will replace this node 'inode' with two new
|
||||
- * ones, 'left' and 'right', each with half of the
|
||||
+ * ones, 'node0' and 'node1', each with half of the
|
||||
* original children. The two new nodes will have
|
||||
* a position one bit further down the key and this
|
||||
* means that the "significant" part of their keys
|
||||
* (see the discussion near the top of this file)
|
||||
* will differ by one bit, which will be "0" in
|
||||
- * left's key and "1" in right's key. Since we are
|
||||
+ * node0's key and "1" in node1's key. Since we are
|
||||
* moving the key position by one step, the bit that
|
||||
* we are moving away from - the bit at position
|
||||
- * (inode->pos) - is the one that will differ between
|
||||
- * left and right. So... we synthesize that bit in the
|
||||
- * two new keys.
|
||||
- * The mask 'm' below will be a single "one" bit at
|
||||
- * the position (inode->pos)
|
||||
+ * (tn->pos) - is the one that will differ between
|
||||
+ * node0 and node1. So... we synthesize that bit in the
|
||||
+ * two new keys.
|
||||
*/
|
||||
+ node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1);
|
||||
+ if (!node1)
|
||||
+ goto nomem;
|
||||
+ tnode_free_append(tn, node1);
|
||||
+
|
||||
+ node0 = tnode_new(inode->key & ~m, inode->pos, inode->bits - 1);
|
||||
+ if (!node0)
|
||||
+ goto nomem;
|
||||
+ tnode_free_append(tn, node0);
|
||||
+
|
||||
+ /* populate child pointers in new nodes */
|
||||
+ for (k = tnode_child_length(inode), j = k / 2; j;) {
|
||||
+ put_child(node1, --j, tnode_get_child(inode, --k));
|
||||
+ put_child(node0, j, tnode_get_child(inode, j));
|
||||
+ put_child(node1, --j, tnode_get_child(inode, --k));
|
||||
+ put_child(node0, j, tnode_get_child(inode, j));
|
||||
+ }
|
||||
+
|
||||
+ /* link new nodes to parent */
|
||||
+ NODE_INIT_PARENT(node1, tn);
|
||||
+ NODE_INIT_PARENT(node0, tn);
|
||||
+
|
||||
+ /* link parent to nodes */
|
||||
+ put_child(tn, 2 * i + 1, node1);
|
||||
+ put_child(tn, 2 * i, node0);
|
||||
+ }
|
||||
+
|
||||
+ /* setup the parent pointer into and out of this node */
|
||||
+ tp = node_parent(oldtnode);
|
||||
+ NODE_INIT_PARENT(tn, tp);
|
||||
+ put_child_root(tp, t, tn->key, tn);
|
||||
|
||||
- /* Use the old key, but set the new significant
|
||||
- * bit to zero.
|
||||
- */
|
||||
+ /* prepare oldtnode to be freed */
|
||||
+ tnode_free_init(oldtnode);
|
||||
|
||||
- left = tnode_get_child(tn, 2*i);
|
||||
- put_child(tn, 2*i, NULL);
|
||||
+ /* update all child nodes parent pointers to route to us */
|
||||
+ for (i = tnode_child_length(oldtnode); i;) {
|
||||
+ inode = tnode_get_child(oldtnode, --i);
|
||||
|
||||
- BUG_ON(!left);
|
||||
+ /* A leaf or an internal node with skipped bits */
|
||||
+ if (!tnode_full(oldtnode, inode)) {
|
||||
+ node_set_parent(inode, tn);
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- right = tnode_get_child(tn, 2*i+1);
|
||||
- put_child(tn, 2*i+1, NULL);
|
||||
+ /* drop the node in the old tnode free list */
|
||||
+ tnode_free_append(oldtnode, inode);
|
||||
|
||||
- BUG_ON(!right);
|
||||
+ /* fetch new nodes */
|
||||
+ node1 = tnode_get_child(tn, 2 * i + 1);
|
||||
+ node0 = tnode_get_child(tn, 2 * i);
|
||||
|
||||
- size = tnode_child_length(left);
|
||||
- for (j = 0; j < size; j++) {
|
||||
- put_child(left, j, rtnl_dereference(inode->child[j]));
|
||||
- put_child(right, j, rtnl_dereference(inode->child[j + size]));
|
||||
+ /* bits == 1 then node0 and node1 represent inode's children */
|
||||
+ if (inode->bits == 1) {
|
||||
+ node_set_parent(node1, tn);
|
||||
+ node_set_parent(node0, tn);
|
||||
+ continue;
|
||||
}
|
||||
|
||||
- put_child(tn, 2 * i, left);
|
||||
- put_child(tn, 2 * i + 1, right);
|
||||
+ /* update parent pointers in child node's children */
|
||||
+ for (k = tnode_child_length(inode), j = k / 2; j;) {
|
||||
+ node_set_parent(tnode_get_child(inode, --k), node1);
|
||||
+ node_set_parent(tnode_get_child(inode, --j), node0);
|
||||
+ node_set_parent(tnode_get_child(inode, --k), node1);
|
||||
+ node_set_parent(tnode_get_child(inode, --j), node0);
|
||||
+ }
|
||||
|
||||
/* resize child nodes */
|
||||
- resize(t, left);
|
||||
- resize(t, right);
|
||||
+ resize(t, node1);
|
||||
+ resize(t, node0);
|
||||
}
|
||||
|
||||
- put_child_root(tp, t, tn->key, tn);
|
||||
-
|
||||
/* we completed without error, prepare to free old node */
|
||||
tnode_free(oldtnode);
|
||||
return 0;
|
||||
@@ -566,10 +566,8 @@ nomem:
|
||||
|
||||
static int halve(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- unsigned long olen = tnode_child_length(oldtnode);
|
||||
- struct tnode *tp = node_parent(oldtnode);
|
||||
- struct tnode *tn, *left, *right;
|
||||
- int i;
|
||||
+ struct tnode *tn, *tp, *inode, *node0, *node1;
|
||||
+ unsigned long i;
|
||||
|
||||
pr_debug("In halve\n");
|
||||
|
||||
@@ -577,68 +575,64 @@ static int halve(struct trie *t, struct
|
||||
if (!tn)
|
||||
return -ENOMEM;
|
||||
|
||||
- /*
|
||||
- * Preallocate and store tnodes before the actual work so we
|
||||
- * don't get into an inconsistent state if memory allocation
|
||||
- * fails. In case of failure we return the oldnode and halve
|
||||
- * of tnode is ignored.
|
||||
+ /* Assemble all of the pointers in our cluster, in this case that
|
||||
+ * represents all of the pointers out of our allocated nodes that
|
||||
+ * point to existing tnodes and the links between our allocated
|
||||
+ * nodes.
|
||||
*/
|
||||
+ for (i = tnode_child_length(oldtnode); i;) {
|
||||
+ node1 = tnode_get_child(oldtnode, --i);
|
||||
+ node0 = tnode_get_child(oldtnode, --i);
|
||||
|
||||
- for (i = 0; i < olen; i += 2) {
|
||||
- left = tnode_get_child(oldtnode, i);
|
||||
- right = tnode_get_child(oldtnode, i+1);
|
||||
+ /* At least one of the children is empty */
|
||||
+ if (!node1 || !node0) {
|
||||
+ put_child(tn, i / 2, node1 ? : node0);
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
/* Two nonempty children */
|
||||
- if (left && right) {
|
||||
- struct tnode *newn;
|
||||
-
|
||||
- newn = tnode_new(left->key, oldtnode->pos, 1);
|
||||
- if (!newn) {
|
||||
- tnode_free(tn);
|
||||
- return -ENOMEM;
|
||||
- }
|
||||
- tnode_free_append(tn, newn);
|
||||
-
|
||||
- put_child(tn, i/2, newn);
|
||||
+ inode = tnode_new(node0->key, oldtnode->pos, 1);
|
||||
+ if (!inode) {
|
||||
+ tnode_free(tn);
|
||||
+ return -ENOMEM;
|
||||
}
|
||||
+ tnode_free_append(tn, inode);
|
||||
|
||||
+ /* initialize pointers out of node */
|
||||
+ put_child(inode, 1, node1);
|
||||
+ put_child(inode, 0, node0);
|
||||
+ NODE_INIT_PARENT(inode, tn);
|
||||
+
|
||||
+ /* link parent to node */
|
||||
+ put_child(tn, i / 2, inode);
|
||||
}
|
||||
|
||||
+ /* setup the parent pointer out of and back into this node */
|
||||
+ tp = node_parent(oldtnode);
|
||||
+ NODE_INIT_PARENT(tn, tp);
|
||||
+ put_child_root(tp, t, tn->key, tn);
|
||||
+
|
||||
/* prepare oldtnode to be freed */
|
||||
tnode_free_init(oldtnode);
|
||||
|
||||
- for (i = 0; i < olen; i += 2) {
|
||||
- struct tnode *newBinNode;
|
||||
-
|
||||
- left = tnode_get_child(oldtnode, i);
|
||||
- right = tnode_get_child(oldtnode, i+1);
|
||||
-
|
||||
- /* At least one of the children is empty */
|
||||
- if (left == NULL) {
|
||||
- if (right == NULL) /* Both are empty */
|
||||
- continue;
|
||||
- put_child(tn, i/2, right);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- if (right == NULL) {
|
||||
- put_child(tn, i/2, left);
|
||||
+ /* update all of the child parent pointers */
|
||||
+ for (i = tnode_child_length(tn); i;) {
|
||||
+ inode = tnode_get_child(tn, --i);
|
||||
+
|
||||
+ /* only new tnodes will be considered "full" nodes */
|
||||
+ if (!tnode_full(tn, inode)) {
|
||||
+ node_set_parent(inode, tn);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Two nonempty children */
|
||||
- newBinNode = tnode_get_child(tn, i/2);
|
||||
- put_child(newBinNode, 0, left);
|
||||
- put_child(newBinNode, 1, right);
|
||||
-
|
||||
- put_child(tn, i / 2, newBinNode);
|
||||
+ node_set_parent(tnode_get_child(inode, 1), inode);
|
||||
+ node_set_parent(tnode_get_child(inode, 0), inode);
|
||||
|
||||
/* resize child node */
|
||||
- resize(t, newBinNode);
|
||||
+ resize(t, inode);
|
||||
}
|
||||
|
||||
- put_child_root(tp, t, tn->key, tn);
|
||||
-
|
||||
/* all pointers should be clean so we are done */
|
||||
tnode_free(oldtnode);
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:57:02 -0800
|
||||
Subject: [PATCH] fib_trie: Remove checks for index >= tnode_child_length
|
||||
from tnode_get_child
|
||||
|
||||
For some reason the compiler doesn't seem to understand that when we are in
|
||||
a loop that runs from tnode_child_length - 1 to 0 we don't expect the value
|
||||
of tn->bits to change. As such every call to tnode_get_child was rerunning
|
||||
tnode_chile_length which ended up consuming quite a bit of space in the
|
||||
resultant assembly code.
|
||||
|
||||
I have gone though and verified that in all cases where tnode_get_child
|
||||
is used we are either winding though a fixed loop from tnode_child_length -
|
||||
1 to 0, or are in a fastpath case where we are verifying the value by
|
||||
either checking for any remaining bits after shifting index by bits and
|
||||
testing for leaf, or by using tnode_child_length.
|
||||
|
||||
size net/ipv4/fib_trie.o
|
||||
Before:
|
||||
text data bss dec hex filename
|
||||
15506 376 8 15890 3e12 net/ipv4/fib_trie.o
|
||||
|
||||
After:
|
||||
text data bss dec hex filename
|
||||
14827 376 8 15211 3b6b net/ipv4/fib_trie.o
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -186,8 +186,6 @@ static inline unsigned long tnode_child_
|
||||
static inline struct tnode *tnode_get_child(const struct tnode *tn,
|
||||
unsigned long i)
|
||||
{
|
||||
- BUG_ON(i >= tnode_child_length(tn));
|
||||
-
|
||||
return rtnl_dereference(tn->child[i]);
|
||||
}
|
||||
|
||||
@@ -195,8 +193,6 @@ static inline struct tnode *tnode_get_ch
|
||||
static inline struct tnode *tnode_get_child_rcu(const struct tnode *tn,
|
||||
unsigned long i)
|
||||
{
|
||||
- BUG_ON(i >= tnode_child_length(tn));
|
||||
-
|
||||
return rcu_dereference_rtnl(tn->child[i]);
|
||||
}
|
||||
|
||||
@@ -371,7 +367,7 @@ static inline int tnode_full(const struc
|
||||
*/
|
||||
static void put_child(struct tnode *tn, unsigned long i, struct tnode *n)
|
||||
{
|
||||
- struct tnode *chi = rtnl_dereference(tn->child[i]);
|
||||
+ struct tnode *chi = tnode_get_child(tn, i);
|
||||
int isfull, wasfull;
|
||||
|
||||
BUG_ON(i >= tnode_child_length(tn));
|
||||
@@ -867,7 +863,7 @@ static struct tnode *fib_find_node(struc
|
||||
if (IS_LEAF(n))
|
||||
break;
|
||||
|
||||
- n = rcu_dereference_rtnl(n->child[index]);
|
||||
+ n = tnode_get_child_rcu(n, index);
|
||||
}
|
||||
|
||||
return n;
|
||||
@@ -934,7 +930,7 @@ static struct list_head *fib_insert_node
|
||||
}
|
||||
|
||||
tp = n;
|
||||
- n = rcu_dereference_rtnl(n->child[index]);
|
||||
+ n = tnode_get_child_rcu(n, index);
|
||||
}
|
||||
|
||||
l = leaf_new(key);
|
||||
@@ -1215,7 +1211,7 @@ int fib_table_lookup(struct fib_table *t
|
||||
cindex = index;
|
||||
}
|
||||
|
||||
- n = rcu_dereference(n->child[index]);
|
||||
+ n = tnode_get_child_rcu(n, index);
|
||||
if (unlikely(!n))
|
||||
goto backtrace;
|
||||
}
|
||||
@@ -1835,7 +1831,7 @@ static void trie_collect_stats(struct tr
|
||||
if (n->bits < MAX_STAT_DEPTH)
|
||||
s->nodesizes[n->bits]++;
|
||||
|
||||
- for (i = 0; i < tnode_child_length(n); i++) {
|
||||
+ for (i = tnode_child_length(n); i--;) {
|
||||
if (!rcu_access_pointer(n->child[i]))
|
||||
s->nullpointers++;
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Wed, 31 Dec 2014 10:57:08 -0800
|
||||
Subject: [PATCH] fib_trie: Add tracking value for suffix length
|
||||
|
||||
This change adds a tracking value for the maximum suffix length of all
|
||||
prefixes stored in any given tnode. With this value we can determine if we
|
||||
need to backtrace or not based on if the suffix is greater than the pos
|
||||
value.
|
||||
|
||||
By doing this we can reduce the CPU overhead for lookups in the local table
|
||||
as many of the prefixes there are 32b long and have a suffix length of 0
|
||||
meaning we can immediately backtrace to the root node without needing to
|
||||
test any of the nodes between it and where we ended up.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -96,6 +96,7 @@ struct tnode {
|
||||
t_key key;
|
||||
unsigned char bits; /* 2log(KEYLENGTH) bits needed */
|
||||
unsigned char pos; /* 2log(KEYLENGTH) bits needed */
|
||||
+ unsigned char slen;
|
||||
struct tnode __rcu *parent;
|
||||
struct rcu_head rcu;
|
||||
union {
|
||||
@@ -311,6 +312,7 @@ static struct tnode *leaf_new(t_key key)
|
||||
* as the nodes are searched
|
||||
*/
|
||||
l->key = key;
|
||||
+ l->slen = 0;
|
||||
l->pos = 0;
|
||||
/* set bits to 0 indicating we are not a tnode */
|
||||
l->bits = 0;
|
||||
@@ -342,6 +344,7 @@ static struct tnode *tnode_new(t_key key
|
||||
|
||||
if (tn) {
|
||||
tn->parent = NULL;
|
||||
+ tn->slen = pos;
|
||||
tn->pos = pos;
|
||||
tn->bits = bits;
|
||||
tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0;
|
||||
@@ -387,6 +390,9 @@ static void put_child(struct tnode *tn,
|
||||
else if (!wasfull && isfull)
|
||||
tn->full_children++;
|
||||
|
||||
+ if (n && (tn->slen < n->slen))
|
||||
+ tn->slen = n->slen;
|
||||
+
|
||||
rcu_assign_pointer(tn->child[i], n);
|
||||
}
|
||||
|
||||
@@ -635,6 +641,41 @@ static int halve(struct trie *t, struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static unsigned char update_suffix(struct tnode *tn)
|
||||
+{
|
||||
+ unsigned char slen = tn->pos;
|
||||
+ unsigned long stride, i;
|
||||
+
|
||||
+ /* search though the list of children looking for nodes that might
|
||||
+ * have a suffix greater than the one we currently have. This is
|
||||
+ * why we start with a stride of 2 since a stride of 1 would
|
||||
+ * represent the nodes with suffix length equal to tn->pos
|
||||
+ */
|
||||
+ for (i = 0, stride = 0x2ul ; i < tnode_child_length(tn); i += stride) {
|
||||
+ struct tnode *n = tnode_get_child(tn, i);
|
||||
+
|
||||
+ if (!n || (n->slen <= slen))
|
||||
+ continue;
|
||||
+
|
||||
+ /* update stride and slen based on new value */
|
||||
+ stride <<= (n->slen - slen);
|
||||
+ slen = n->slen;
|
||||
+ i &= ~(stride - 1);
|
||||
+
|
||||
+ /* if slen covers all but the last bit we can stop here
|
||||
+ * there will be nothing longer than that since only node
|
||||
+ * 0 and 1 << (bits - 1) could have that as their suffix
|
||||
+ * length.
|
||||
+ */
|
||||
+ if ((slen + 1) >= (tn->pos + tn->bits))
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ tn->slen = slen;
|
||||
+
|
||||
+ return slen;
|
||||
+}
|
||||
+
|
||||
/* From "Implementing a dynamic compressed trie" by Stefan Nilsson of
|
||||
* the Helsinki University of Technology and Matti Tikkanen of Nokia
|
||||
* Telecommunications, page 6:
|
||||
@@ -790,6 +831,19 @@ no_children:
|
||||
/* drop dead node */
|
||||
tnode_free_init(tn);
|
||||
tnode_free(tn);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Return if at least one deflate was run */
|
||||
+ if (max_work != MAX_WORK)
|
||||
+ return;
|
||||
+
|
||||
+ /* push the suffix length to the parent node */
|
||||
+ if (tn->slen > tn->pos) {
|
||||
+ unsigned char slen = update_suffix(tn);
|
||||
+
|
||||
+ if (tp && (slen > tp->slen))
|
||||
+ tp->slen = slen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,8 +872,58 @@ static inline struct list_head *get_fa_h
|
||||
return &li->falh;
|
||||
}
|
||||
|
||||
-static void insert_leaf_info(struct hlist_head *head, struct leaf_info *new)
|
||||
+static void leaf_pull_suffix(struct tnode *l)
|
||||
+{
|
||||
+ struct tnode *tp = node_parent(l);
|
||||
+
|
||||
+ while (tp && (tp->slen > tp->pos) && (tp->slen > l->slen)) {
|
||||
+ if (update_suffix(tp) > l->slen)
|
||||
+ break;
|
||||
+ tp = node_parent(tp);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void leaf_push_suffix(struct tnode *l)
|
||||
+{
|
||||
+ struct tnode *tn = node_parent(l);
|
||||
+
|
||||
+ /* if this is a new leaf then tn will be NULL and we can sort
|
||||
+ * out parent suffix lengths as a part of trie_rebalance
|
||||
+ */
|
||||
+ while (tn && (tn->slen < l->slen)) {
|
||||
+ tn->slen = l->slen;
|
||||
+ tn = node_parent(tn);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void remove_leaf_info(struct tnode *l, struct leaf_info *old)
|
||||
+{
|
||||
+ struct hlist_node *prev;
|
||||
+
|
||||
+ /* record the location of the pointer to this object */
|
||||
+ prev = rtnl_dereference(hlist_pprev_rcu(&old->hlist));
|
||||
+
|
||||
+ /* remove the leaf info from the list */
|
||||
+ hlist_del_rcu(&old->hlist);
|
||||
+
|
||||
+ /* if we emptied the list this leaf will be freed and we can sort
|
||||
+ * out parent suffix lengths as a part of trie_rebalance
|
||||
+ */
|
||||
+ if (hlist_empty(&l->list))
|
||||
+ return;
|
||||
+
|
||||
+ /* if we removed the tail then we need to update slen */
|
||||
+ if (!rcu_access_pointer(hlist_next_rcu(prev))) {
|
||||
+ struct leaf_info *li = hlist_entry(prev, typeof(*li), hlist);
|
||||
+
|
||||
+ l->slen = KEYLENGTH - li->plen;
|
||||
+ leaf_pull_suffix(l);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void insert_leaf_info(struct tnode *l, struct leaf_info *new)
|
||||
{
|
||||
+ struct hlist_head *head = &l->list;
|
||||
struct leaf_info *li = NULL, *last = NULL;
|
||||
|
||||
if (hlist_empty(head)) {
|
||||
@@ -836,6 +940,12 @@ static void insert_leaf_info(struct hlis
|
||||
else
|
||||
hlist_add_before_rcu(&new->hlist, &li->hlist);
|
||||
}
|
||||
+
|
||||
+ /* if we added to the tail node then we need to update slen */
|
||||
+ if (!rcu_access_pointer(hlist_next_rcu(&new->hlist))) {
|
||||
+ l->slen = KEYLENGTH - new->plen;
|
||||
+ leaf_push_suffix(l);
|
||||
+ }
|
||||
}
|
||||
|
||||
/* rcu_read_lock needs to be hold by caller from readside */
|
||||
@@ -925,7 +1035,7 @@ static struct list_head *fib_insert_node
|
||||
/* we have found a leaf. Prefixes have already been compared */
|
||||
if (IS_LEAF(n)) {
|
||||
/* Case 1: n is a leaf, and prefixes match*/
|
||||
- insert_leaf_info(&n->list, li);
|
||||
+ insert_leaf_info(n, li);
|
||||
return fa_head;
|
||||
}
|
||||
|
||||
@@ -939,7 +1049,7 @@ static struct list_head *fib_insert_node
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- insert_leaf_info(&l->list, li);
|
||||
+ insert_leaf_info(l, li);
|
||||
|
||||
/* Case 2: n is a LEAF or a TNODE and the key doesn't match.
|
||||
*
|
||||
@@ -1206,7 +1316,7 @@ int fib_table_lookup(struct fib_table *t
|
||||
/* only record pn and cindex if we are going to be chopping
|
||||
* bits later. Otherwise we are just wasting cycles.
|
||||
*/
|
||||
- if (index) {
|
||||
+ if (n->slen > n->pos) {
|
||||
pn = n;
|
||||
cindex = index;
|
||||
}
|
||||
@@ -1225,7 +1335,7 @@ int fib_table_lookup(struct fib_table *t
|
||||
* between the key and the prefix exist in the region of
|
||||
* the lsb and higher in the prefix.
|
||||
*/
|
||||
- if (unlikely(prefix_mismatch(key, n)))
|
||||
+ if (unlikely(prefix_mismatch(key, n)) || (n->slen == n->pos))
|
||||
goto backtrace;
|
||||
|
||||
/* exit out and process leaf */
|
||||
@@ -1425,7 +1535,7 @@ int fib_table_delete(struct fib_table *t
|
||||
tb->tb_num_default--;
|
||||
|
||||
if (list_empty(fa_head)) {
|
||||
- hlist_del_rcu(&li->hlist);
|
||||
+ remove_leaf_info(l, li);
|
||||
free_leaf_info(li);
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Thu, 22 Jan 2015 15:51:08 -0800
|
||||
Subject: [PATCH] fib_trie: Use index & (~0ul << n->bits) instead of index >>
|
||||
n->bits
|
||||
|
||||
In doing performance testing and analysis of the changes I recently found
|
||||
that by shifting the index I had created an unnecessary dependency.
|
||||
|
||||
I have updated the code so that we instead shift a mask by bits and then
|
||||
just test against that as that should save us about 2 CPU cycles since we
|
||||
can generate the mask while the key and pos are being processed.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -961,12 +961,12 @@ static struct tnode *fib_find_node(struc
|
||||
* prefix plus zeros for the bits in the cindex. The index
|
||||
* is the difference between the key and this value. From
|
||||
* this we can actually derive several pieces of data.
|
||||
- * if !(index >> bits)
|
||||
- * we know the value is cindex
|
||||
- * else
|
||||
+ * if (index & (~0ul << bits))
|
||||
* we have a mismatch in skip bits and failed
|
||||
+ * else
|
||||
+ * we know the value is cindex
|
||||
*/
|
||||
- if (index >> n->bits)
|
||||
+ if (index & (~0ul << n->bits))
|
||||
return NULL;
|
||||
|
||||
/* we have found a leaf. Prefixes have already been compared */
|
||||
@@ -1301,12 +1301,12 @@ int fib_table_lookup(struct fib_table *t
|
||||
* prefix plus zeros for the "bits" in the prefix. The index
|
||||
* is the difference between the key and this value. From
|
||||
* this we can actually derive several pieces of data.
|
||||
- * if !(index >> bits)
|
||||
- * we know the value is child index
|
||||
- * else
|
||||
+ * if (index & (~0ul << bits))
|
||||
* we have a mismatch in skip bits and failed
|
||||
+ * else
|
||||
+ * we know the value is cindex
|
||||
*/
|
||||
- if (index >> n->bits)
|
||||
+ if (index & (~0ul << n->bits))
|
||||
break;
|
||||
|
||||
/* we have found a leaf. Prefixes have already been compared */
|
|
@ -1,267 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Thu, 22 Jan 2015 15:51:14 -0800
|
||||
Subject: [PATCH] fib_trie: Fix RCU bug and merge similar bits of inflate/halve
|
||||
|
||||
This patch addresses two issues.
|
||||
|
||||
The first issue is the fact that I believe I had the RCU freeing sequence
|
||||
slightly out of order. As a result we could get into an issue if a caller
|
||||
went into a child of a child of the new node, then backtraced into the to be
|
||||
freed parent, and then attempted to access a child of a child that may have
|
||||
been consumed in a resize of one of the new nodes children. To resolve this I
|
||||
have moved the resize after we have freed the oldtnode. The only side effect
|
||||
of this is that we will now be calling resize on more nodes in the case of
|
||||
inflate due to the fact that we don't have a good way to test to see if a
|
||||
full_tnode on the new node was there before or after the allocation. This
|
||||
should have minimal impact however since the node should already be
|
||||
correctly size so it is just the cost of calling should_inflate that we
|
||||
will be taking on the node which is only a couple of cycles.
|
||||
|
||||
The second issue is the fact that inflate and halve were essentially doing
|
||||
the same thing after the new node was added to the trie replacing the old
|
||||
one. As such it wasn't really necessary to keep the code in both functions
|
||||
so I have split it out into two other functions, called replace and
|
||||
update_children.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -396,8 +396,30 @@ static void put_child(struct tnode *tn,
|
||||
rcu_assign_pointer(tn->child[i], n);
|
||||
}
|
||||
|
||||
-static void put_child_root(struct tnode *tp, struct trie *t,
|
||||
- t_key key, struct tnode *n)
|
||||
+static void update_children(struct tnode *tn)
|
||||
+{
|
||||
+ unsigned long i;
|
||||
+
|
||||
+ /* update all of the child parent pointers */
|
||||
+ for (i = tnode_child_length(tn); i;) {
|
||||
+ struct tnode *inode = tnode_get_child(tn, --i);
|
||||
+
|
||||
+ if (!inode)
|
||||
+ continue;
|
||||
+
|
||||
+ /* Either update the children of a tnode that
|
||||
+ * already belongs to us or update the child
|
||||
+ * to point to ourselves.
|
||||
+ */
|
||||
+ if (node_parent(inode) == tn)
|
||||
+ update_children(inode);
|
||||
+ else
|
||||
+ node_set_parent(inode, tn);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static inline void put_child_root(struct tnode *tp, struct trie *t,
|
||||
+ t_key key, struct tnode *n)
|
||||
{
|
||||
if (tp)
|
||||
put_child(tp, get_index(key, tp), n);
|
||||
@@ -434,10 +456,35 @@ static void tnode_free(struct tnode *tn)
|
||||
}
|
||||
}
|
||||
|
||||
+static void replace(struct trie *t, struct tnode *oldtnode, struct tnode *tn)
|
||||
+{
|
||||
+ struct tnode *tp = node_parent(oldtnode);
|
||||
+ unsigned long i;
|
||||
+
|
||||
+ /* setup the parent pointer out of and back into this node */
|
||||
+ NODE_INIT_PARENT(tn, tp);
|
||||
+ put_child_root(tp, t, tn->key, tn);
|
||||
+
|
||||
+ /* update all of the child parent pointers */
|
||||
+ update_children(tn);
|
||||
+
|
||||
+ /* all pointers should be clean so we are done */
|
||||
+ tnode_free(oldtnode);
|
||||
+
|
||||
+ /* resize children now that oldtnode is freed */
|
||||
+ for (i = tnode_child_length(tn); i;) {
|
||||
+ struct tnode *inode = tnode_get_child(tn, --i);
|
||||
+
|
||||
+ /* resize child node */
|
||||
+ if (tnode_full(tn, inode))
|
||||
+ resize(t, inode);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static int inflate(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- struct tnode *inode, *node0, *node1, *tn, *tp;
|
||||
- unsigned long i, j, k;
|
||||
+ struct tnode *tn;
|
||||
+ unsigned long i;
|
||||
t_key m;
|
||||
|
||||
pr_debug("In inflate\n");
|
||||
@@ -446,13 +493,18 @@ static int inflate(struct trie *t, struc
|
||||
if (!tn)
|
||||
return -ENOMEM;
|
||||
|
||||
+ /* prepare oldtnode to be freed */
|
||||
+ tnode_free_init(oldtnode);
|
||||
+
|
||||
/* Assemble all of the pointers in our cluster, in this case that
|
||||
* represents all of the pointers out of our allocated nodes that
|
||||
* point to existing tnodes and the links between our allocated
|
||||
* nodes.
|
||||
*/
|
||||
for (i = tnode_child_length(oldtnode), m = 1u << tn->pos; i;) {
|
||||
- inode = tnode_get_child(oldtnode, --i);
|
||||
+ struct tnode *inode = tnode_get_child(oldtnode, --i);
|
||||
+ struct tnode *node0, *node1;
|
||||
+ unsigned long j, k;
|
||||
|
||||
/* An empty child */
|
||||
if (inode == NULL)
|
||||
@@ -464,6 +516,9 @@ static int inflate(struct trie *t, struc
|
||||
continue;
|
||||
}
|
||||
|
||||
+ /* drop the node in the old tnode free list */
|
||||
+ tnode_free_append(oldtnode, inode);
|
||||
+
|
||||
/* An internal node with two children */
|
||||
if (inode->bits == 1) {
|
||||
put_child(tn, 2 * i + 1, tnode_get_child(inode, 1));
|
||||
@@ -488,9 +543,9 @@ static int inflate(struct trie *t, struc
|
||||
node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1);
|
||||
if (!node1)
|
||||
goto nomem;
|
||||
- tnode_free_append(tn, node1);
|
||||
+ node0 = tnode_new(inode->key, inode->pos, inode->bits - 1);
|
||||
|
||||
- node0 = tnode_new(inode->key & ~m, inode->pos, inode->bits - 1);
|
||||
+ tnode_free_append(tn, node1);
|
||||
if (!node0)
|
||||
goto nomem;
|
||||
tnode_free_append(tn, node0);
|
||||
@@ -512,53 +567,9 @@ static int inflate(struct trie *t, struc
|
||||
put_child(tn, 2 * i, node0);
|
||||
}
|
||||
|
||||
- /* setup the parent pointer into and out of this node */
|
||||
- tp = node_parent(oldtnode);
|
||||
- NODE_INIT_PARENT(tn, tp);
|
||||
- put_child_root(tp, t, tn->key, tn);
|
||||
+ /* setup the parent pointers into and out of this node */
|
||||
+ replace(t, oldtnode, tn);
|
||||
|
||||
- /* prepare oldtnode to be freed */
|
||||
- tnode_free_init(oldtnode);
|
||||
-
|
||||
- /* update all child nodes parent pointers to route to us */
|
||||
- for (i = tnode_child_length(oldtnode); i;) {
|
||||
- inode = tnode_get_child(oldtnode, --i);
|
||||
-
|
||||
- /* A leaf or an internal node with skipped bits */
|
||||
- if (!tnode_full(oldtnode, inode)) {
|
||||
- node_set_parent(inode, tn);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- /* drop the node in the old tnode free list */
|
||||
- tnode_free_append(oldtnode, inode);
|
||||
-
|
||||
- /* fetch new nodes */
|
||||
- node1 = tnode_get_child(tn, 2 * i + 1);
|
||||
- node0 = tnode_get_child(tn, 2 * i);
|
||||
-
|
||||
- /* bits == 1 then node0 and node1 represent inode's children */
|
||||
- if (inode->bits == 1) {
|
||||
- node_set_parent(node1, tn);
|
||||
- node_set_parent(node0, tn);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- /* update parent pointers in child node's children */
|
||||
- for (k = tnode_child_length(inode), j = k / 2; j;) {
|
||||
- node_set_parent(tnode_get_child(inode, --k), node1);
|
||||
- node_set_parent(tnode_get_child(inode, --j), node0);
|
||||
- node_set_parent(tnode_get_child(inode, --k), node1);
|
||||
- node_set_parent(tnode_get_child(inode, --j), node0);
|
||||
- }
|
||||
-
|
||||
- /* resize child nodes */
|
||||
- resize(t, node1);
|
||||
- resize(t, node0);
|
||||
- }
|
||||
-
|
||||
- /* we completed without error, prepare to free old node */
|
||||
- tnode_free(oldtnode);
|
||||
return 0;
|
||||
nomem:
|
||||
/* all pointers should be clean so we are done */
|
||||
@@ -568,7 +579,7 @@ nomem:
|
||||
|
||||
static int halve(struct trie *t, struct tnode *oldtnode)
|
||||
{
|
||||
- struct tnode *tn, *tp, *inode, *node0, *node1;
|
||||
+ struct tnode *tn;
|
||||
unsigned long i;
|
||||
|
||||
pr_debug("In halve\n");
|
||||
@@ -577,14 +588,18 @@ static int halve(struct trie *t, struct
|
||||
if (!tn)
|
||||
return -ENOMEM;
|
||||
|
||||
+ /* prepare oldtnode to be freed */
|
||||
+ tnode_free_init(oldtnode);
|
||||
+
|
||||
/* Assemble all of the pointers in our cluster, in this case that
|
||||
* represents all of the pointers out of our allocated nodes that
|
||||
* point to existing tnodes and the links between our allocated
|
||||
* nodes.
|
||||
*/
|
||||
for (i = tnode_child_length(oldtnode); i;) {
|
||||
- node1 = tnode_get_child(oldtnode, --i);
|
||||
- node0 = tnode_get_child(oldtnode, --i);
|
||||
+ struct tnode *node1 = tnode_get_child(oldtnode, --i);
|
||||
+ struct tnode *node0 = tnode_get_child(oldtnode, --i);
|
||||
+ struct tnode *inode;
|
||||
|
||||
/* At least one of the children is empty */
|
||||
if (!node1 || !node0) {
|
||||
@@ -609,34 +624,8 @@ static int halve(struct trie *t, struct
|
||||
put_child(tn, i / 2, inode);
|
||||
}
|
||||
|
||||
- /* setup the parent pointer out of and back into this node */
|
||||
- tp = node_parent(oldtnode);
|
||||
- NODE_INIT_PARENT(tn, tp);
|
||||
- put_child_root(tp, t, tn->key, tn);
|
||||
-
|
||||
- /* prepare oldtnode to be freed */
|
||||
- tnode_free_init(oldtnode);
|
||||
-
|
||||
- /* update all of the child parent pointers */
|
||||
- for (i = tnode_child_length(tn); i;) {
|
||||
- inode = tnode_get_child(tn, --i);
|
||||
-
|
||||
- /* only new tnodes will be considered "full" nodes */
|
||||
- if (!tnode_full(tn, inode)) {
|
||||
- node_set_parent(inode, tn);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- /* Two nonempty children */
|
||||
- node_set_parent(tnode_get_child(inode, 1), inode);
|
||||
- node_set_parent(tnode_get_child(inode, 0), inode);
|
||||
-
|
||||
- /* resize child node */
|
||||
- resize(t, inode);
|
||||
- }
|
||||
-
|
||||
- /* all pointers should be clean so we are done */
|
||||
- tnode_free(oldtnode);
|
||||
+ /* setup the parent pointers into and out of this node */
|
||||
+ replace(t, oldtnode, tn);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Thu, 22 Jan 2015 15:51:20 -0800
|
||||
Subject: [PATCH] fib_trie: Fall back to slen update on inflate/halve failure
|
||||
|
||||
This change corrects an issue where if inflate or halve fails we were
|
||||
exiting the resize function without at least updating the slen for the
|
||||
node. To correct this I have moved the update of max_size into the while
|
||||
loop so that it is only decremented on a successful call to either inflate
|
||||
or halve.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -752,7 +752,7 @@ static void resize(struct trie *t, struc
|
||||
{
|
||||
struct tnode *tp = node_parent(tn), *n = NULL;
|
||||
struct tnode __rcu **cptr;
|
||||
- int max_work;
|
||||
+ int max_work = MAX_WORK;
|
||||
|
||||
pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n",
|
||||
tn, inflate_threshold, halve_threshold);
|
||||
@@ -775,8 +775,7 @@ static void resize(struct trie *t, struc
|
||||
/* Double as long as the resulting node has a number of
|
||||
* nonempty nodes that are above the threshold.
|
||||
*/
|
||||
- max_work = MAX_WORK;
|
||||
- while (should_inflate(tp, tn) && max_work--) {
|
||||
+ while (should_inflate(tp, tn) && max_work) {
|
||||
if (inflate(t, tn)) {
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
this_cpu_inc(t->stats->resize_node_skipped);
|
||||
@@ -784,6 +783,7 @@ static void resize(struct trie *t, struc
|
||||
break;
|
||||
}
|
||||
|
||||
+ max_work--;
|
||||
tn = rtnl_dereference(*cptr);
|
||||
}
|
||||
|
||||
@@ -794,8 +794,7 @@ static void resize(struct trie *t, struc
|
||||
/* Halve as long as the number of empty children in this
|
||||
* node is above threshold.
|
||||
*/
|
||||
- max_work = MAX_WORK;
|
||||
- while (should_halve(tp, tn) && max_work--) {
|
||||
+ while (should_halve(tp, tn) && max_work) {
|
||||
if (halve(t, tn)) {
|
||||
#ifdef CONFIG_IP_FIB_TRIE_STATS
|
||||
this_cpu_inc(t->stats->resize_node_skipped);
|
||||
@@ -803,6 +802,7 @@ static void resize(struct trie *t, struc
|
||||
break;
|
||||
}
|
||||
|
||||
+ max_work--;
|
||||
tn = rtnl_dereference(*cptr);
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Thu, 22 Jan 2015 15:51:26 -0800
|
||||
Subject: [PATCH] fib_trie: Add collapse() and should_collapse() to resize
|
||||
|
||||
This patch really does two things.
|
||||
|
||||
First it pulls the logic for determining if we should collapse one node out
|
||||
of the tree and the actual code doing the collapse into a separate pair of
|
||||
functions. This helps to make the changes to these areas more readable.
|
||||
|
||||
Second it encodes the upper 32b of the empty_children value onto the
|
||||
full_children value in the case of bits == KEYLENGTH. By doing this we are
|
||||
able to handle the case of a 32b node where empty_children would appear to
|
||||
be 0 when it was actually 1ul << 32.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -83,7 +83,8 @@
|
||||
|
||||
#define MAX_STAT_DEPTH 32
|
||||
|
||||
-#define KEYLENGTH (8*sizeof(t_key))
|
||||
+#define KEYLENGTH (8*sizeof(t_key))
|
||||
+#define KEY_MAX ((t_key)~0)
|
||||
|
||||
typedef unsigned int t_key;
|
||||
|
||||
@@ -102,8 +103,8 @@ struct tnode {
|
||||
union {
|
||||
/* The fields in this struct are valid if bits > 0 (TNODE) */
|
||||
struct {
|
||||
- unsigned int full_children; /* KEYLENGTH bits needed */
|
||||
- unsigned int empty_children; /* KEYLENGTH bits needed */
|
||||
+ t_key empty_children; /* KEYLENGTH bits needed */
|
||||
+ t_key full_children; /* KEYLENGTH bits needed */
|
||||
struct tnode __rcu *child[0];
|
||||
};
|
||||
/* This list pointer if valid if bits == 0 (LEAF) */
|
||||
@@ -302,6 +303,16 @@ static struct tnode *tnode_alloc(size_t
|
||||
return vzalloc(size);
|
||||
}
|
||||
|
||||
+static inline void empty_child_inc(struct tnode *n)
|
||||
+{
|
||||
+ ++n->empty_children ? : ++n->full_children;
|
||||
+}
|
||||
+
|
||||
+static inline void empty_child_dec(struct tnode *n)
|
||||
+{
|
||||
+ n->empty_children-- ? : n->full_children--;
|
||||
+}
|
||||
+
|
||||
static struct tnode *leaf_new(t_key key)
|
||||
{
|
||||
struct tnode *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL);
|
||||
@@ -335,7 +346,7 @@ static struct leaf_info *leaf_info_new(i
|
||||
|
||||
static struct tnode *tnode_new(t_key key, int pos, int bits)
|
||||
{
|
||||
- size_t sz = offsetof(struct tnode, child[1 << bits]);
|
||||
+ size_t sz = offsetof(struct tnode, child[1ul << bits]);
|
||||
struct tnode *tn = tnode_alloc(sz);
|
||||
unsigned int shift = pos + bits;
|
||||
|
||||
@@ -348,8 +359,10 @@ static struct tnode *tnode_new(t_key key
|
||||
tn->pos = pos;
|
||||
tn->bits = bits;
|
||||
tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0;
|
||||
- tn->full_children = 0;
|
||||
- tn->empty_children = 1<<bits;
|
||||
+ if (bits == KEYLENGTH)
|
||||
+ tn->full_children = 1;
|
||||
+ else
|
||||
+ tn->empty_children = 1ul << bits;
|
||||
}
|
||||
|
||||
pr_debug("AT %p s=%zu %zu\n", tn, sizeof(struct tnode),
|
||||
@@ -375,11 +388,11 @@ static void put_child(struct tnode *tn,
|
||||
|
||||
BUG_ON(i >= tnode_child_length(tn));
|
||||
|
||||
- /* update emptyChildren */
|
||||
+ /* update emptyChildren, overflow into fullChildren */
|
||||
if (n == NULL && chi != NULL)
|
||||
- tn->empty_children++;
|
||||
- else if (n != NULL && chi == NULL)
|
||||
- tn->empty_children--;
|
||||
+ empty_child_inc(tn);
|
||||
+ if (n != NULL && chi == NULL)
|
||||
+ empty_child_dec(tn);
|
||||
|
||||
/* update fullChildren */
|
||||
wasfull = tnode_full(tn, chi);
|
||||
@@ -630,6 +643,24 @@ static int halve(struct trie *t, struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void collapse(struct trie *t, struct tnode *oldtnode)
|
||||
+{
|
||||
+ struct tnode *n, *tp;
|
||||
+ unsigned long i;
|
||||
+
|
||||
+ /* scan the tnode looking for that one child that might still exist */
|
||||
+ for (n = NULL, i = tnode_child_length(oldtnode); !n && i;)
|
||||
+ n = tnode_get_child(oldtnode, --i);
|
||||
+
|
||||
+ /* compress one level */
|
||||
+ tp = node_parent(oldtnode);
|
||||
+ put_child_root(tp, t, oldtnode->key, n);
|
||||
+ node_set_parent(n, tp);
|
||||
+
|
||||
+ /* drop dead node */
|
||||
+ node_free(oldtnode);
|
||||
+}
|
||||
+
|
||||
static unsigned char update_suffix(struct tnode *tn)
|
||||
{
|
||||
unsigned char slen = tn->pos;
|
||||
@@ -729,10 +760,12 @@ static bool should_inflate(const struct
|
||||
|
||||
/* Keep root node larger */
|
||||
threshold *= tp ? inflate_threshold : inflate_threshold_root;
|
||||
- used += tn->full_children;
|
||||
used -= tn->empty_children;
|
||||
+ used += tn->full_children;
|
||||
|
||||
- return tn->pos && ((50 * used) >= threshold);
|
||||
+ /* if bits == KEYLENGTH then pos = 0, and will fail below */
|
||||
+
|
||||
+ return (used > 1) && tn->pos && ((50 * used) >= threshold);
|
||||
}
|
||||
|
||||
static bool should_halve(const struct tnode *tp, const struct tnode *tn)
|
||||
@@ -744,13 +777,29 @@ static bool should_halve(const struct tn
|
||||
threshold *= tp ? halve_threshold : halve_threshold_root;
|
||||
used -= tn->empty_children;
|
||||
|
||||
- return (tn->bits > 1) && ((100 * used) < threshold);
|
||||
+ /* if bits == KEYLENGTH then used = 100% on wrap, and will fail below */
|
||||
+
|
||||
+ return (used > 1) && (tn->bits > 1) && ((100 * used) < threshold);
|
||||
+}
|
||||
+
|
||||
+static bool should_collapse(const struct tnode *tn)
|
||||
+{
|
||||
+ unsigned long used = tnode_child_length(tn);
|
||||
+
|
||||
+ used -= tn->empty_children;
|
||||
+
|
||||
+ /* account for bits == KEYLENGTH case */
|
||||
+ if ((tn->bits == KEYLENGTH) && tn->full_children)
|
||||
+ used -= KEY_MAX;
|
||||
+
|
||||
+ /* One child or none, time to drop us from the trie */
|
||||
+ return used < 2;
|
||||
}
|
||||
|
||||
#define MAX_WORK 10
|
||||
static void resize(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
- struct tnode *tp = node_parent(tn), *n = NULL;
|
||||
+ struct tnode *tp = node_parent(tn);
|
||||
struct tnode __rcu **cptr;
|
||||
int max_work = MAX_WORK;
|
||||
|
||||
@@ -764,14 +813,6 @@ static void resize(struct trie *t, struc
|
||||
cptr = tp ? &tp->child[get_index(tn->key, tp)] : &t->trie;
|
||||
BUG_ON(tn != rtnl_dereference(*cptr));
|
||||
|
||||
- /* No children */
|
||||
- if (tn->empty_children > (tnode_child_length(tn) - 1))
|
||||
- goto no_children;
|
||||
-
|
||||
- /* One child */
|
||||
- if (tn->empty_children == (tnode_child_length(tn) - 1))
|
||||
- goto one_child;
|
||||
-
|
||||
/* Double as long as the resulting node has a number of
|
||||
* nonempty nodes that are above the threshold.
|
||||
*/
|
||||
@@ -807,19 +848,8 @@ static void resize(struct trie *t, struc
|
||||
}
|
||||
|
||||
/* Only one child remains */
|
||||
- if (tn->empty_children == (tnode_child_length(tn) - 1)) {
|
||||
- unsigned long i;
|
||||
-one_child:
|
||||
- for (i = tnode_child_length(tn); !n && i;)
|
||||
- n = tnode_get_child(tn, --i);
|
||||
-no_children:
|
||||
- /* compress one level */
|
||||
- put_child_root(tp, t, tn->key, n);
|
||||
- node_set_parent(n, tp);
|
||||
-
|
||||
- /* drop dead node */
|
||||
- tnode_free_init(tn);
|
||||
- tnode_free(tn);
|
||||
+ if (should_collapse(tn)) {
|
||||
+ collapse(t, tn);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Thu, 22 Jan 2015 15:51:33 -0800
|
||||
Subject: [PATCH] fib_trie: Use empty_children instead of counting empty nodes
|
||||
in stats collection
|
||||
|
||||
It doesn't make much sense to count the pointers ourselves when
|
||||
empty_children already has a count for the number of NULL pointers stored
|
||||
in the tnode. As such save ourselves the cycles and just use
|
||||
empty_children.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -1954,16 +1954,10 @@ static void trie_collect_stats(struct tr
|
||||
hlist_for_each_entry_rcu(li, &n->list, hlist)
|
||||
++s->prefixes;
|
||||
} else {
|
||||
- unsigned long i;
|
||||
-
|
||||
s->tnodes++;
|
||||
if (n->bits < MAX_STAT_DEPTH)
|
||||
s->nodesizes[n->bits]++;
|
||||
-
|
||||
- for (i = tnode_child_length(n); i--;) {
|
||||
- if (!rcu_access_pointer(n->child[i]))
|
||||
- s->nullpointers++;
|
||||
- }
|
||||
+ s->nullpointers += n->empty_children;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
|
@ -1,79 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Thu, 22 Jan 2015 15:51:39 -0800
|
||||
Subject: [PATCH] fib_trie: Move fib_find_alias to file where it is used
|
||||
|
||||
The function fib_find_alias is only accessed by functions in fib_trie.c as
|
||||
such it makes sense to relocate it and cast it as static so that the
|
||||
compiler can take advantage of optimizations it can do to it as a local
|
||||
function.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_lookup.h
|
||||
+++ b/net/ipv4/fib_lookup.h
|
||||
@@ -32,7 +32,6 @@ int fib_dump_info(struct sk_buff *skb, u
|
||||
unsigned int);
|
||||
void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, int dst_len,
|
||||
u32 tb_id, const struct nl_info *info, unsigned int nlm_flags);
|
||||
-struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio);
|
||||
|
||||
static inline void fib_result_assign(struct fib_result *res,
|
||||
struct fib_info *fi)
|
||||
--- a/net/ipv4/fib_semantics.c
|
||||
+++ b/net/ipv4/fib_semantics.c
|
||||
@@ -410,24 +410,6 @@ errout:
|
||||
rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err);
|
||||
}
|
||||
|
||||
-/* Return the first fib alias matching TOS with
|
||||
- * priority less than or equal to PRIO.
|
||||
- */
|
||||
-struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio)
|
||||
-{
|
||||
- if (fah) {
|
||||
- struct fib_alias *fa;
|
||||
- list_for_each_entry(fa, fah, fa_list) {
|
||||
- if (fa->fa_tos > tos)
|
||||
- continue;
|
||||
- if (fa->fa_info->fib_priority >= prio ||
|
||||
- fa->fa_tos < tos)
|
||||
- return fa;
|
||||
- }
|
||||
- }
|
||||
- return NULL;
|
||||
-}
|
||||
-
|
||||
static int fib_detect_death(struct fib_info *fi, int order,
|
||||
struct fib_info **last_resort, int *last_idx,
|
||||
int dflt)
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -998,6 +998,26 @@ static struct tnode *fib_find_node(struc
|
||||
return n;
|
||||
}
|
||||
|
||||
+/* Return the first fib alias matching TOS with
|
||||
+ * priority less than or equal to PRIO.
|
||||
+ */
|
||||
+static struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio)
|
||||
+{
|
||||
+ struct fib_alias *fa;
|
||||
+
|
||||
+ if (!fah)
|
||||
+ return NULL;
|
||||
+
|
||||
+ list_for_each_entry(fa, fah, fa_list) {
|
||||
+ if (fa->fa_tos > tos)
|
||||
+ continue;
|
||||
+ if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos)
|
||||
+ return fa;
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
static void trie_rebalance(struct trie *t, struct tnode *tn)
|
||||
{
|
||||
struct tnode *tp;
|
|
@ -1,116 +0,0 @@
|
|||
From: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Date: Thu, 22 Jan 2015 15:51:45 -0800
|
||||
Subject: [PATCH] fib_trie: Various clean-ups for handling slen
|
||||
|
||||
While doing further work on the fib_trie I noted a few items.
|
||||
|
||||
First I was using calls that were far more complicated than they needed to
|
||||
be for determining when to push/pull the suffix length. I have updated the
|
||||
code to reflect the simplier logic.
|
||||
|
||||
The second issue is that I realised we weren't necessarily handling the
|
||||
case of a leaf_info struct surviving a flush. I have updated the logic so
|
||||
that now we will call pull_suffix in the event of having a leaf info value
|
||||
left in the leaf after flushing it.
|
||||
|
||||
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/ipv4/fib_trie.c
|
||||
+++ b/net/ipv4/fib_trie.c
|
||||
@@ -917,27 +917,20 @@ static void leaf_push_suffix(struct tnod
|
||||
|
||||
static void remove_leaf_info(struct tnode *l, struct leaf_info *old)
|
||||
{
|
||||
- struct hlist_node *prev;
|
||||
-
|
||||
- /* record the location of the pointer to this object */
|
||||
- prev = rtnl_dereference(hlist_pprev_rcu(&old->hlist));
|
||||
+ /* record the location of the previous list_info entry */
|
||||
+ struct hlist_node **pprev = old->hlist.pprev;
|
||||
+ struct leaf_info *li = hlist_entry(pprev, typeof(*li), hlist.next);
|
||||
|
||||
/* remove the leaf info from the list */
|
||||
hlist_del_rcu(&old->hlist);
|
||||
|
||||
- /* if we emptied the list this leaf will be freed and we can sort
|
||||
- * out parent suffix lengths as a part of trie_rebalance
|
||||
- */
|
||||
- if (hlist_empty(&l->list))
|
||||
+ /* only access li if it is pointing at the last valid hlist_node */
|
||||
+ if (hlist_empty(&l->list) || (*pprev))
|
||||
return;
|
||||
|
||||
- /* if we removed the tail then we need to update slen */
|
||||
- if (!rcu_access_pointer(hlist_next_rcu(prev))) {
|
||||
- struct leaf_info *li = hlist_entry(prev, typeof(*li), hlist);
|
||||
-
|
||||
- l->slen = KEYLENGTH - li->plen;
|
||||
- leaf_pull_suffix(l);
|
||||
- }
|
||||
+ /* update the trie with the latest suffix length */
|
||||
+ l->slen = KEYLENGTH - li->plen;
|
||||
+ leaf_pull_suffix(l);
|
||||
}
|
||||
|
||||
static void insert_leaf_info(struct tnode *l, struct leaf_info *new)
|
||||
@@ -961,7 +954,7 @@ static void insert_leaf_info(struct tnod
|
||||
}
|
||||
|
||||
/* if we added to the tail node then we need to update slen */
|
||||
- if (!rcu_access_pointer(hlist_next_rcu(&new->hlist))) {
|
||||
+ if (l->slen < (KEYLENGTH - new->plen)) {
|
||||
l->slen = KEYLENGTH - new->plen;
|
||||
leaf_push_suffix(l);
|
||||
}
|
||||
@@ -1613,6 +1606,7 @@ static int trie_flush_leaf(struct tnode
|
||||
struct hlist_head *lih = &l->list;
|
||||
struct hlist_node *tmp;
|
||||
struct leaf_info *li = NULL;
|
||||
+ unsigned char plen = KEYLENGTH;
|
||||
|
||||
hlist_for_each_entry_safe(li, tmp, lih, hlist) {
|
||||
found += trie_flush_list(&li->falh);
|
||||
@@ -1620,8 +1614,14 @@ static int trie_flush_leaf(struct tnode
|
||||
if (list_empty(&li->falh)) {
|
||||
hlist_del_rcu(&li->hlist);
|
||||
free_leaf_info(li);
|
||||
+ continue;
|
||||
}
|
||||
+
|
||||
+ plen = li->plen;
|
||||
}
|
||||
+
|
||||
+ l->slen = KEYLENGTH - plen;
|
||||
+
|
||||
return found;
|
||||
}
|
||||
|
||||
@@ -1700,13 +1700,22 @@ int fib_table_flush(struct fib_table *tb
|
||||
for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) {
|
||||
found += trie_flush_leaf(l);
|
||||
|
||||
- if (ll && hlist_empty(&ll->list))
|
||||
- trie_leaf_remove(t, ll);
|
||||
+ if (ll) {
|
||||
+ if (hlist_empty(&ll->list))
|
||||
+ trie_leaf_remove(t, ll);
|
||||
+ else
|
||||
+ leaf_pull_suffix(ll);
|
||||
+ }
|
||||
+
|
||||
ll = l;
|
||||
}
|
||||
|
||||
- if (ll && hlist_empty(&ll->list))
|
||||
- trie_leaf_remove(t, ll);
|
||||
+ if (ll) {
|
||||
+ if (hlist_empty(&ll->list))
|
||||
+ trie_leaf_remove(t, ll);
|
||||
+ else
|
||||
+ leaf_pull_suffix(ll);
|
||||
+ }
|
||||
|
||||
pr_debug("trie_flush found=%d\n", found);
|
||||
return found;
|
|
@ -1,89 +0,0 @@
|
|||
From: Simon Farnsworth <simon@farnz.org.uk>
|
||||
Date: Sun, 1 Mar 2015 10:54:39 +0000
|
||||
Subject: [PATCH] pppoe: Use workqueue to die properly when a PADT is received
|
||||
|
||||
When a PADT frame is received, the socket may not be in a good state to
|
||||
close down the PPP interface. The current implementation handles this by
|
||||
simply blocking all further PPP traffic, and hoping that the lack of traffic
|
||||
will trigger the user to investigate.
|
||||
|
||||
Use schedule_work to get to a process context from which we clear down the
|
||||
PPP interface, in a fashion analogous to hangup on a TTY-based PPP
|
||||
interface. This causes pppd to disconnect immediately, and allows tools to
|
||||
take immediate corrective action.
|
||||
|
||||
Note that pppd's rp_pppoe.so plugin has code in it to disable the session
|
||||
when it disconnects; however, as a consequence of this patch, the session is
|
||||
already disabled before rp_pppoe.so is asked to disable the session. The
|
||||
result is a harmless error message:
|
||||
|
||||
Failed to disconnect PPPoE socket: 114 Operation already in progress
|
||||
|
||||
This message is safe to ignore, as long as the error is 114 Operation
|
||||
already in progress; in that specific case, it means that the PPPoE session
|
||||
has already been disabled before pppd tried to disable it.
|
||||
|
||||
Signed-off-by: Simon Farnsworth <simon@farnz.org.uk>
|
||||
Tested-by: Dan Williams <dcbw@redhat.com>
|
||||
Tested-by: Christoph Schulz <develop@kristov.de>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ppp/pppoe.c
|
||||
+++ b/drivers/net/ppp/pppoe.c
|
||||
@@ -454,6 +454,18 @@ out:
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
+static void pppoe_unbind_sock_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct pppox_sock *po = container_of(work, struct pppox_sock,
|
||||
+ proto.pppoe.padt_work);
|
||||
+ struct sock *sk = sk_pppox(po);
|
||||
+
|
||||
+ lock_sock(sk);
|
||||
+ pppox_unbind_sock(sk);
|
||||
+ release_sock(sk);
|
||||
+ sock_put(sk);
|
||||
+}
|
||||
+
|
||||
/************************************************************************
|
||||
*
|
||||
* Receive a PPPoE Discovery frame.
|
||||
@@ -499,7 +511,8 @@ static int pppoe_disc_rcv(struct sk_buff
|
||||
}
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
- sock_put(sk);
|
||||
+ if (!schedule_work(&po->proto.pppoe.padt_work))
|
||||
+ sock_put(sk);
|
||||
}
|
||||
|
||||
abort:
|
||||
@@ -612,6 +625,8 @@ static int pppoe_connect(struct socket *
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
+ INIT_WORK(&po->proto.pppoe.padt_work, pppoe_unbind_sock_work);
|
||||
+
|
||||
error = -EINVAL;
|
||||
if (sp->sa_protocol != PX_PROTO_OE)
|
||||
goto end;
|
||||
--- a/include/linux/if_pppox.h
|
||||
+++ b/include/linux/if_pppox.h
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ppp_channel.h>
|
||||
#include <linux/skbuff.h>
|
||||
+#include <linux/workqueue.h>
|
||||
#include <uapi/linux/if_pppox.h>
|
||||
|
||||
static inline struct pppoe_hdr *pppoe_hdr(const struct sk_buff *skb)
|
||||
@@ -32,6 +33,7 @@ struct pppoe_opt {
|
||||
struct pppoe_addr pa; /* what this socket is bound to*/
|
||||
struct sockaddr_pppox relay; /* what socket data will be
|
||||
relayed to (PPPoE relaying) */
|
||||
+ struct work_struct padt_work;/* Work item for handling PADT */
|
||||
};
|
||||
|
||||
struct pptp_opt {
|
|
@ -1,25 +0,0 @@
|
|||
From: Joakim Tjernlund <Joakim.Tjernlund@transmode.se>
|
||||
Date: Mon, 20 Apr 2015 21:07:48 +0200
|
||||
Subject: [PATCH] pppoe: Lacks DST MAC address check
|
||||
|
||||
A pppoe session is identified by its session ID and MAC address.
|
||||
Currently pppoe does not check if the received pkg has the correct
|
||||
MAC address. This is a problem when the eth I/F is in promisc mode
|
||||
as then any DST MAC address is accepted.
|
||||
|
||||
Signed-off-by: Joakim Tjernlund <joakim.tjernlund@transmode.se>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ppp/pppoe.c
|
||||
+++ b/drivers/net/ppp/pppoe.c
|
||||
@@ -379,6 +379,9 @@ static int pppoe_rcv_core(struct sock *s
|
||||
* can't change.
|
||||
*/
|
||||
|
||||
+ if (skb->pkt_type == PACKET_OTHERHOST)
|
||||
+ goto abort_kfree;
|
||||
+
|
||||
if (sk->sk_state & PPPOX_BOUND) {
|
||||
ppp_input(&po->chan, skb);
|
||||
} else if (sk->sk_state & PPPOX_RELAY) {
|
|
@ -1,28 +0,0 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 9 May 2015 23:08:38 +0200
|
||||
Subject: [PATCH] pppoe: drop pppoe device in pppoe_unbind_sock_work
|
||||
|
||||
After receiving a PADT and the socket is closed, user space will no
|
||||
longer drop the reference to the pppoe device.
|
||||
This leads to errors like this:
|
||||
|
||||
[ 488.570000] unregister_netdevice: waiting for eth0.2 to become free. Usage count = 2
|
||||
|
||||
Fixes: 287f3a943fe ("pppoe: Use workqueue to die properly when a PADT is received")
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ppp/pppoe.c
|
||||
+++ b/drivers/net/ppp/pppoe.c
|
||||
@@ -464,6 +464,10 @@ static void pppoe_unbind_sock_work(struc
|
||||
struct sock *sk = sk_pppox(po);
|
||||
|
||||
lock_sock(sk);
|
||||
+ if (po->pppoe_dev) {
|
||||
+ dev_put(po->pppoe_dev);
|
||||
+ po->pppoe_dev = NULL;
|
||||
+ }
|
||||
pppox_unbind_sock(sk);
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
|
@ -1,45 +0,0 @@
|
|||
From: Guillaume Nault <g.nault@alphalink.fr>
|
||||
Date: Thu, 19 Nov 2015 12:52:56 +0100
|
||||
Subject: [PATCH] ppp: don't set sk_state to PPPOX_ZOMBIE in pppoe_disc_rcv()
|
||||
|
||||
Since 287f3a943fef ("pppoe: Use workqueue to die properly when a PADT
|
||||
is received"), pppoe_disc_rcv() disconnects the socket by scheduling
|
||||
pppoe_unbind_sock_work(). This is enough to stop socket transmission
|
||||
and makes the PPPOX_ZOMBIE state uncessary.
|
||||
|
||||
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ppp/pppoe.c
|
||||
+++ b/drivers/net/ppp/pppoe.c
|
||||
@@ -500,27 +500,9 @@ static int pppoe_disc_rcv(struct sk_buff
|
||||
|
||||
pn = pppoe_pernet(dev_net(dev));
|
||||
po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
|
||||
- if (po) {
|
||||
- struct sock *sk = sk_pppox(po);
|
||||
-
|
||||
- bh_lock_sock(sk);
|
||||
-
|
||||
- /* If the user has locked the socket, just ignore
|
||||
- * the packet. With the way two rcv protocols hook into
|
||||
- * one socket family type, we cannot (easily) distinguish
|
||||
- * what kind of SKB it is during backlog rcv.
|
||||
- */
|
||||
- if (sock_owned_by_user(sk) == 0) {
|
||||
- /* We're no longer connect at the PPPOE layer,
|
||||
- * and must wait for ppp channel to disconnect us.
|
||||
- */
|
||||
- sk->sk_state = PPPOX_ZOMBIE;
|
||||
- }
|
||||
-
|
||||
- bh_unlock_sock(sk);
|
||||
+ if (po)
|
||||
if (!schedule_work(&po->proto.pppoe.padt_work))
|
||||
- sock_put(sk);
|
||||
- }
|
||||
+ sock_put(sk_pppox(po));
|
||||
|
||||
abort:
|
||||
kfree_skb(skb);
|
|
@ -1,51 +0,0 @@
|
|||
From: Guillaume Nault <g.nault@alphalink.fr>
|
||||
Date: Thu, 19 Nov 2015 12:53:21 +0100
|
||||
Subject: [PATCH] ppp: remove PPPOX_ZOMBIE socket state
|
||||
|
||||
PPPOX_ZOMBIE is never set anymore.
|
||||
|
||||
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ppp/pppoe.c
|
||||
+++ b/drivers/net/ppp/pppoe.c
|
||||
@@ -311,7 +311,7 @@ static void pppoe_flush_dev(struct net_d
|
||||
lock_sock(sk);
|
||||
|
||||
if (po->pppoe_dev == dev &&
|
||||
- sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
|
||||
+ sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
|
||||
pppox_unbind_sock(sk);
|
||||
sk->sk_state_change(sk);
|
||||
po->pppoe_dev = NULL;
|
||||
@@ -775,7 +775,7 @@ static int pppoe_ioctl(struct socket *so
|
||||
struct pppox_sock *relay_po;
|
||||
|
||||
err = -EBUSY;
|
||||
- if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD))
|
||||
+ if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD))
|
||||
break;
|
||||
|
||||
err = -ENOTCONN;
|
||||
--- a/drivers/net/ppp/pppox.c
|
||||
+++ b/drivers/net/ppp/pppox.c
|
||||
@@ -58,7 +58,7 @@ void pppox_unbind_sock(struct sock *sk)
|
||||
{
|
||||
/* Clear connection to ppp device, if attached. */
|
||||
|
||||
- if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
|
||||
+ if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) {
|
||||
ppp_unregister_channel(&pppox_sk(sk)->chan);
|
||||
sk->sk_state = PPPOX_DEAD;
|
||||
}
|
||||
--- a/include/linux/if_pppox.h
|
||||
+++ b/include/linux/if_pppox.h
|
||||
@@ -91,7 +91,6 @@ enum {
|
||||
PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */
|
||||
PPPOX_BOUND = 2, /* bound to ppp device */
|
||||
PPPOX_RELAY = 4, /* forwarding is enabled */
|
||||
- PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */
|
||||
PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/
|
||||
};
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
From: Guillaume Nault <g.nault@alphalink.fr>
|
||||
Date: Thu, 3 Dec 2015 16:49:32 +0100
|
||||
Subject: [PATCH] pppoe: fix memory corruption in padt work structure
|
||||
|
||||
pppoe_connect() mustn't touch the padt_work field of pppoe sockets
|
||||
because that work could be already pending.
|
||||
|
||||
[ 21.473147] BUG: unable to handle kernel NULL pointer dereference at 00000004
|
||||
[ 21.474523] IP: [<c1043177>] process_one_work+0x29/0x31c
|
||||
[ 21.475164] *pde = 00000000
|
||||
[ 21.475513] Oops: 0000 [#1] SMP
|
||||
[ 21.475910] Modules linked in: pppoe pppox ppp_generic slhc crc32c_intel aesni_intel virtio_net xts aes_i586 lrw gf128mul ablk_helper cryptd evdev acpi_cpufreq processor serio_raw button ext4 crc16 mbcache jbd2 virtio_blk virtio_pci virtio_ring virtio
|
||||
[ 21.476168] CPU: 2 PID: 164 Comm: kworker/2:2 Not tainted 4.4.0-rc1 #1
|
||||
[ 21.476168] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Debian-1.8.2-1 04/01/2014
|
||||
[ 21.476168] task: f5f83c00 ti: f5e28000 task.ti: f5e28000
|
||||
[ 21.476168] EIP: 0060:[<c1043177>] EFLAGS: 00010046 CPU: 2
|
||||
[ 21.476168] EIP is at process_one_work+0x29/0x31c
|
||||
[ 21.484082] EAX: 00000000 EBX: f678b2a0 ECX: 00000004 EDX: 00000000
|
||||
[ 21.484082] ESI: f6c69940 EDI: f5e29ef0 EBP: f5e29f0c ESP: f5e29edc
|
||||
[ 21.484082] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
|
||||
[ 21.484082] CR0: 80050033 CR2: 000000a4 CR3: 317ad000 CR4: 00040690
|
||||
[ 21.484082] Stack:
|
||||
[ 21.484082] 00000000 f6c69950 00000000 f6c69940 c0042338 f5e29f0c c1327945 00000000
|
||||
[ 21.484082] 00000008 f678b2a0 f6c69940 f678b2b8 f5e29f30 c1043984 f5f83c00 f6c69970
|
||||
[ 21.484082] f678b2a0 c10437d3 f6775e80 f678b2a0 c10437d3 f5e29fac c1047059 f5e29f74
|
||||
[ 21.484082] Call Trace:
|
||||
[ 21.484082] [<c1327945>] ? _raw_spin_lock_irq+0x28/0x30
|
||||
[ 21.484082] [<c1043984>] worker_thread+0x1b1/0x244
|
||||
[ 21.484082] [<c10437d3>] ? rescuer_thread+0x229/0x229
|
||||
[ 21.484082] [<c10437d3>] ? rescuer_thread+0x229/0x229
|
||||
[ 21.484082] [<c1047059>] kthread+0x8f/0x94
|
||||
[ 21.484082] [<c1327a32>] ? _raw_spin_unlock_irq+0x22/0x26
|
||||
[ 21.484082] [<c1327ee9>] ret_from_kernel_thread+0x21/0x38
|
||||
[ 21.484082] [<c1046fca>] ? kthread_parkme+0x19/0x19
|
||||
[ 21.496082] Code: 5d c3 55 89 e5 57 56 53 89 c3 83 ec 24 89 d0 89 55 e0 8d 7d e4 e8 6c d8 ff ff b9 04 00 00 00 89 45 d8 8b 43 24 89 45 dc 8b 45 d8 <8b> 40 04 8b 80 e0 00 00 00 c1 e8 05 24 01 88 45 d7 8b 45 e0 8d
|
||||
[ 21.496082] EIP: [<c1043177>] process_one_work+0x29/0x31c SS:ESP 0068:f5e29edc
|
||||
[ 21.496082] CR2: 0000000000000004
|
||||
[ 21.496082] ---[ end trace e362cc9cf10dae89 ]---
|
||||
|
||||
Reported-by: Andrew <nitr0@seti.kr.ua>
|
||||
Fixes: 287f3a943fef ("pppoe: Use workqueue to die properly when a PADT is received")
|
||||
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ppp/pppoe.c
|
||||
+++ b/drivers/net/ppp/pppoe.c
|
||||
@@ -550,6 +550,9 @@ static int pppoe_create(struct net *net,
|
||||
sk->sk_family = PF_PPPOX;
|
||||
sk->sk_protocol = PX_PROTO_OE;
|
||||
|
||||
+ INIT_WORK(&pppox_sk(sk)->proto.pppoe.padt_work,
|
||||
+ pppoe_unbind_sock_work);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -614,8 +617,6 @@ static int pppoe_connect(struct socket *
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
- INIT_WORK(&po->proto.pppoe.padt_work, pppoe_unbind_sock_work);
|
||||
-
|
||||
error = -EINVAL;
|
||||
if (sp->sa_protocol != PX_PROTO_OE)
|
||||
goto end;
|
||||
@@ -645,8 +646,13 @@ static int pppoe_connect(struct socket *
|
||||
po->pppoe_dev = NULL;
|
||||
}
|
||||
|
||||
- memset(sk_pppox(po) + 1, 0,
|
||||
- sizeof(struct pppox_sock) - sizeof(struct sock));
|
||||
+ po->pppoe_ifindex = 0;
|
||||
+ memset(&po->pppoe_pa, 0, sizeof(po->pppoe_pa));
|
||||
+ memset(&po->pppoe_relay, 0, sizeof(po->pppoe_relay));
|
||||
+ memset(&po->chan, 0, sizeof(po->chan));
|
||||
+ po->next = NULL;
|
||||
+ po->num = 0;
|
||||
+
|
||||
sk->sk_state = PPPOX_NONE;
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Thu, 17 Sep 2015 11:24:48 +0100
|
||||
Subject: [PATCH] ipv6: ip6_fragment: fix headroom tests and skb leak
|
||||
|
||||
David Woodhouse reports skb_under_panic when we try to push ethernet
|
||||
header to fragmented ipv6 skbs:
|
||||
|
||||
skbuff: skb_under_panic: text:c1277f1e len:1294 put:14 head:dec98000
|
||||
data:dec97ffc tail:0xdec9850a end:0xdec98f40 dev:br-lan
|
||||
[..]
|
||||
ip6_finish_output2+0x196/0x4da
|
||||
|
||||
David further debugged this:
|
||||
[..] offending fragments were arriving here with skb_headroom(skb)==10.
|
||||
Which is reasonable, being the Solos ADSL card's header of 8 bytes
|
||||
followed by 2 bytes of PPP frame type.
|
||||
|
||||
The problem is that if netfilter ipv6 defragmentation is used, skb_cow()
|
||||
in ip6_forward will only see reassembled skb.
|
||||
|
||||
Therefore, headroom is overestimated by 8 bytes (we pulled fragment
|
||||
header) and we don't check the skbs in the frag_list either.
|
||||
|
||||
We can't do these checks in netfilter defrag since outdev isn't known yet.
|
||||
|
||||
Furthermore, existing tests in ip6_fragment did not consider the fragment
|
||||
or ipv6 header size when checking headroom of the fraglist skbs.
|
||||
|
||||
While at it, also fix a skb leak on memory allocation -- ip6_fragment
|
||||
must consume the skb.
|
||||
|
||||
I tested this e1000 driver hacked to not allocate additional headroom
|
||||
(we end up in slowpath, since LL_RESERVED_SPACE is 16).
|
||||
|
||||
If 2 bytes of headroom are allocated, fastpath is taken (14 byte
|
||||
ethernet header was pulled, so 16 byte headroom available in all
|
||||
fragments).
|
||||
|
||||
Reported-by: David Woodhouse <dwmw2@infradead.org>
|
||||
Diagnosed-by: David Woodhouse <dwmw2@infradead.org>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Closes 20532
|
||||
---
|
||||
|
||||
--- a/net/ipv6/ip6_output.c
|
||||
+++ b/net/ipv6/ip6_output.c
|
||||
@@ -600,20 +600,22 @@ int ip6_fragment(struct sk_buff *skb, in
|
||||
}
|
||||
mtu -= hlen + sizeof(struct frag_hdr);
|
||||
|
||||
+ hroom = LL_RESERVED_SPACE(rt->dst.dev);
|
||||
if (skb_has_frag_list(skb)) {
|
||||
int first_len = skb_pagelen(skb);
|
||||
struct sk_buff *frag2;
|
||||
|
||||
if (first_len - hlen > mtu ||
|
||||
((first_len - hlen) & 7) ||
|
||||
- skb_cloned(skb))
|
||||
+ skb_cloned(skb) ||
|
||||
+ skb_headroom(skb) < (hroom + sizeof(struct frag_hdr)))
|
||||
goto slow_path;
|
||||
|
||||
skb_walk_frags(skb, frag) {
|
||||
/* Correct geometry. */
|
||||
if (frag->len > mtu ||
|
||||
((frag->len & 7) && frag->next) ||
|
||||
- skb_headroom(frag) < hlen)
|
||||
+ skb_headroom(frag) < (hlen + hroom + sizeof(struct frag_hdr)))
|
||||
goto slow_path_clean;
|
||||
|
||||
/* Partially cloned skb? */
|
||||
@@ -630,8 +632,6 @@ int ip6_fragment(struct sk_buff *skb, in
|
||||
|
||||
err = 0;
|
||||
offset = 0;
|
||||
- frag = skb_shinfo(skb)->frag_list;
|
||||
- skb_frag_list_init(skb);
|
||||
/* BUILD HEADER */
|
||||
|
||||
*prevhdr = NEXTHDR_FRAGMENT;
|
||||
@@ -639,8 +639,11 @@ int ip6_fragment(struct sk_buff *skb, in
|
||||
if (!tmp_hdr) {
|
||||
IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
||||
IPSTATS_MIB_FRAGFAILS);
|
||||
- return -ENOMEM;
|
||||
+ err = -ENOMEM;
|
||||
+ goto fail;
|
||||
}
|
||||
+ frag = skb_shinfo(skb)->frag_list;
|
||||
+ skb_frag_list_init(skb);
|
||||
|
||||
__skb_pull(skb, hlen);
|
||||
fh = (struct frag_hdr *)__skb_push(skb, sizeof(struct frag_hdr));
|
||||
@@ -738,7 +741,6 @@ slow_path:
|
||||
*/
|
||||
|
||||
*prevhdr = NEXTHDR_FRAGMENT;
|
||||
- hroom = LL_RESERVED_SPACE(rt->dst.dev);
|
||||
troom = rt->dst.dev->needed_tailroom;
|
||||
|
||||
/*
|
|
@ -1,54 +0,0 @@
|
|||
From: David Woodhouse <dwmw2@infradead.org>
|
||||
Date: Thu, 17 Sep 2015 11:19:53 +0100
|
||||
Subject: [PATCH] solos-pci: Increase headroom on received packets
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
A comment in include/linux/skbuff.h says that:
|
||||
|
||||
* Various parts of the networking layer expect at least 32 bytes of
|
||||
* headroom, you should not reduce this.
|
||||
|
||||
This was demonstrated by a panic when handling fragmented IPv6 packets:
|
||||
http://marc.info/?l=linux-netdev&m=144236093519172&w=2
|
||||
|
||||
It's not entirely clear if that comment is still valid — and if it is,
|
||||
perhaps netif_rx() ought to be enforcing it with a warning.
|
||||
|
||||
But either way, it is rather stupid from a performance point of view
|
||||
for us to be receiving packets into a buffer which doesn't have enough
|
||||
room to prepend an Ethernet header — it means that *every* incoming
|
||||
packet is going to be need to be reallocated. So let's fix that.
|
||||
|
||||
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
|
||||
---
|
||||
|
||||
--- a/drivers/atm/solos-pci.c
|
||||
+++ b/drivers/atm/solos-pci.c
|
||||
@@ -805,7 +805,12 @@ static void solos_bh(unsigned long card_
|
||||
continue;
|
||||
}
|
||||
|
||||
- skb = alloc_skb(size + 1, GFP_ATOMIC);
|
||||
+ /* Use netdev_alloc_skb() because it adds NET_SKB_PAD of
|
||||
+ * headroom, and ensures we can route packets back out an
|
||||
+ * Ethernet interface (for example) without having to
|
||||
+ * reallocate. Adding NET_IP_ALIGN also ensures that both
|
||||
+ * PPPoATM and PPPoEoBR2684 packets end up aligned. */
|
||||
+ skb = netdev_alloc_skb_ip_align(NULL, size + 1);
|
||||
if (!skb) {
|
||||
if (net_ratelimit())
|
||||
dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n");
|
||||
@@ -869,7 +874,10 @@ static void solos_bh(unsigned long card_
|
||||
/* Allocate RX skbs for any ports which need them */
|
||||
if (card->using_dma && card->atmdev[port] &&
|
||||
!card->rx_skb[port]) {
|
||||
- struct sk_buff *skb = alloc_skb(RX_DMA_SIZE, GFP_ATOMIC);
|
||||
+ /* Unlike the MMIO case (qv) we can't add NET_IP_ALIGN
|
||||
+ * here; the FPGA can only DMA to addresses which are
|
||||
+ * aligned to 4 bytes. */
|
||||
+ struct sk_buff *skb = dev_alloc_skb(RX_DMA_SIZE);
|
||||
if (skb) {
|
||||
SKB_CB(skb)->dma_addr =
|
||||
pci_map_single(card->dev, skb->data,
|
|
@ -1,67 +0,0 @@
|
|||
From de88e9b0354c2e3ff8eae3f97afe43a34f5ed239 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Sat, 13 May 2017 13:03:21 +0200
|
||||
Subject: [PATCH] regmap: make LZO cache optional
|
||||
|
||||
Commit 2cbbb579bcbe3 ("regmap: Add the LZO cache support") added support
|
||||
for LZO compression in regcache, but there were never any users added
|
||||
afterwards. Since LZO support itself has its own size, it currently is
|
||||
rather a deoptimization.
|
||||
|
||||
So make it optional by introducing a symbol that can be selected by
|
||||
drivers wanting to make use of it.
|
||||
|
||||
Saves e.g. ~46 kB on MIPS (size of LZO support + regcache LZO code).
|
||||
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
I tried using google to find any users (even out-of-tree ones), but at
|
||||
best I found a single driver submission that was switched to RBTREE in
|
||||
subsequent resubmissions (MFD_SMSC).
|
||||
|
||||
One could maybe also just drop the code because of no users for 5 years,
|
||||
but that would be up to the maintainer(s) to decide.
|
||||
|
||||
drivers/base/regmap/Kconfig | 5 ++++-
|
||||
drivers/base/regmap/Makefile | 3 ++-
|
||||
drivers/base/regmap/regcache.c | 2 ++
|
||||
3 files changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/base/regmap/Kconfig
|
||||
+++ b/drivers/base/regmap/Kconfig
|
||||
@@ -4,9 +4,12 @@
|
||||
|
||||
config REGMAP
|
||||
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
|
||||
+ select IRQ_DOMAIN if REGMAP_IRQ
|
||||
+ bool
|
||||
+
|
||||
+config REGCACHE_COMPRESSED
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
- select IRQ_DOMAIN if REGMAP_IRQ
|
||||
bool
|
||||
|
||||
config REGMAP_I2C
|
||||
--- a/drivers/base/regmap/Makefile
|
||||
+++ b/drivers/base/regmap/Makefile
|
||||
@@ -1,5 +1,6 @@
|
||||
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
|
||||
-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
|
||||
+obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o
|
||||
+obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
|
||||
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
|
||||
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
|
||||
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
||||
--- a/drivers/base/regmap/regcache.c
|
||||
+++ b/drivers/base/regmap/regcache.c
|
||||
@@ -21,7 +21,9 @@
|
||||
|
||||
static const struct regcache_ops *cache_types[] = {
|
||||
®cache_rbtree_ops,
|
||||
+#if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED)
|
||||
®cache_lzo_ops,
|
||||
+#endif
|
||||
®cache_flat_ops,
|
||||
};
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
[linux-unionfs added to Cc]
|
||||
|
||||
On Tue, May 19, 2015 at 09:51:20AM +0200, Bastian Bittorf wrote:
|
||||
> Hi Miklos,
|
||||
>
|
||||
> sorry for writing directly to you, feel free to forward
|
||||
> this to the appropriate mailinglist.
|
||||
>
|
||||
> we have a problem with mainline overlay filesystem on kernel 3.18:
|
||||
> https://dev.openwrt.org/ticket/19564
|
||||
>
|
||||
> 2 things are odd:
|
||||
> when the working filesystem is full, overlays fails with:
|
||||
>
|
||||
> overlayfs: failed to create directory /overlay/work/work
|
||||
>
|
||||
> what is strange, that we call it with:
|
||||
>
|
||||
> mount(overlay, "/mnt", "overlay", MS_NOATIME, lowerdir)
|
||||
>
|
||||
> see here:
|
||||
> http://nbd.name/gitweb.cgi?p=fstools.git;a=blob;f=libfstools/mount.c;h=81176ce399b4cd8e2d347c0008c13dec92407f55;hb=e6004000ff15d7bd32cf5663e8690fc94d7ec747#l125
|
||||
>
|
||||
> do you have an idea whats wrong?
|
||||
> 1) is it really needed, that we need space for creating dir "/overlay/work"?
|
||||
> 2) why does overlay need "/overlay/work/work"?
|
||||
|
||||
The work directory is needed for atomic copy-up and similar. It is not actually
|
||||
necessary to mount a read-only overlay. Post 4.0 it is possible to mount the
|
||||
overlay without workdir (but even then it won't happen automatically in case the
|
||||
upper fs is full, so this should be fixed in the latest kernel too).
|
||||
|
||||
Could you please try the following patch? If the workdir can't be created it
|
||||
will fall back to mounting the overlay read-only.
|
||||
|
||||
Thanks,
|
||||
Miklos
|
||||
|
||||
---
|
||||
fs/overlayfs/copy_up.c | 3 +++
|
||||
fs/overlayfs/dir.c | 9 +++++++++
|
||||
fs/overlayfs/super.c | 12 +++++++++---
|
||||
3 files changed, 21 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/fs/overlayfs/copy_up.c
|
||||
+++ b/fs/overlayfs/copy_up.c
|
||||
@@ -315,6 +315,9 @@ int ovl_copy_up_one(struct dentry *paren
|
||||
struct cred *override_cred;
|
||||
char *link = NULL;
|
||||
|
||||
+ if (WARN_ON(!workdir))
|
||||
+ return -EROFS;
|
||||
+
|
||||
ovl_path_upper(parent, &parentpath);
|
||||
upperdir = parentpath.dentry;
|
||||
|
||||
--- a/fs/overlayfs/dir.c
|
||||
+++ b/fs/overlayfs/dir.c
|
||||
@@ -222,6 +222,9 @@ static struct dentry *ovl_clear_empty(st
|
||||
struct kstat stat;
|
||||
int err;
|
||||
|
||||
+ if (WARN_ON(!workdir))
|
||||
+ return ERR_PTR(-EROFS);
|
||||
+
|
||||
err = ovl_lock_rename_workdir(workdir, upperdir);
|
||||
if (err)
|
||||
goto out;
|
||||
@@ -322,6 +325,9 @@ static int ovl_create_over_whiteout(stru
|
||||
struct dentry *newdentry;
|
||||
int err;
|
||||
|
||||
+ if (WARN_ON(!workdir))
|
||||
+ return -EROFS;
|
||||
+
|
||||
err = ovl_lock_rename_workdir(workdir, upperdir);
|
||||
if (err)
|
||||
goto out;
|
||||
@@ -507,6 +513,9 @@ static int ovl_remove_and_whiteout(struc
|
||||
int err;
|
||||
int flags = 0;
|
||||
|
||||
+ if (WARN_ON(!workdir))
|
||||
+ return -EROFS;
|
||||
+
|
||||
if (is_dir) {
|
||||
opaquedir = ovl_check_empty_and_clear(dentry);
|
||||
err = PTR_ERR(opaquedir);
|
||||
--- a/fs/overlayfs/super.c
|
||||
+++ b/fs/overlayfs/super.c
|
||||
@@ -760,9 +760,15 @@ static int ovl_fill_super(struct super_b
|
||||
ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
|
||||
err = PTR_ERR(ufs->workdir);
|
||||
if (IS_ERR(ufs->workdir)) {
|
||||
- pr_err("overlayfs: failed to create directory %s/%s\n",
|
||||
- ufs->config.workdir, OVL_WORKDIR_NAME);
|
||||
- goto out_put_lower_mnt;
|
||||
+ if (err == -ENOSPC || err == -EROFS) {
|
||||
+ pr_warning("overlayfs: failed to create work directory (%s), mounting read-only\n", err == ENOSPC ? "ENOSPC" : "EROFS");
|
||||
+ sb->s_flags |= MS_RDONLY;
|
||||
+ ufs->workdir = NULL;
|
||||
+ } else {
|
||||
+ pr_err("overlayfs: failed to create directory %s/%s\n",
|
||||
+ ufs->config.workdir, OVL_WORKDIR_NAME);
|
||||
+ goto out_put_lower_mnt;
|
||||
+ }
|
||||
}
|
||||
|
||||
/*
|
|
@ -1,10 +0,0 @@
|
|||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -567,6 +567,7 @@ static const struct spi_device_id spi_no
|
||||
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
+ { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
|
@ -1,47 +0,0 @@
|
|||
From: Mark Brown <broonie@kernel.org>
|
||||
Date: Tue, 9 Dec 2014 19:46:56 +0000
|
||||
Subject: [PATCH] spi: Check to see if the device is processing a message
|
||||
before we idle
|
||||
|
||||
cur_msg is updated under the queue lock and holds the message we are
|
||||
currently processing. Since currently we only ever do removals in the
|
||||
pump kthread it doesn't matter in what order we do things but we want
|
||||
to be able to push things out from the submitting thread so pull the
|
||||
check to see if we're currently handling a message before we check to
|
||||
see if the queue is idle.
|
||||
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
|
||||
--- a/drivers/spi/spi.c
|
||||
+++ b/drivers/spi/spi.c
|
||||
@@ -891,8 +891,16 @@ static void spi_pump_messages(struct kth
|
||||
bool was_busy = false;
|
||||
int ret;
|
||||
|
||||
- /* Lock queue and check for queue work */
|
||||
+ /* Lock queue */
|
||||
spin_lock_irqsave(&master->queue_lock, flags);
|
||||
+
|
||||
+ /* Make sure we are not already running a message */
|
||||
+ if (master->cur_msg) {
|
||||
+ spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Check if the queue is idle */
|
||||
if (list_empty(&master->queue) || !master->running) {
|
||||
if (!master->busy) {
|
||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
@@ -916,11 +924,6 @@ static void spi_pump_messages(struct kth
|
||||
return;
|
||||
}
|
||||
|
||||
- /* Make sure we are not already running a message */
|
||||
- if (master->cur_msg) {
|
||||
- spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
- return;
|
||||
- }
|
||||
/* Extract head of queue */
|
||||
master->cur_msg =
|
||||
list_first_entry(&master->queue, struct spi_message, queue);
|
|
@ -1,184 +0,0 @@
|
|||
From: Mark Brown <broonie@kernel.org>
|
||||
Date: Tue, 9 Dec 2014 21:38:05 +0000
|
||||
Subject: [PATCH] spi: Pump transfers inside calling context for spi_sync()
|
||||
|
||||
If we are using the standard SPI message pump (which all drivers should be
|
||||
transitioning over to) then special case the message enqueue and instead of
|
||||
starting the worker thread to push messages to the hardware do so in the
|
||||
context of the caller if the controller is idle. This avoids a context
|
||||
switch in the common case where the controller has a single user in a
|
||||
single thread, for short PIO transfers there may be no need to context
|
||||
switch away from the calling context to complete the transfer.
|
||||
|
||||
The code is a bit more complex than is desirable in part due to the need
|
||||
to handle drivers not using the standard queue and in part due to handling
|
||||
the various combinations of bus locking and asynchronous submission in
|
||||
interrupt context.
|
||||
|
||||
It is still suboptimal since it will still wake the message pump for each
|
||||
transfer in order to schedule idling of the hardware and if multiple
|
||||
contexts are using the controller simultaneously a caller may end up
|
||||
pumping a message for some random other thread rather than for itself,
|
||||
and if the thread ends up deferring due to another context idling the
|
||||
hardware then it will just busy wait. It can, however, have the benefit
|
||||
of aggregating power up and down of the hardware when a caller performs
|
||||
a series of transfers back to back without any need for the use of
|
||||
spi_async().
|
||||
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
|
||||
--- a/drivers/spi/spi.c
|
||||
+++ b/drivers/spi/spi.c
|
||||
@@ -882,6 +882,9 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_t
|
||||
* needs processing and if so call out to the driver to initialize hardware
|
||||
* and transfer each message.
|
||||
*
|
||||
+ * Note that it is called both from the kthread itself and also from
|
||||
+ * inside spi_sync(); the queue extraction handling at the top of the
|
||||
+ * function should deal with this safely.
|
||||
*/
|
||||
static void spi_pump_messages(struct kthread_work *work)
|
||||
{
|
||||
@@ -900,6 +903,13 @@ static void spi_pump_messages(struct kth
|
||||
return;
|
||||
}
|
||||
|
||||
+ /* If another context is idling the device then defer */
|
||||
+ if (master->idling) {
|
||||
+ queue_kthread_work(&master->kworker, &master->pump_messages);
|
||||
+ spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
/* Check if the queue is idle */
|
||||
if (list_empty(&master->queue) || !master->running) {
|
||||
if (!master->busy) {
|
||||
@@ -907,7 +917,9 @@ static void spi_pump_messages(struct kth
|
||||
return;
|
||||
}
|
||||
master->busy = false;
|
||||
+ master->idling = true;
|
||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
+
|
||||
kfree(master->dummy_rx);
|
||||
master->dummy_rx = NULL;
|
||||
kfree(master->dummy_tx);
|
||||
@@ -921,6 +933,10 @@ static void spi_pump_messages(struct kth
|
||||
pm_runtime_put_autosuspend(master->dev.parent);
|
||||
}
|
||||
trace_spi_master_idle(master);
|
||||
+
|
||||
+ spin_lock_irqsave(&master->queue_lock, flags);
|
||||
+ master->idling = false;
|
||||
+ spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1166,12 +1182,9 @@ static int spi_destroy_queue(struct spi_
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * spi_queued_transfer - transfer function for queued transfers
|
||||
- * @spi: spi device which is requesting transfer
|
||||
- * @msg: spi message which is to handled is queued to driver queue
|
||||
- */
|
||||
-static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
|
||||
+static int __spi_queued_transfer(struct spi_device *spi,
|
||||
+ struct spi_message *msg,
|
||||
+ bool need_pump)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
unsigned long flags;
|
||||
@@ -1186,13 +1199,23 @@ static int spi_queued_transfer(struct sp
|
||||
msg->status = -EINPROGRESS;
|
||||
|
||||
list_add_tail(&msg->queue, &master->queue);
|
||||
- if (!master->busy)
|
||||
+ if (!master->busy && need_pump)
|
||||
queue_kthread_work(&master->kworker, &master->pump_messages);
|
||||
|
||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * spi_queued_transfer - transfer function for queued transfers
|
||||
+ * @spi: spi device which is requesting transfer
|
||||
+ * @msg: spi message which is to handled is queued to driver queue
|
||||
+ */
|
||||
+static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
|
||||
+{
|
||||
+ return __spi_queued_transfer(spi, msg, true);
|
||||
+}
|
||||
+
|
||||
static int spi_master_initialize_queue(struct spi_master *master)
|
||||
{
|
||||
int ret;
|
||||
@@ -2104,19 +2127,46 @@ static int __spi_sync(struct spi_device
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
int status;
|
||||
struct spi_master *master = spi->master;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ status = __spi_validate(spi, message);
|
||||
+ if (status != 0)
|
||||
+ return status;
|
||||
|
||||
message->complete = spi_complete;
|
||||
message->context = &done;
|
||||
+ message->spi = spi;
|
||||
|
||||
if (!bus_locked)
|
||||
mutex_lock(&master->bus_lock_mutex);
|
||||
|
||||
- status = spi_async_locked(spi, message);
|
||||
+ /* If we're not using the legacy transfer method then we will
|
||||
+ * try to transfer in the calling context so special case.
|
||||
+ * This code would be less tricky if we could remove the
|
||||
+ * support for driver implemented message queues.
|
||||
+ */
|
||||
+ if (master->transfer == spi_queued_transfer) {
|
||||
+ spin_lock_irqsave(&master->bus_lock_spinlock, flags);
|
||||
+
|
||||
+ trace_spi_message_submit(message);
|
||||
+
|
||||
+ status = __spi_queued_transfer(spi, message, false);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
|
||||
+ } else {
|
||||
+ status = spi_async_locked(spi, message);
|
||||
+ }
|
||||
|
||||
if (!bus_locked)
|
||||
mutex_unlock(&master->bus_lock_mutex);
|
||||
|
||||
if (status == 0) {
|
||||
+ /* Push out the messages in the calling context if we
|
||||
+ * can.
|
||||
+ */
|
||||
+ if (master->transfer == spi_queued_transfer)
|
||||
+ spi_pump_messages(&master->pump_messages);
|
||||
+
|
||||
wait_for_completion(&done);
|
||||
status = message->status;
|
||||
}
|
||||
--- a/include/linux/spi/spi.h
|
||||
+++ b/include/linux/spi/spi.h
|
||||
@@ -260,6 +260,7 @@ static inline void spi_unregister_driver
|
||||
* @pump_messages: work struct for scheduling work to the message pump
|
||||
* @queue_lock: spinlock to syncronise access to message queue
|
||||
* @queue: message queue
|
||||
+ * @idling: the device is entering idle state
|
||||
* @cur_msg: the currently in-flight message
|
||||
* @cur_msg_prepared: spi_prepare_message was called for the currently
|
||||
* in-flight message
|
||||
@@ -425,6 +426,7 @@ struct spi_master {
|
||||
spinlock_t queue_lock;
|
||||
struct list_head queue;
|
||||
struct spi_message *cur_msg;
|
||||
+ bool idling;
|
||||
bool busy;
|
||||
bool running;
|
||||
bool rt;
|
|
@ -1,83 +0,0 @@
|
|||
From: Mark Brown <broonie@kernel.org>
|
||||
Date: Wed, 10 Dec 2014 13:46:33 +0000
|
||||
Subject: [PATCH] spi: Only idle the message pump in the worker kthread
|
||||
|
||||
In order to avoid the situation where the kthread is waiting for another
|
||||
context to make the hardware idle let the message pump know if it's being
|
||||
called from the worker thread context and if it isn't then defer to the
|
||||
worker thread instead of idling the hardware immediately. This will ensure
|
||||
that if this situation happens we block rather than busy waiting.
|
||||
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
|
||||
--- a/drivers/spi/spi.c
|
||||
+++ b/drivers/spi/spi.c
|
||||
@@ -875,8 +875,9 @@ void spi_finalize_current_transfer(struc
|
||||
EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
|
||||
|
||||
/**
|
||||
- * spi_pump_messages - kthread work function which processes spi message queue
|
||||
- * @work: pointer to kthread work struct contained in the master struct
|
||||
+ * __spi_pump_messages - function which processes spi message queue
|
||||
+ * @master: master to process queue for
|
||||
+ * @in_kthread: true if we are in the context of the message pump thread
|
||||
*
|
||||
* This function checks if there is any spi message in the queue that
|
||||
* needs processing and if so call out to the driver to initialize hardware
|
||||
@@ -886,10 +887,8 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_t
|
||||
* inside spi_sync(); the queue extraction handling at the top of the
|
||||
* function should deal with this safely.
|
||||
*/
|
||||
-static void spi_pump_messages(struct kthread_work *work)
|
||||
+static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
|
||||
{
|
||||
- struct spi_master *master =
|
||||
- container_of(work, struct spi_master, pump_messages);
|
||||
unsigned long flags;
|
||||
bool was_busy = false;
|
||||
int ret;
|
||||
@@ -916,6 +915,15 @@ static void spi_pump_messages(struct kth
|
||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
return;
|
||||
}
|
||||
+
|
||||
+ /* Only do teardown in the thread */
|
||||
+ if (!in_kthread) {
|
||||
+ queue_kthread_work(&master->kworker,
|
||||
+ &master->pump_messages);
|
||||
+ spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
master->busy = false;
|
||||
master->idling = true;
|
||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
@@ -1004,6 +1012,18 @@ static void spi_pump_messages(struct kth
|
||||
}
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * spi_pump_messages - kthread work function which processes spi message queue
|
||||
+ * @work: pointer to kthread work struct contained in the master struct
|
||||
+ */
|
||||
+static void spi_pump_messages(struct kthread_work *work)
|
||||
+{
|
||||
+ struct spi_master *master =
|
||||
+ container_of(work, struct spi_master, pump_messages);
|
||||
+
|
||||
+ __spi_pump_messages(master, true);
|
||||
+}
|
||||
+
|
||||
static int spi_init_queue(struct spi_master *master)
|
||||
{
|
||||
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
|
||||
@@ -2165,7 +2185,7 @@ static int __spi_sync(struct spi_device
|
||||
* can.
|
||||
*/
|
||||
if (master->transfer == spi_queued_transfer)
|
||||
- spi_pump_messages(&master->pump_messages);
|
||||
+ __spi_pump_messages(master, false);
|
||||
|
||||
wait_for_completion(&done);
|
||||
status = message->status;
|
|
@ -1,146 +0,0 @@
|
|||
From 279c6c7fa64f5763e6b9f05e7ab3840092e702e7 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Hemminger <stephen@networkplumber.org>
|
||||
Date: Mon, 29 Jun 2015 14:57:48 -1000
|
||||
Subject: [PATCH] api: fix compatibility of linux/in.h with netinet/in.h
|
||||
|
||||
u
|
||||
This fixes breakage to iproute2 build with recent kernel headers
|
||||
caused by:
|
||||
commit a263653ed798216c0069922d7b5237ca49436007
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Jun 17 10:28:27 2015 -0500
|
||||
|
||||
netfilter: don't pull include/linux/netfilter.h from netns headers
|
||||
|
||||
The issue is that definitions in linux/in.h overlap with those
|
||||
in netinet/in.h. This patch solves this by introducing the same
|
||||
mechanism as was used to solve the same problem with linux/in6.h
|
||||
|
||||
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
include/uapi/linux/in.h | 16 +++++++++++++---
|
||||
include/uapi/linux/libc-compat.h | 22 ++++++++++++++++++++++
|
||||
2 files changed, 35 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/include/uapi/linux/in.h
|
||||
+++ b/include/uapi/linux/in.h
|
||||
@@ -19,8 +19,10 @@
|
||||
#define _UAPI_LINUX_IN_H
|
||||
|
||||
#include <linux/types.h>
|
||||
+#include <linux/libc-compat.h>
|
||||
#include <linux/socket.h>
|
||||
|
||||
+#if __UAPI_DEF_IN_IPPROTO
|
||||
/* Standard well-defined IP protocols. */
|
||||
enum {
|
||||
IPPROTO_IP = 0, /* Dummy protocol for TCP */
|
||||
@@ -73,12 +75,14 @@ enum {
|
||||
#define IPPROTO_RAW IPPROTO_RAW
|
||||
IPPROTO_MAX
|
||||
};
|
||||
+#endif
|
||||
|
||||
-
|
||||
+#if __UAPI_DEF_IN_ADDR
|
||||
/* Internet address. */
|
||||
struct in_addr {
|
||||
__be32 s_addr;
|
||||
};
|
||||
+#endif
|
||||
|
||||
#define IP_TOS 1
|
||||
#define IP_TTL 2
|
||||
@@ -154,6 +158,7 @@ struct in_addr {
|
||||
|
||||
/* Request struct for multicast socket ops */
|
||||
|
||||
+#if __UAPI_DEF_IP_MREQ
|
||||
struct ip_mreq {
|
||||
struct in_addr imr_multiaddr; /* IP multicast address of group */
|
||||
struct in_addr imr_interface; /* local IP address of interface */
|
||||
@@ -205,14 +210,18 @@ struct group_filter {
|
||||
#define GROUP_FILTER_SIZE(numsrc) \
|
||||
(sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \
|
||||
+ (numsrc) * sizeof(struct __kernel_sockaddr_storage))
|
||||
+#endif
|
||||
|
||||
+#if __UAPI_DEF_IN_PKTINFO
|
||||
struct in_pktinfo {
|
||||
int ipi_ifindex;
|
||||
struct in_addr ipi_spec_dst;
|
||||
struct in_addr ipi_addr;
|
||||
};
|
||||
+#endif
|
||||
|
||||
/* Structure describing an Internet (IP) socket address. */
|
||||
+#if __UAPI_DEF_SOCKADDR_IN
|
||||
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
|
||||
struct sockaddr_in {
|
||||
__kernel_sa_family_t sin_family; /* Address family */
|
||||
@@ -224,8 +233,9 @@ struct sockaddr_in {
|
||||
sizeof(unsigned short int) - sizeof(struct in_addr)];
|
||||
};
|
||||
#define sin_zero __pad /* for BSD UNIX comp. -FvK */
|
||||
+#endif
|
||||
|
||||
-
|
||||
+#if __UAPI_DEF_IN_CLASS
|
||||
/*
|
||||
* Definitions of the bits in an Internet address integer.
|
||||
* On subnets, host and network parts are found according
|
||||
@@ -276,7 +286,7 @@ struct sockaddr_in {
|
||||
#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */
|
||||
#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */
|
||||
#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */
|
||||
-
|
||||
+#endif
|
||||
|
||||
/* <asm/byteorder.h> contains the htonl type stuff.. */
|
||||
#include <asm/byteorder.h>
|
||||
--- a/include/uapi/linux/libc-compat.h
|
||||
+++ b/include/uapi/linux/libc-compat.h
|
||||
@@ -56,6 +56,13 @@
|
||||
|
||||
/* GLIBC headers included first so don't define anything
|
||||
* that would already be defined. */
|
||||
+#define __UAPI_DEF_IN_ADDR 0
|
||||
+#define __UAPI_DEF_IN_IPPROTO 0
|
||||
+#define __UAPI_DEF_IN_PKTINFO 0
|
||||
+#define __UAPI_DEF_IP_MREQ 0
|
||||
+#define __UAPI_DEF_SOCKADDR_IN 0
|
||||
+#define __UAPI_DEF_IN_CLASS 0
|
||||
+
|
||||
#define __UAPI_DEF_IN6_ADDR 0
|
||||
/* The exception is the in6_addr macros which must be defined
|
||||
* if the glibc code didn't define them. This guard matches
|
||||
@@ -76,6 +83,13 @@
|
||||
/* Linux headers included first, and we must define everything
|
||||
* we need. The expectation is that glibc will check the
|
||||
* __UAPI_DEF_* defines and adjust appropriately. */
|
||||
+#define __UAPI_DEF_IN_ADDR 1
|
||||
+#define __UAPI_DEF_IN_IPPROTO 1
|
||||
+#define __UAPI_DEF_IN_PKTINFO 1
|
||||
+#define __UAPI_DEF_IP_MREQ 1
|
||||
+#define __UAPI_DEF_SOCKADDR_IN 1
|
||||
+#define __UAPI_DEF_IN_CLASS 1
|
||||
+
|
||||
#define __UAPI_DEF_IN6_ADDR 1
|
||||
/* We unconditionally define the in6_addr macros and glibc must
|
||||
* coordinate. */
|
||||
@@ -99,6 +113,14 @@
|
||||
* that we need. */
|
||||
#else /* !defined(__GLIBC__) */
|
||||
|
||||
+/* Definitions for in.h */
|
||||
+#define __UAPI_DEF_IN_ADDR 1
|
||||
+#define __UAPI_DEF_IN_IPPROTO 1
|
||||
+#define __UAPI_DEF_IN_PKTINFO 1
|
||||
+#define __UAPI_DEF_IP_MREQ 1
|
||||
+#define __UAPI_DEF_SOCKADDR_IN 1
|
||||
+#define __UAPI_DEF_IN_CLASS 1
|
||||
+
|
||||
/* Definitions for in6.h */
|
||||
#define __UAPI_DEF_IN6_ADDR 1
|
||||
#define __UAPI_DEF_IN6_ADDR_ALT 1
|
|
@ -1,182 +0,0 @@
|
|||
From: Rusty Russell <rusty@rustcorp.com.au>
|
||||
Date: Tue, 20 Jan 2015 09:07:04 +1030
|
||||
Subject: [PATCH] module_arch_freeing_init(): new hook for archs before module->module_init freed.
|
||||
|
||||
Archs have been abusing module_free() to clean up their arch-specific
|
||||
allocations. Since module_free() is also (ab)used by BPF and trace code,
|
||||
let's keep it to simple allocations, and provide a hook called before
|
||||
that.
|
||||
|
||||
This means that avr32, ia64, parisc and s390 no longer need to implement
|
||||
their own module_free() at all. avr32 doesn't need module_finalize()
|
||||
either.
|
||||
|
||||
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
|
||||
Cc: Chris Metcalf <cmetcalf@ezchip.com>
|
||||
Cc: Haavard Skinnemoen <hskinnemoen@gmail.com>
|
||||
Cc: Hans-Christian Egtvedt <egtvedt@samfundet.no>
|
||||
Cc: Tony Luck <tony.luck@intel.com>
|
||||
Cc: Fenghua Yu <fenghua.yu@intel.com>
|
||||
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
|
||||
Cc: Helge Deller <deller@gmx.de>
|
||||
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
|
||||
Cc: linux-kernel@vger.kernel.org
|
||||
Cc: linux-ia64@vger.kernel.org
|
||||
Cc: linux-parisc@vger.kernel.org
|
||||
Cc: linux-s390@vger.kernel.org
|
||||
|
||||
Origin: backport, https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d453cded05ee219b77815ea194dc36efa5398bca
|
||||
---
|
||||
arch/avr32/kernel/module.c | 13 +------------
|
||||
arch/ia64/kernel/module.c | 6 ++----
|
||||
arch/parisc/kernel/module.c | 6 +-----
|
||||
arch/s390/kernel/module.c | 10 +++-------
|
||||
arch/tile/kernel/module.c | 2 +-
|
||||
include/linux/moduleloader.h | 2 ++
|
||||
kernel/module.c | 7 +++++++
|
||||
7 files changed, 17 insertions(+), 29 deletions(-)
|
||||
|
||||
--- a/arch/avr32/kernel/module.c
|
||||
+++ b/arch/avr32/kernel/module.c
|
||||
@@ -19,12 +19,10 @@
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
-void module_free(struct module *mod, void *module_region)
|
||||
+void module_arch_freeing_init(struct module *mod)
|
||||
{
|
||||
vfree(mod->arch.syminfo);
|
||||
mod->arch.syminfo = NULL;
|
||||
-
|
||||
- vfree(module_region);
|
||||
}
|
||||
|
||||
static inline int check_rela(Elf32_Rela *rela, struct module *module,
|
||||
@@ -291,12 +289,3 @@ int apply_relocate_add(Elf32_Shdr *sechd
|
||||
|
||||
return ret;
|
||||
}
|
||||
-
|
||||
-int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
||||
- struct module *module)
|
||||
-{
|
||||
- vfree(module->arch.syminfo);
|
||||
- module->arch.syminfo = NULL;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
--- a/arch/ia64/kernel/module.c
|
||||
+++ b/arch/ia64/kernel/module.c
|
||||
@@ -305,14 +305,12 @@ plt_target (struct plt_entry *plt)
|
||||
#endif /* !USE_BRL */
|
||||
|
||||
void
|
||||
-module_free (struct module *mod, void *module_region)
|
||||
+module_arch_freeing_init (struct module *mod)
|
||||
{
|
||||
- if (mod && mod->arch.init_unw_table &&
|
||||
- module_region == mod->module_init) {
|
||||
+ if (mod->arch.init_unw_table) {
|
||||
unw_remove_unwind_table(mod->arch.init_unw_table);
|
||||
mod->arch.init_unw_table = NULL;
|
||||
}
|
||||
- vfree(module_region);
|
||||
}
|
||||
|
||||
/* Have we already seen one of these relocations? */
|
||||
--- a/arch/parisc/kernel/module.c
|
||||
+++ b/arch/parisc/kernel/module.c
|
||||
@@ -298,14 +298,10 @@ static inline unsigned long count_stubs(
|
||||
}
|
||||
#endif
|
||||
|
||||
-
|
||||
-/* Free memory returned from module_alloc */
|
||||
-void module_free(struct module *mod, void *module_region)
|
||||
+void module_arch_freeing_init(struct module *mod)
|
||||
{
|
||||
kfree(mod->arch.section);
|
||||
mod->arch.section = NULL;
|
||||
-
|
||||
- vfree(module_region);
|
||||
}
|
||||
|
||||
/* Additional bytes needed in front of individual sections */
|
||||
--- a/arch/s390/kernel/module.c
|
||||
+++ b/arch/s390/kernel/module.c
|
||||
@@ -55,14 +55,10 @@ void *module_alloc(unsigned long size)
|
||||
}
|
||||
#endif
|
||||
|
||||
-/* Free memory returned from module_alloc */
|
||||
-void module_free(struct module *mod, void *module_region)
|
||||
+void module_arch_freeing_init(struct module *mod)
|
||||
{
|
||||
- if (mod) {
|
||||
- vfree(mod->arch.syminfo);
|
||||
- mod->arch.syminfo = NULL;
|
||||
- }
|
||||
- vfree(module_region);
|
||||
+ vfree(mod->arch.syminfo);
|
||||
+ mod->arch.syminfo = NULL;
|
||||
}
|
||||
|
||||
static void check_rela(Elf_Rela *rela, struct module *me)
|
||||
--- a/arch/tile/kernel/module.c
|
||||
+++ b/arch/tile/kernel/module.c
|
||||
@@ -83,7 +83,7 @@ void module_free(struct module *mod, voi
|
||||
0, 0, 0, NULL, NULL, 0);
|
||||
|
||||
/*
|
||||
- * FIXME: If module_region == mod->module_init, trim exception
|
||||
+ * FIXME: Add module_arch_freeing_init to trim exception
|
||||
* table entries.
|
||||
*/
|
||||
}
|
||||
--- a/include/linux/moduleloader.h
|
||||
+++ b/include/linux/moduleloader.h
|
||||
@@ -82,4 +82,6 @@ int module_finalize(const Elf_Ehdr *hdr,
|
||||
/* Any cleanup needed when module leaves. */
|
||||
void module_arch_cleanup(struct module *mod);
|
||||
|
||||
+/* Any cleanup before freeing mod->module_init */
|
||||
+void module_arch_freeing_init(struct module *mod);
|
||||
#endif
|
||||
--- a/kernel/module.c
|
||||
+++ b/kernel/module.c
|
||||
@@ -1840,6 +1840,10 @@ void __weak module_arch_cleanup(struct m
|
||||
{
|
||||
}
|
||||
|
||||
+void __weak module_arch_freeing_init(struct module *mod)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
/* Free a module, remove from lists, etc. */
|
||||
static void free_module(struct module *mod)
|
||||
{
|
||||
@@ -1872,6 +1876,7 @@ static void free_module(struct module *m
|
||||
|
||||
/* This may be NULL, but that's OK */
|
||||
unset_module_init_ro_nx(mod);
|
||||
+ module_arch_freeing_init(mod);
|
||||
module_free(mod, mod->module_init);
|
||||
kfree(mod->args);
|
||||
percpu_modfree(mod);
|
||||
@@ -2983,6 +2988,7 @@ static struct module *layout_and_allocat
|
||||
static void module_deallocate(struct module *mod, struct load_info *info)
|
||||
{
|
||||
percpu_modfree(mod);
|
||||
+ module_arch_freeing_init(mod);
|
||||
module_free(mod, mod->module_init);
|
||||
module_free(mod, mod->module_core);
|
||||
}
|
||||
@@ -3105,6 +3111,7 @@ static int do_init_module(struct module
|
||||
rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms);
|
||||
#endif
|
||||
unset_module_init_ro_nx(mod);
|
||||
+ module_arch_freeing_init(mod);
|
||||
module_free(mod, mod->module_init);
|
||||
mod->module_init = NULL;
|
||||
mod->init_size = 0;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue