summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/sparc/Kconfig20
-rw-r--r--arch/sparc/Makefile1
-rw-r--r--arch/sparc/include/asm/cmpxchg_64.h1
-rw-r--r--arch/sparc/include/asm/head_32.h6
-rw-r--r--arch/sparc/include/asm/hibernate.h23
-rw-r--r--arch/sparc/include/asm/leon_pci.h1
-rw-r--r--arch/sparc/include/asm/mmu_context_64.h2
-rw-r--r--arch/sparc/include/asm/processor_64.h3
-rw-r--r--arch/sparc/kernel/Makefile3
-rw-r--r--arch/sparc/kernel/asm-offsets.c15
-rw-r--r--arch/sparc/kernel/leon_kernel.c10
-rw-r--r--arch/sparc/kernel/leon_pci.c2
-rw-r--r--arch/sparc/kernel/leon_pci_grpci1.c724
-rw-r--r--arch/sparc/kernel/leon_pci_grpci2.c5
-rw-r--r--arch/sparc/kernel/leon_pmc.c4
-rw-r--r--arch/sparc/kernel/vio.c1
-rw-r--r--arch/sparc/mm/init_64.c7
-rw-r--r--arch/sparc/power/Makefile3
-rw-r--r--arch/sparc/power/hibernate.c42
-rw-r--r--arch/sparc/power/hibernate_asm.S131
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c2
-rw-r--r--drivers/net/ethernet/emulex/benet/be.h1
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c35
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h2
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c1
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c54
-rw-r--r--drivers/net/ethernet/marvell/sky2.c2
-rw-r--r--drivers/net/ethernet/ti/cpsw.c2
-rw-r--r--drivers/net/usb/asix_common.c3
-rw-r--r--drivers/net/usb/pegasus.c3
-rw-r--r--drivers/net/usb/qmi_wwan.c1
-rw-r--r--drivers/net/xen-netback/netback.c69
-rw-r--r--drivers/tty/serial/sunsu.c1
-rw-r--r--ipc/sem.c86
-rw-r--r--net/8021q/vlan_dev.c2
-rw-r--r--net/bridge/br_stp_timer.c2
-rw-r--r--net/core/dev.c2
-rw-r--r--net/core/ethtool.c2
-rw-r--r--net/ipv4/af_inet.c1
-rw-r--r--net/ipv4/gre.c4
-rw-r--r--net/ipv4/udp.c7
-rw-r--r--net/packet/af_packet.c53
-rw-r--r--net/tipc/bcast.c40
43 files changed, 1206 insertions, 173 deletions
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index f5041d741dea..a639c0d07b8b 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -99,6 +99,9 @@ config HAVE_LATENCYTOP_SUPPORT
bool
default y if SPARC64
+config ARCH_HIBERNATION_POSSIBLE
+ def_bool y if SPARC64
+
config AUDIT_ARCH
bool
default y
@@ -303,6 +306,10 @@ config ARCH_SPARSEMEM_DEFAULT
source "mm/Kconfig"
+if SPARC64
+source "kernel/power/Kconfig"
+endif
+
config SCHED_SMT
bool "SMT (Hyperthreading) scheduler support"
depends on SPARC64 && SMP
@@ -472,7 +479,18 @@ config LEON_PCI
depends on PCI && SPARC_LEON
default y
-config GRPCI2
+config SPARC_GRPCI1
+ bool "GRPCI Host Bridge Support"
+ depends on LEON_PCI
+ default y
+ help
+ Say Y here to include the GRPCI Host Bridge Driver. The GRPCI
+ PCI host controller is typically found in GRLIB SPARC32/LEON
+ systems. The driver has one property (all_pci_errors) controlled
+ from the bootloader that makes the GRPCI to generate interrupts
+ on detected PCI Parity and System errors.
+
+config SPARC_GRPCI2
bool "GRPCI2 Host Bridge Support"
depends on LEON_PCI
default y
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index 541b8b075c7d..9ff423678cbc 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -57,6 +57,7 @@ core-y += arch/sparc/
libs-y += arch/sparc/prom/
libs-y += arch/sparc/lib/
+drivers-$(CONFIG_PM) += arch/sparc/power/
drivers-$(CONFIG_OPROFILE) += arch/sparc/oprofile/
boot := arch/sparc/boot
diff --git a/arch/sparc/include/asm/cmpxchg_64.h b/arch/sparc/include/asm/cmpxchg_64.h
index b30eb37294c5..4adefe8e2885 100644
--- a/arch/sparc/include/asm/cmpxchg_64.h
+++ b/arch/sparc/include/asm/cmpxchg_64.h
@@ -141,5 +141,6 @@ static inline unsigned long __cmpxchg_local(volatile void *ptr,
BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
cmpxchg_local((ptr), (o), (n)); \
})
+#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
#endif /* __ARCH_SPARC64_CMPXCHG__ */
diff --git a/arch/sparc/include/asm/head_32.h b/arch/sparc/include/asm/head_32.h
index a76874838f61..5f1dbe315bc8 100644
--- a/arch/sparc/include/asm/head_32.h
+++ b/arch/sparc/include/asm/head_32.h
@@ -55,15 +55,15 @@
/* The Get Condition Codes software trap for userland. */
#define GETCC_TRAP \
- b getcc_trap_handler; mov %psr, %l0; nop; nop;
+ b getcc_trap_handler; rd %psr, %l0; nop; nop;
/* The Set Condition Codes software trap for userland. */
#define SETCC_TRAP \
- b setcc_trap_handler; mov %psr, %l0; nop; nop;
+ b setcc_trap_handler; rd %psr, %l0; nop; nop;
/* The Get PSR software trap for userland. */
#define GETPSR_TRAP \
- mov %psr, %i0; jmp %l2; rett %l2 + 4; nop;
+ rd %psr, %i0; jmp %l2; rett %l2 + 4; nop;
/* This is for hard interrupts from level 1-14, 15 is non-maskable (nmi) and
* gets handled with another macro.
diff --git a/arch/sparc/include/asm/hibernate.h b/arch/sparc/include/asm/hibernate.h
new file mode 100644
index 000000000000..2ec34f842249
--- /dev/null
+++ b/arch/sparc/include/asm/hibernate.h
@@ -0,0 +1,23 @@
+/*
+ * hibernate.h: Hibernaton support specific for sparc64.
+ *
+ * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
+ */
+
+#ifndef ___SPARC_HIBERNATE_H
+#define ___SPARC_HIBERNATE_H
+
+struct saved_context {
+ unsigned long fp;
+ unsigned long cwp;
+ unsigned long wstate;
+
+ unsigned long tick;
+ unsigned long pstate;
+
+ unsigned long g4;
+ unsigned long g5;
+ unsigned long g6;
+};
+
+#endif
diff --git a/arch/sparc/include/asm/leon_pci.h b/arch/sparc/include/asm/leon_pci.h
index f48527ebdd8f..bfd3ab3092b5 100644
--- a/arch/sparc/include/asm/leon_pci.h
+++ b/arch/sparc/include/asm/leon_pci.h
@@ -12,6 +12,7 @@ struct leon_pci_info {
struct pci_ops *ops;
struct resource io_space;
struct resource mem_space;
+ struct resource busn;
int (*map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
};
diff --git a/arch/sparc/include/asm/mmu_context_64.h b/arch/sparc/include/asm/mmu_context_64.h
index 9191ca62ed9c..3d528f06e4b0 100644
--- a/arch/sparc/include/asm/mmu_context_64.h
+++ b/arch/sparc/include/asm/mmu_context_64.h
@@ -68,7 +68,7 @@ extern void smp_tsb_sync(struct mm_struct *mm);
extern void __flush_tlb_mm(unsigned long, unsigned long);
-/* Switch the current MM context. Interrupts are disabled. */
+/* Switch the current MM context. */
static inline void switch_mm(struct mm_struct *old_mm, struct mm_struct *mm, struct task_struct *tsk)
{
unsigned long ctx_valid, flags;
diff --git a/arch/sparc/include/asm/processor_64.h b/arch/sparc/include/asm/processor_64.h
index cce72ce4c334..4c3f7f01c709 100644
--- a/arch/sparc/include/asm/processor_64.h
+++ b/arch/sparc/include/asm/processor_64.h
@@ -18,9 +18,6 @@
#include <asm/ptrace.h>
#include <asm/page.h>
-/* Don't hold the runqueue lock over context switch */
-#define __ARCH_WANT_UNLOCKED_CTXSW
-
/* The sparc has no problems with write protection */
#define wp_works_ok 1
#define wp_works_ok__is_a_macro /* for versions in ksyms.c */
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 5276fd4e9d03..d432fb20358e 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -74,7 +74,8 @@ obj-y += dma.o
obj-$(CONFIG_PCIC_PCI) += pcic.o
obj-$(CONFIG_LEON_PCI) += leon_pci.o
-obj-$(CONFIG_GRPCI2) += leon_pci_grpci2.o
+obj-$(CONFIG_SPARC_GRPCI2)+= leon_pci_grpci2.o
+obj-$(CONFIG_SPARC_GRPCI1)+= leon_pci_grpci1.o
obj-$(CONFIG_SMP) += trampoline_$(BITS).o smp_$(BITS).o
obj-$(CONFIG_SPARC32_SMP) += sun4m_smp.o sun4d_smp.o leon_smp.o
diff --git a/arch/sparc/kernel/asm-offsets.c b/arch/sparc/kernel/asm-offsets.c
index 68f7e1118e9b..961b87f99e69 100644
--- a/arch/sparc/kernel/asm-offsets.c
+++ b/arch/sparc/kernel/asm-offsets.c
@@ -14,6 +14,8 @@
// #include <linux/mm.h>
#include <linux/kbuild.h>
+#include <asm/hibernate.h>
+
#ifdef CONFIG_SPARC32
int sparc32_foo(void)
{
@@ -24,6 +26,19 @@ int sparc32_foo(void)
#else
int sparc64_foo(void)
{
+#ifdef CONFIG_HIBERNATION
+ BLANK();
+ OFFSET(SC_REG_FP, saved_context, fp);
+ OFFSET(SC_REG_CWP, saved_context, cwp);
+ OFFSET(SC_REG_WSTATE, saved_context, wstate);
+
+ OFFSET(SC_REG_TICK, saved_context, tick);
+ OFFSET(SC_REG_PSTATE, saved_context, pstate);
+
+ OFFSET(SC_REG_G4, saved_context, g4);
+ OFFSET(SC_REG_G5, saved_context, g5);
+ OFFSET(SC_REG_G6, saved_context, g6);
+#endif
return 0;
}
#endif
diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c
index 87f60ee65433..7c0231dabe44 100644
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -213,6 +213,7 @@ unsigned int leon_build_device_irq(unsigned int real_irq,
{
unsigned int irq;
unsigned long mask;
+ struct irq_desc *desc;
irq = 0;
mask = leon_get_irqmask(real_irq);
@@ -226,9 +227,12 @@ unsigned int leon_build_device_irq(unsigned int real_irq,
if (do_ack)
mask |= LEON_DO_ACK_HW;
- irq_set_chip_and_handler_name(irq, &leon_irq,
- flow_handler, name);
- irq_set_chip_data(irq, (void *)mask);
+ desc = irq_to_desc(irq);
+ if (!desc || !desc->handle_irq || desc->handle_irq == handle_bad_irq) {
+ irq_set_chip_and_handler_name(irq, &leon_irq,
+ flow_handler, name);
+ irq_set_chip_data(irq, (void *)mask);
+ }
out:
return irq;
diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c
index 852dc8430528..88aaaa57bb64 100644
--- a/arch/sparc/kernel/leon_pci.c
+++ b/arch/sparc/kernel/leon_pci.c
@@ -29,6 +29,8 @@ void leon_pci_init(struct platform_device *ofdev, struct leon_pci_info *info)
pci_add_resource_offset(&resources, &info->io_space,
info->io_space.start - 0x1000);
pci_add_resource(&resources, &info->mem_space);
+ info->busn.flags = IORESOURCE_BUS;
+ pci_add_resource(&resources, &info->busn);
root_bus = pci_scan_root_bus(&ofdev->dev, 0, info->ops, info,
&resources);
diff --git a/arch/sparc/kernel/leon_pci_grpci1.c b/arch/sparc/kernel/leon_pci_grpci1.c
new file mode 100644
index 000000000000..7739a54315e2
--- /dev/null
+++ b/arch/sparc/kernel/leon_pci_grpci1.c
@@ -0,0 +1,724 @@
+/*
+ * leon_pci_grpci1.c: GRPCI1 Host PCI driver
+ *
+ * Copyright (C) 2013 Aeroflex Gaisler AB
+ *
+ * This GRPCI1 driver does not support PCI interrupts taken from
+ * GPIO pins. Interrupt generation at PCI parity and system error
+ * detection is by default turned off since some GRPCI1 cores does
+ * not support detection. It can be turned on from the bootloader
+ * using the all_pci_errors property.
+ *
+ * Contributors: Daniel Hellstrom <daniel@gaisler.com>
+ */
+
+#include <linux/of_device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <asm/leon_pci.h>
+#include <asm/sections.h>
+#include <asm/vaddrs.h>
+#include <asm/leon.h>
+#include <asm/io.h>
+
+#include "irq.h"
+
+/* Enable/Disable Debugging Configuration Space Access */
+#undef GRPCI1_DEBUG_CFGACCESS
+
+/*
+ * GRPCI1 APB Register MAP
+ */
+struct grpci1_regs {
+ unsigned int cfg_stat; /* 0x00 Configuration / Status */
+ unsigned int bar0; /* 0x04 BAR0 (RO) */
+ unsigned int page0; /* 0x08 PAGE0 (RO) */
+ unsigned int bar1; /* 0x0C BAR1 (RO) */
+ unsigned int page1; /* 0x10 PAGE1 */
+ unsigned int iomap; /* 0x14 IO Map */
+ unsigned int stat_cmd; /* 0x18 PCI Status & Command (RO) */
+ unsigned int irq; /* 0x1C Interrupt register */
+};
+
+#define REGLOAD(a) (be32_to_cpu(__raw_readl(&(a))))
+#define REGSTORE(a, v) (__raw_writel(cpu_to_be32(v), &(a)))
+
+#define PAGE0_BTEN_BIT 0
+#define PAGE0_BTEN (1 << PAGE0_BTEN_BIT)
+
+#define CFGSTAT_HOST_BIT 13
+#define CFGSTAT_CTO_BIT 8
+#define CFGSTAT_HOST (1 << CFGSTAT_HOST_BIT)
+#define CFGSTAT_CTO (1 << CFGSTAT_CTO_BIT)
+
+#define IRQ_DPE (1 << 9)
+#define IRQ_SSE (1 << 8)
+#define IRQ_RMA (1 << 7)
+#define IRQ_RTA (1 << 6)
+#define IRQ_STA (1 << 5)
+#define IRQ_DPED (1 << 4)
+#define IRQ_INTD (1 << 3)
+#define IRQ_INTC (1 << 2)
+#define IRQ_INTB (1 << 1)
+#define IRQ_INTA (1 << 0)
+#define IRQ_DEF_ERRORS (IRQ_RMA | IRQ_RTA | IRQ_STA)
+#define IRQ_ALL_ERRORS (IRQ_DPED | IRQ_DEF_ERRORS | IRQ_SSE | IRQ_DPE)
+#define IRQ_INTX (IRQ_INTA | IRQ_INTB | IRQ_INTC | IRQ_INTD)
+#define IRQ_MASK_BIT 16
+
+#define DEF_PCI_ERRORS (PCI_STATUS_SIG_TARGET_ABORT | \
+ PCI_STATUS_REC_TARGET_ABORT | \
+ PCI_STATUS_REC_MASTER_ABORT)
+#define ALL_PCI_ERRORS (PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY | \
+ PCI_STATUS_SIG_SYSTEM_ERROR | DEF_PCI_ERRORS)
+
+#define TGT 256
+
+struct grpci1_priv {
+ struct leon_pci_info info; /* must be on top of this structure */
+ struct grpci1_regs *regs; /* GRPCI register map */
+ struct device *dev;
+ int pci_err_mask; /* STATUS register error mask */
+ int irq; /* LEON irqctrl GRPCI IRQ */
+ unsigned char irq_map[4]; /* GRPCI nexus PCI INTX# IRQs */
+ unsigned int irq_err; /* GRPCI nexus Virt Error IRQ */
+
+ /* AHB PCI Windows */
+ unsigned long pci_area; /* MEMORY */
+ unsigned long pci_area_end;
+ unsigned long pci_io; /* I/O */
+ unsigned long pci_conf; /* CONFIGURATION */
+ unsigned long pci_conf_end;
+ unsigned long pci_io_va;
+};
+
+static struct grpci1_priv *grpci1priv;
+
+static int grpci1_cfg_w32(struct grpci1_priv *priv, unsigned int bus,
+ unsigned int devfn, int where, u32 val);
+
+int grpci1_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct grpci1_priv *priv = dev->bus->sysdata;
+ int irq_group;
+
+ /* Use default IRQ decoding on PCI BUS0 according slot numbering */
+ irq_group = slot & 0x3;
+ pin = ((pin - 1) + irq_group) & 0x3;
+
+ return priv->irq_map[pin];
+}
+
+static int grpci1_cfg_r32(struct grpci1_priv *priv, unsigned int bus,
+ unsigned int devfn, int where, u32 *val)
+{
+ u32 *pci_conf, tmp, cfg;
+
+ if (where & 0x3)
+ return -EINVAL;
+
+ if (bus == 0) {
+ devfn += (0x8 * 6); /* start at AD16=Device0 */
+ } else if (bus == TGT) {
+ bus = 0;
+ devfn = 0; /* special case: bridge controller itself */
+ }
+
+ /* Select bus */
+ cfg = REGLOAD(priv->regs->cfg_stat);
+ REGSTORE(priv->regs->cfg_stat, (cfg & ~(0xf << 23)) | (bus << 23));
+
+ /* do read access */
+ pci_conf = (u32 *) (priv->pci_conf | (devfn << 8) | (where & 0xfc));
+ tmp = LEON3_BYPASS_LOAD_PA(pci_conf);
+
+ /* check if master abort was received */
+ if (REGLOAD(priv->regs->cfg_stat) & CFGSTAT_CTO) {
+ *val = 0xffffffff;
+ /* Clear Master abort bit in PCI cfg space (is set) */
+ tmp = REGLOAD(priv->regs->stat_cmd);
+ grpci1_cfg_w32(priv, TGT, 0, PCI_COMMAND, tmp);
+ } else {
+ /* Bus always little endian (unaffected by byte-swapping) */
+ *val = flip_dword(tmp);
+ }
+
+ return 0;
+}
+
+static int grpci1_cfg_r16(struct grpci1_priv *priv, unsigned int bus,
+ unsigned int devfn, int where, u32 *val)
+{
+ u32 v;
+ int ret;
+
+ if (where & 0x1)
+ return -EINVAL;
+ ret = grpci1_cfg_r32(priv, bus, devfn, where & ~0x3, &v);
+ *val = 0xffff & (v >> (8 * (where & 0x3)));
+ return ret;
+}
+
+static int grpci1_cfg_r8(struct grpci1_priv *priv, unsigned int bus,
+ unsigned int devfn, int where, u32 *val)
+{
+ u32 v;
+ int ret;
+
+ ret = grpci1_cfg_r32(priv, bus, devfn, where & ~0x3, &v);
+ *val = 0xff & (v >> (8 * (where & 3)));
+
+ return ret;
+}
+
+static int grpci1_cfg_w32(struct grpci1_priv *priv, unsigned int bus,
+ unsigned int devfn, int where, u32 val)
+{
+ unsigned int *pci_conf;
+ u32 cfg;
+
+ if (where & 0x3)
+ return -EINVAL;
+
+ if (bus == 0) {
+ devfn += (0x8 * 6); /* start at AD16=Device0 */
+ } else if (bus == TGT) {
+ bus = 0;
+ devfn = 0; /* special case: bridge controller itself */
+ }
+
+ /* Select bus */
+ cfg = REGLOAD(priv->regs->cfg_stat);
+ REGSTORE(priv->regs->cfg_stat, (cfg & ~(0xf << 23)) | (bus << 23));
+
+ pci_conf = (unsigned int *) (priv->pci_conf |
+ (devfn << 8) | (where & 0xfc));
+ LEON3_BYPASS_STORE_PA(pci_conf, flip_dword(val));
+
+ return 0;
+}
+
+static int grpci1_cfg_w16(struct grpci1_priv *priv, unsigned int bus,
+ unsigned int devfn, int where, u32 val)
+{
+ int ret;
+ u32 v;
+
+ if (where & 0x1)
+ return -EINVAL;
+ ret = grpci1_cfg_r32(priv, bus, devfn, where&~3, &v);
+ if (ret)
+ return ret;
+ v = (v & ~(0xffff << (8 * (where & 0x3)))) |
+ ((0xffff & val) << (8 * (where & 0x3)));
+ return grpci1_cfg_w32(priv, bus, devfn, where & ~0x3, v);
+}
+
+static int grpci1_cfg_w8(struct grpci1_priv *priv, unsigned int bus,
+ unsigned int devfn, int where, u32 val)
+{
+ int ret;
+ u32 v;
+
+ ret = grpci1_cfg_r32(priv, bus, devfn, where & ~0x3, &v);
+ if (ret != 0)
+ return ret;
+ v = (v & ~(0xff << (8 * (where & 0x3)))) |
+ ((0xff & val) << (8 * (where & 0x3)));
+ return grpci1_cfg_w32(priv, bus, devfn, where & ~0x3, v);
+}
+
+/* Read from Configuration Space. When entering here the PCI layer has taken
+ * the pci_lock spinlock and IRQ is off.
+ */
+static int grpci1_read_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ struct grpci1_priv *priv = grpci1priv;
+ unsigned int busno = bus->number;
+ int ret;
+
+ if (PCI_SLOT(devfn) > 15 || busno > 15) {
+ *val = ~0;
+ return 0;
+ }
+
+ switch (size) {
+ case 1:
+ ret = grpci1_cfg_r8(priv, busno, devfn, where, val);
+ break;
+ case 2:
+ ret = grpci1_cfg_r16(priv, busno, devfn, where, val);
+ break;
+ case 4:
+ ret = grpci1_cfg_r32(priv, busno, devfn, where, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+#ifdef GRPCI1_DEBUG_CFGACCESS
+ printk(KERN_INFO
+ "grpci1_read_config: [%02x:%02x:%x] ofs=%d val=%x size=%d\n",
+ busno, PCI_SLOT(devfn), PCI_FUNC(devfn), where, *val, size);
+#endif
+
+ return ret;
+}
+
+/* Write to Configuration Space. When entering here the PCI layer has taken
+ * the pci_lock spinlock and IRQ is off.
+ */
+static int grpci1_write_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ struct grpci1_priv *priv = grpci1priv;
+ unsigned int busno = bus->number;
+
+ if (PCI_SLOT(devfn) > 15 || busno > 15)
+ return 0;
+
+#ifdef GRPCI1_DEBUG_CFGACCESS
+ printk(KERN_INFO
+ "grpci1_write_config: [%02x:%02x:%x] ofs=%d size=%d val=%x\n",
+ busno, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
+#endif
+
+ switch (size) {
+ default:
+ return -EINVAL;
+ case 1:
+ return grpci1_cfg_w8(priv, busno, devfn, where, val);
+ case 2:
+ return grpci1_cfg_w16(priv, busno, devfn, where, val);
+ case 4:
+ return grpci1_cfg_w32(priv, busno, devfn, where, val);
+ }
+}
+
+static struct pci_ops grpci1_ops = {
+ .read = grpci1_read_config,
+ .write = grpci1_write_config,
+};
+
+/* GENIRQ IRQ chip implementation for grpci1 irqmode=0..2. In configuration
+ * 3 where all PCI Interrupts has a separate IRQ on the system IRQ controller
+ * this is not needed and the standard IRQ controller can be used.
+ */
+
+static void grpci1_mask_irq(struct irq_data *data)
+{
+ u32 irqidx;
+ struct grpci1_priv *priv = grpci1priv;
+
+ irqidx = (u32)data->chip_data - 1;
+ if (irqidx > 3) /* only mask PCI interrupts here */
+ return;
+ irqidx += IRQ_MASK_BIT;
+
+ REGSTORE(priv->regs->irq, REGLOAD(priv->regs->irq) & ~(1 << irqidx));
+}
+
+static void grpci1_unmask_irq(struct irq_data *data)
+{
+ u32 irqidx;
+ struct grpci1_priv *priv = grpci1priv;
+
+ irqidx = (u32)data->chip_data - 1;
+ if (irqidx > 3) /* only unmask PCI interrupts here */
+ return;
+ irqidx += IRQ_MASK_BIT;
+
+ REGSTORE(priv->regs->irq, REGLOAD(priv->regs->irq) | (1 << irqidx));
+}
+
+static unsigned int grpci1_startup_irq(struct irq_data *data)
+{
+ grpci1_unmask_irq(data);
+ return 0;
+}
+
+static void grpci1_shutdown_irq(struct irq_data *data)
+{
+ grpci1_mask_irq(data);
+}
+
+static struct irq_chip grpci1_irq = {
+ .name = "grpci1",
+ .irq_startup = grpci1_startup_irq,
+ .irq_shutdown = grpci1_shutdown_irq,
+ .irq_mask = grpci1_mask_irq,
+ .irq_unmask = grpci1_unmask_irq,
+};
+
+/* Handle one or multiple IRQs from the PCI core */
+static void grpci1_pci_flow_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct grpci1_priv *priv = grpci1priv;
+ int i, ack = 0;
+ unsigned int irqreg;
+
+ irqreg = REGLOAD(priv->regs->irq);
+ irqreg = (irqreg >> IRQ_MASK_BIT) & irqreg;
+
+ /* Error Interrupt? */
+ if (irqreg & IRQ_ALL_ERRORS) {
+ generic_handle_irq(priv->irq_err);
+ ack = 1;
+ }
+
+ /* PCI Interrupt? */
+ if (irqreg & IRQ_INTX) {
+ /* Call respective PCI Interrupt handler */
+ for (i = 0; i < 4; i++) {
+ if (irqreg & (1 << i))
+ generic_handle_irq(priv->irq_map[i]);
+ }
+ ack = 1;
+ }
+
+ /*
+ * Call "first level" IRQ chip end-of-irq handler. It will ACK LEON IRQ
+ * Controller, this must be done after IRQ sources have been handled to
+ * avoid double IRQ generation
+ */
+ if (ack)
+ desc->irq_data.chip->irq_eoi(&desc->irq_data);
+}
+
+/* Create a virtual IRQ */
+static unsigned int grpci1_build_device_irq(unsigned int irq)
+{
+ unsigned int virq = 0, pil;
+
+ pil = 1 << 8;
+ virq = irq_alloc(irq, pil);
+ if (virq == 0)
+ goto out;
+
+ irq_set_chip_and_handler_name(virq, &grpci1_irq, handle_simple_irq,
+ "pcilvl");
+ irq_set_chip_data(virq, (void *)irq);
+
+out:
+ return virq;
+}
+
+/*
+ * Initialize mappings AMBA<->PCI, clear IRQ state, setup PCI interface
+ *
+ * Target BARs:
+ * BAR0: unused in this implementation
+ * BAR1: peripheral DMA to host's memory (size at least 256MByte)
+ * BAR2..BAR5: not implemented in hardware
+ */
+void grpci1_hw_init(struct grpci1_priv *priv)
+{
+ u32 ahbadr, bar_sz, data, pciadr;
+ struct grpci1_regs *regs = priv->regs;
+
+ /* set 1:1 mapping between AHB -> PCI memory space */
+ REGSTORE(regs->cfg_stat, priv->pci_area & 0xf0000000);
+
+ /* map PCI accesses to target BAR1 to Linux kernel memory 1:1 */
+ ahbadr = 0xf0000000 & (u32)__pa(PAGE_ALIGN((unsigned long) &_end));
+ REGSTORE(regs->page1, ahbadr);
+
+ /* translate I/O accesses to 0, I/O Space always @ PCI low 64Kbytes */
+ REGSTORE(regs->iomap, REGLOAD(regs->iomap) & 0x0000ffff);
+
+ /* disable and clear pending interrupts */
+ REGSTORE(regs->irq, 0);
+
+ /* Setup BAR0 outside access range so that it does not conflict with
+ * peripheral DMA. There is no need to set up the PAGE0 register.
+ */
+ grpci1_cfg_w32(priv, TGT, 0, PCI_BASE_ADDRESS_0, 0xffffffff);
+ grpci1_cfg_r32(priv, TGT, 0, PCI_BASE_ADDRESS_0, &bar_sz);
+ bar_sz = ~bar_sz + 1;
+ pciadr = priv->pci_area - bar_sz;
+ grpci1_cfg_w32(priv, TGT, 0, PCI_BASE_ADDRESS_0, pciadr);
+
+ /*
+ * Setup the Host's PCI Target BAR1 for other peripherals to access,
+ * and do DMA to the host's memory.
+ */
+ grpci1_cfg_w32(priv, TGT, 0, PCI_BASE_ADDRESS_1, ahbadr);
+
+ /*
+ * Setup Latency Timer and cache line size. Default cache line
+ * size will result in poor performance (256 word fetches), 0xff
+ * will set it according to the max size of the PCI FIFO.
+ */
+ grpci1_cfg_w8(priv, TGT, 0, PCI_CACHE_LINE_SIZE, 0xff);
+ grpci1_cfg_w8(priv, TGT, 0, PCI_LATENCY_TIMER, 0x40);
+
+ /* set as bus master, enable pci memory responses, clear status bits */
+ grpci1_cfg_r32(priv, TGT, 0, PCI_COMMAND, &data);
+ data |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ grpci1_cfg_w32(priv, TGT, 0, PCI_COMMAND, data);
+}
+
+static irqreturn_t grpci1_jump_interrupt(int irq, void *arg)
+{
+ struct grpci1_priv *priv = arg;
+ dev_err(priv->dev, "Jump IRQ happened\n");
+ return IRQ_NONE;
+}
+
+/* Handle GRPCI1 Error Interrupt */
+static irqreturn_t grpci1_err_interrupt(int irq, void *arg)
+{
+ struct grpci1_priv *priv = arg;
+ u32 status;
+
+ grpci1_cfg_r16(priv, TGT, 0, PCI_STATUS, &status);
+ status &= priv->pci_err_mask;
+
+ if (status == 0)
+ return IRQ_NONE;
+
+ if (status & PCI_STATUS_PARITY)
+ dev_err(priv->dev, "Data Parity Error\n");
+
+ if (status & PCI_STATUS_SIG_TARGET_ABORT)
+ dev_err(priv->dev, "Signalled Target Abort\n");
+
+ if (status & PCI_STATUS_REC_TARGET_ABORT)
+ dev_err(priv->dev, "Received Target Abort\n");
+
+ if (status & PCI_STATUS_REC_MASTER_ABORT)
+ dev_err(priv->dev, "Received Master Abort\n");
+
+ if (status & PCI_STATUS_SIG_SYSTEM_ERROR)
+ dev_err(priv->dev, "Signalled System Error\n");
+
+ if (status & PCI_STATUS_DETECTED_PARITY)
+ dev_err(priv->dev, "Parity Error\n");
+
+ /* Clear handled INT TYPE IRQs */
+ grpci1_cfg_w16(priv, TGT, 0, PCI_STATUS, status);
+
+ return IRQ_HANDLED;
+}
+
+static int grpci1_of_probe(struct platform_device *ofdev)
+{
+ struct grpci1_regs *regs;
+ struct grpci1_priv *priv;
+ int err, len;
+ const int *tmp;
+ u32 cfg, size, err_mask;
+ struct resource *res;
+
+ if (grpci1priv) {
+ dev_err(&ofdev->dev, "only one GRPCI1 supported\n");
+ return -ENODEV;
+ }
+
+ if (ofdev->num_resources < 3) {
+ dev_err(&ofdev->dev, "not enough APB/AHB resources\n");
+ return -EIO;
+ }
+
+ priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&ofdev->dev, "memory allocation failed\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(ofdev, priv);
+ priv->dev = &ofdev->dev;
+
+ /* find device register base address */
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+ regs = devm_request_and_ioremap(&ofdev->dev, res);
+ if (!regs) {
+ dev_err(&ofdev->dev, "io-regs mapping failed\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ /*
+ * check that we're in Host Slot and that we can act as a Host Bridge
+ * and not only as target/peripheral.
+ */
+ cfg = REGLOAD(regs->cfg_stat);
+ if ((cfg & CFGSTAT_HOST) == 0) {
+ dev_err(&ofdev->dev, "not in host system slot\n");
+ return -EIO;
+ }
+
+ /* check that BAR1 support 256 MByte so that we can map kernel space */
+ REGSTORE(regs->page1, 0xffffffff);
+ size = ~REGLOAD(regs->page1) + 1;
+ if (size < 0x10000000) {
+ dev_err(&ofdev->dev, "BAR1 must be at least 256MByte\n");
+ return -EIO;
+ }
+
+ /* hardware must support little-endian PCI (byte-twisting) */
+ if ((REGLOAD(regs->page0) & PAGE0_BTEN) == 0) {
+ dev_err(&ofdev->dev, "byte-twisting is required\n");
+ return -EIO;
+ }
+
+ priv->regs = regs;
+ priv->irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
+ dev_info(&ofdev->dev, "host found at 0x%p, irq%d\n", regs, priv->irq);
+
+ /* Find PCI Memory, I/O and Configuration Space Windows */
+ priv->pci_area = ofdev->resource[1].start;
+ priv->pci_area_end = ofdev->resource[1].end+1;
+ priv->pci_io = ofdev->resource[2].start;
+ priv->pci_conf = ofdev->resource[2].start + 0x10000;
+ priv->pci_conf_end = priv->pci_conf + 0x10000;
+ priv->pci_io_va = (unsigned long)ioremap(priv->pci_io, 0x10000);
+ if (!priv->pci_io_va) {
+ dev_err(&ofdev->dev, "unable to map PCI I/O area\n");
+ return -EIO;
+ }
+
+ printk(KERN_INFO
+ "GRPCI1: MEMORY SPACE [0x%08lx - 0x%08lx]\n"
+ " I/O SPACE [0x%08lx - 0x%08lx]\n"
+ " CONFIG SPACE [0x%08lx - 0x%08lx]\n",
+ priv->pci_area, priv->pci_area_end-1,
+ priv->pci_io, priv->pci_conf-1,
+ priv->pci_conf, priv->pci_conf_end-1);
+
+ /*
+ * I/O Space resources in I/O Window mapped into Virtual Adr Space
+ * We never use low 4KB because some devices seem have problems using
+ * address 0.
+ */
+ priv->info.io_space.name = "GRPCI1 PCI I/O Space";
+ priv->info.io_space.start = priv->pci_io_va + 0x1000;
+ priv->info.io_space.end = priv->pci_io_va + 0x10000 - 1;
+ priv->info.io_space.flags = IORESOURCE_IO;
+
+ /*
+ * grpci1 has no prefetchable memory, map everything as
+ * non-prefetchable memory
+ */
+ priv->info.mem_space.name = "GRPCI1 PCI MEM Space";
+ priv->info.mem_space.start = priv->pci_area;
+ priv->info.mem_space.end = priv->pci_area_end - 1;
+ priv->info.mem_space.flags = IORESOURCE_MEM;
+
+ if (request_resource(&iomem_resource, &priv->info.mem_space) < 0) {
+ dev_err(&ofdev->dev, "unable to request PCI memory area\n");
+ err = -ENOMEM;
+ goto err1;
+ }
+
+ if (request_resource(&ioport_resource, &priv->info.io_space) < 0) {
+ dev_err(&ofdev->dev, "unable to request PCI I/O area\n");
+ err = -ENOMEM;
+ goto err2;
+ }
+
+ /* setup maximum supported PCI buses */
+ priv->info.busn.name = "GRPCI1 busn";
+ priv->info.busn.start = 0;
+ priv->info.busn.end = 15;
+
+ grpci1priv = priv;
+
+ /* Initialize hardware */
+ grpci1_hw_init(priv);
+
+ /*
+ * Get PCI Interrupt to System IRQ mapping and setup IRQ handling
+ * Error IRQ. All PCI and PCI-Error interrupts are shared using the
+ * same system IRQ.
+ */
+ leon_update_virq_handling(priv->irq, grpci1_pci_flow_irq, "pcilvl", 0);
+
+ priv->irq_map[0] = grpci1_build_device_irq(1);
+ priv->irq_map[1] = grpci1_build_device_irq(2);
+ priv->irq_map[2] = grpci1_build_device_irq(3);
+ priv->irq_map[3] = grpci1_build_device_irq(4);
+ priv->irq_err = grpci1_build_device_irq(5);
+
+ printk(KERN_INFO " PCI INTA..D#: IRQ%d, IRQ%d, IRQ%d, IRQ%d\n",
+ priv->irq_map[0], priv->irq_map[1], priv->irq_map[2],
+ priv->irq_map[3]);
+
+ /* Enable IRQs on LEON IRQ controller */
+ err = devm_request_irq(&ofdev->dev, priv->irq, grpci1_jump_interrupt, 0,
+ "GRPCI1_JUMP", priv);
+ if (err) {
+ dev_err(&ofdev->dev, "ERR IRQ request failed: %d\n", err);
+ goto err3;
+ }
+
+ /* Setup IRQ handler for access errors */
+ err = devm_request_irq(&ofdev->dev, priv->irq_err,
+ grpci1_err_interrupt, IRQF_SHARED, "GRPCI1_ERR",
+ priv);
+ if (err) {
+ dev_err(&ofdev->dev, "ERR VIRQ request failed: %d\n", err);
+ goto err3;
+ }
+
+ tmp = of_get_property(ofdev->dev.of_node, "all_pci_errors", &len);
+ if (tmp && (len == 4)) {
+ priv->pci_err_mask = ALL_PCI_ERRORS;
+ err_mask = IRQ_ALL_ERRORS << IRQ_MASK_BIT;
+ } else {
+ priv->pci_err_mask = DEF_PCI_ERRORS;
+ err_mask = IRQ_DEF_ERRORS << IRQ_MASK_BIT;
+ }
+
+ /*
+ * Enable Error Interrupts. PCI interrupts are unmasked once request_irq
+ * is called by the PCI Device drivers
+ */
+ REGSTORE(regs->irq, err_mask);
+
+ /* Init common layer and scan buses */
+ priv->info.ops = &grpci1_ops;
+ priv->info.map_irq = grpci1_map_irq;
+ leon_pci_init(ofdev, &priv->info);
+
+ return 0;
+
+err3:
+ release_resource(&priv->info.io_space);
+err2:
+ release_resource(&priv->info.mem_space);
+err1:
+ iounmap((void *)priv->pci_io_va);
+ grpci1priv = NULL;
+ return err;
+}
+
+static struct of_device_id grpci1_of_match[] = {
+ {
+ .name = "GAISLER_PCIFBRG",
+ },
+ {
+ .name = "01_014",
+ },
+ {},
+};
+
+static struct platform_driver grpci1_of_driver = {
+ .driver = {
+ .name = "grpci1",
+ .owner = THIS_MODULE,
+ .of_match_table = grpci1_of_match,
+ },
+ .probe = grpci1_of_probe,
+};
+
+static int __init grpci1_init(void)
+{
+ return platform_driver_register(&grpci1_of_driver);
+}
+
+subsys_initcall(grpci1_init);
diff --git a/arch/sparc/kernel/leon_pci_grpci2.c b/arch/sparc/kernel/leon_pci_grpci2.c
index 4d1487138d26..5f0402aab7fb 100644
--- a/arch/sparc/kernel/leon_pci_grpci2.c
+++ b/arch/sparc/kernel/leon_pci_grpci2.c
@@ -799,6 +799,11 @@ static int grpci2_of_probe(struct platform_device *ofdev)
if (request_resource(&ioport_resource, &priv->info.io_space) < 0)
goto err4;
+ /* setup maximum supported PCI buses */
+ priv->info.busn.name = "GRPCI2 busn";
+ priv->info.busn.start = 0;
+ priv->info.busn.end = 255;
+
grpci2_hw_init(priv);
/*
diff --git a/arch/sparc/kernel/leon_pmc.c b/arch/sparc/kernel/leon_pmc.c
index 708bca435219..bdf53d9a8d46 100644
--- a/arch/sparc/kernel/leon_pmc.c
+++ b/arch/sparc/kernel/leon_pmc.c
@@ -48,7 +48,7 @@ void pmc_leon_idle_fixup(void)
*/
register unsigned int address = (unsigned int)leon3_irqctrl_regs;
__asm__ __volatile__ (
- "mov %%g0, %%asr19\n"
+ "wr %%g0, %%asr19\n"
"lda [%0] %1, %%g0\n"
:
: "r"(address), "i"(ASI_LEON_BYPASS));
@@ -61,7 +61,7 @@ void pmc_leon_idle_fixup(void)
void pmc_leon_idle(void)
{
/* For systems without power-down, this will be no-op */
- __asm__ __volatile__ ("mov %g0, %asr19\n\t");
+ __asm__ __volatile__ ("wr %g0, %asr19\n\t");
}
/* Install LEON Power Down function */
diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c
index 3e244f31e56b..8647fcc5ca6c 100644
--- a/arch/sparc/kernel/vio.c
+++ b/arch/sparc/kernel/vio.c
@@ -342,6 +342,7 @@ static void vio_remove(struct mdesc_handle *hp, u64 node)
printk(KERN_INFO "VIO: Removing device %s\n", dev_name(dev));
device_unregister(dev);
+ put_device(dev);
}
}
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 6ac99d64a13c..cf72a8a5b3aa 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -681,10 +681,9 @@ void get_new_mmu_context(struct mm_struct *mm)
{
unsigned long ctx, new_ctx;
unsigned long orig_pgsz_bits;
- unsigned long flags;
int new_version;
- spin_lock_irqsave(&ctx_alloc_lock, flags);
+ spin_lock(&ctx_alloc_lock);
orig_pgsz_bits = (mm->context.sparc64_ctx_val & CTX_PGSZ_MASK);
ctx = (tlb_context_cache + 1) & CTX_NR_MASK;
new_ctx = find_next_zero_bit(mmu_context_bmap, 1 << CTX_NR_BITS, ctx);
@@ -720,7 +719,7 @@ void get_new_mmu_context(struct mm_struct *mm)
out:
tlb_context_cache = new_ctx;
mm->context.sparc64_ctx_val = new_ctx | orig_pgsz_bits;
- spin_unlock_irqrestore(&ctx_alloc_lock, flags);
+ spin_unlock(&ctx_alloc_lock);
if (unlikely(new_version))
smp_new_mmu_context_version();
@@ -2125,7 +2124,6 @@ void free_initmem(void)
ClearPageReserved(p);
init_page_count(p);
__free_page(p);
- num_physpages++;
totalram_pages++;
}
}
@@ -2142,7 +2140,6 @@ void free_initrd_mem(unsigned long start, unsigned long end)
ClearPageReserved(p);
init_page_count(p);
__free_page(p);
- num_physpages++;
totalram_pages++;
}
}
diff --git a/arch/sparc/power/Makefile b/arch/sparc/power/Makefile
new file mode 100644
index 000000000000..3201ace0ddbd
--- /dev/null
+++ b/arch/sparc/power/Makefile
@@ -0,0 +1,3 @@
+# Makefile for Sparc-specific hibernate files.
+
+obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o
diff --git a/arch/sparc/power/hibernate.c b/arch/sparc/power/hibernate.c
new file mode 100644
index 000000000000..42b0b8ce699a
--- /dev/null
+++ b/arch/sparc/power/hibernate.c
@@ -0,0 +1,42 @@
+/*
+ * hibernate.c: Hibernaton support specific for sparc64.
+ *
+ * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
+ */
+
+#include <linux/mm.h>
+
+#include <asm/hibernate.h>
+#include <asm/visasm.h>
+#include <asm/page.h>
+#include <asm/tlb.h>
+
+/* References to section boundaries */
+extern const void __nosave_begin, __nosave_end;
+
+struct saved_context saved_context;
+
+/*
+ * pfn_is_nosave - check if given pfn is in the 'nosave' section
+ */
+
+int pfn_is_nosave(unsigned long pfn)
+{
+ unsigned long nosave_begin_pfn = PFN_DOWN((unsigned long)&__nosave_begin);
+ unsigned long nosave_end_pfn = PFN_DOWN((unsigned long)&__nosave_end);
+
+ return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
+}
+
+void save_processor_state(void)
+{
+ save_and_clear_fpu();
+}
+
+void restore_processor_state(void)
+{
+ struct mm_struct *mm = current->active_mm;
+
+ load_secondary_context(mm);
+ tsb_context_switch(mm);
+}
diff --git a/arch/sparc/power/hibernate_asm.S b/arch/sparc/power/hibernate_asm.S
new file mode 100644
index 000000000000..79942166df84
--- /dev/null
+++ b/arch/sparc/power/hibernate_asm.S
@@ -0,0 +1,131 @@
+/*
+ * hibernate_asm.S: Hibernaton support specific for sparc64.
+ *
+ * Copyright (C) 2013 Kirill V Tkhai (tkhai@yandex.ru)
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/asm-offsets.h>
+#include <asm/cpudata.h>
+#include <asm/page.h>
+
+ENTRY(swsusp_arch_suspend)
+ save %sp, -128, %sp
+ save %sp, -128, %sp
+ flushw
+
+ setuw saved_context, %g3
+
+ /* Save window regs */
+ rdpr %cwp, %g2
+ stx %g2, [%g3 + SC_REG_CWP]
+ rdpr %wstate, %g2
+ stx %g2, [%g3 + SC_REG_WSTATE]
+ stx %fp, [%g3 + SC_REG_FP]
+
+ /* Save state regs */
+ rdpr %tick, %g2
+ stx %g2, [%g3 + SC_REG_TICK]
+ rdpr %pstate, %g2
+ stx %g2, [%g3 + SC_REG_PSTATE]
+
+ /* Save global regs */
+ stx %g4, [%g3 + SC_REG_G4]
+ stx %g5, [%g3 + SC_REG_G5]
+ stx %g6, [%g3 + SC_REG_G6]
+
+ call swsusp_save
+ nop
+
+ mov %o0, %i0
+ restore
+
+ mov %o0, %i0
+ ret
+ restore
+
+ENTRY(swsusp_arch_resume)
+ /* Write restore_pblist to %l0 */
+ sethi %hi(restore_pblist), %l0
+ ldx [%l0 + %lo(restore_pblist)], %l0
+
+ call __flush_tlb_all
+ nop
+
+ /* Write PAGE_OFFSET to %g7 */
+ sethi %uhi(PAGE_OFFSET), %g7
+ sllx %g7, 32, %g7
+
+ setuw (PAGE_SIZE-8), %g3
+
+ /* Use MMU Bypass */
+ rd %asi, %g1
+ wr %g0, ASI_PHYS_USE_EC, %asi
+
+ ba fill_itlb
+ nop
+
+pbe_loop:
+ cmp %l0, %g0
+ be restore_ctx
+ sub %l0, %g7, %l0
+
+ ldxa [%l0 ] %asi, %l1 /* address */
+ ldxa [%l0 + 8] %asi, %l2 /* orig_address */
+
+ /* phys addr */
+ sub %l1, %g7, %l1
+ sub %l2, %g7, %l2
+
+ mov %g3, %l3 /* PAGE_SIZE-8 */
+copy_loop:
+ ldxa [%l1 + %l3] ASI_PHYS_USE_EC, %g2
+ stxa %g2, [%l2 + %l3] ASI_PHYS_USE_EC
+ cmp %l3, %g0
+ bne copy_loop
+ sub %l3, 8, %l3
+
+ /* next pbe */
+ ba pbe_loop
+ ldxa [%l0 + 16] %asi, %l0
+
+restore_ctx:
+ setuw saved_context, %g3
+
+ /* Restore window regs */
+ wrpr %g0, 0, %canrestore
+ wrpr %g0, 0, %otherwin
+ wrpr %g0, 6, %cansave
+ wrpr %g0, 0, %cleanwin
+
+ ldxa [%g3 + SC_REG_CWP] %asi, %g2
+ wrpr %g2, %cwp
+ ldxa [%g3 + SC_REG_WSTATE] %asi, %g2
+ wrpr %g2, %wstate
+ ldxa [%g3 + SC_REG_FP] %asi, %fp
+
+ /* Restore state regs */
+ ldxa [%g3 + SC_REG_PSTATE] %asi, %g2
+ wrpr %g2, %pstate
+ ldxa [%g3 + SC_REG_TICK] %asi, %g2
+ wrpr %g2, %tick
+
+ /* Restore global regs */
+ ldxa [%g3 + SC_REG_G4] %asi, %g4
+ ldxa [%g3 + SC_REG_G5] %asi, %g5
+ ldxa [%g3 + SC_REG_G6] %asi, %g6
+
+ wr %g1, %g0, %asi
+
+ restore
+ restore
+
+ wrpr %g0, 14, %pil
+
+ retl
+ mov %g0, %o0
+
+fill_itlb:
+ ba pbe_loop
+ wrpr %g0, 15, %pil
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index c59ec3ddaa66..3cd397d60434 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -5204,7 +5204,7 @@ static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
if (t4_wait_dev_ready(adap) < 0)
return PCI_ERS_RESULT_DISCONNECT;
- if (t4_fw_hello(adap, adap->fn, adap->fn, MASTER_MUST, NULL))
+ if (t4_fw_hello(adap, adap->fn, adap->fn, MASTER_MUST, NULL) < 0)
return PCI_ERS_RESULT_DISCONNECT;
adap->flags |= FW_OK;
if (adap_init1(adap, &c))
diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h
index 234ce6f07544..f544b297c9ab 100644
--- a/drivers/net/ethernet/emulex/benet/be.h
+++ b/drivers/net/ethernet/emulex/benet/be.h
@@ -327,6 +327,7 @@ enum vf_state {
#define BE_FLAGS_LINK_STATUS_INIT 1
#define BE_FLAGS_WORKER_SCHEDULED (1 << 3)
+#define BE_FLAGS_NAPI_ENABLED (1 << 9)
#define BE_UC_PMAC_COUNT 30
#define BE_VF_UC_PMAC_COUNT 2
#define BE_FLAGS_QNQ_ASYNC_EVT_RCVD (1 << 11)
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 25d3290b8cac..e1e5bb9d9054 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -961,19 +961,8 @@ int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
OPCODE_COMMON_CQ_CREATE, sizeof(*req), wrb, NULL);
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
- if (lancer_chip(adapter)) {
- req->hdr.version = 2;
- req->page_size = 1; /* 1 for 4K */
- AMAP_SET_BITS(struct amap_cq_context_lancer, nodelay, ctxt,
- no_delay);
- AMAP_SET_BITS(struct amap_cq_context_lancer, count, ctxt,
- __ilog2_u32(cq->len/256));
- AMAP_SET_BITS(struct amap_cq_context_lancer, valid, ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context_lancer, eventable,
- ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context_lancer, eqid,
- ctxt, eq->id);
- } else {
+
+ if (BEx_chip(adapter)) {
AMAP_SET_BITS(struct amap_cq_context_be, coalescwm, ctxt,
coalesce_wm);
AMAP_SET_BITS(struct amap_cq_context_be, nodelay,
@@ -983,6 +972,18 @@ int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
AMAP_SET_BITS(struct amap_cq_context_be, valid, ctxt, 1);
AMAP_SET_BITS(struct amap_cq_context_be, eventable, ctxt, 1);
AMAP_SET_BITS(struct amap_cq_context_be, eqid, ctxt, eq->id);
+ } else {
+ req->hdr.version = 2;
+ req->page_size = 1; /* 1 for 4K */
+ AMAP_SET_BITS(struct amap_cq_context_v2, nodelay, ctxt,
+ no_delay);
+ AMAP_SET_BITS(struct amap_cq_context_v2, count, ctxt,
+ __ilog2_u32(cq->len/256));
+ AMAP_SET_BITS(struct amap_cq_context_v2, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context_v2, eventable,
+ ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context_v2, eqid,
+ ctxt, eq->id);
}
be_dws_cpu_to_le(ctxt, sizeof(req->context));
@@ -1763,10 +1764,12 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
req->if_id = cpu_to_le32(adapter->if_handle);
if (flags & IFF_PROMISC) {
req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
- BE_IF_FLAGS_VLAN_PROMISCUOUS);
+ BE_IF_FLAGS_VLAN_PROMISCUOUS |
+ BE_IF_FLAGS_MCAST_PROMISCUOUS);
if (value == ON)
req->if_flags = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
- BE_IF_FLAGS_VLAN_PROMISCUOUS);
+ BE_IF_FLAGS_VLAN_PROMISCUOUS |
+ BE_IF_FLAGS_MCAST_PROMISCUOUS);
} else if (flags & IFF_ALLMULTI) {
req->if_flags_mask = req->if_flags =
cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS);
@@ -2084,7 +2087,7 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
spin_unlock_bh(&adapter->mcc_lock);
if (!wait_for_completion_timeout(&adapter->flash_compl,
- msecs_to_jiffies(30000)))
+ msecs_to_jiffies(60000)))
status = -1;
else
status = adapter->flash_status;
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h
index a855668e0cc5..025bdb0d1764 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -381,7 +381,7 @@ struct amap_cq_context_be {
u8 rsvd5[32]; /* dword 3*/
} __packed;
-struct amap_cq_context_lancer {
+struct amap_cq_context_v2 {
u8 rsvd0[12]; /* dword 0*/
u8 coalescwm[2]; /* dword 0*/
u8 nodelay; /* dword 0*/
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index 5733cde88e2c..3d4461adb3b4 100644
--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -85,6 +85,7 @@ static const struct be_ethtool_stat et_stats[] = {
{DRVSTAT_INFO(tx_pauseframes)},
{DRVSTAT_INFO(tx_controlframes)},
{DRVSTAT_INFO(rx_priority_pause_frames)},
+ {DRVSTAT_INFO(tx_priority_pauseframes)},
/* Received packets dropped when an internal fifo going into
* main packet buffer tank (PMEM) overflows.
*/
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 4babc8a4a543..6c52a60dcdb7 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -410,6 +410,7 @@ static void populate_be_v1_stats(struct be_adapter *adapter)
drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop;
drvs->tx_pauseframes = port_stats->tx_pauseframes;
drvs->tx_controlframes = port_stats->tx_controlframes;
+ drvs->tx_priority_pauseframes = port_stats->tx_priority_pauseframes;
drvs->jabber_events = port_stats->jabber_events;
drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf;
drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr;
@@ -471,11 +472,26 @@ static void accumulate_16bit_val(u32 *acc, u16 val)
ACCESS_ONCE(*acc) = newacc;
}
+void populate_erx_stats(struct be_adapter *adapter,
+ struct be_rx_obj *rxo,
+ u32 erx_stat)
+{
+ if (!BEx_chip(adapter))
+ rx_stats(rxo)->rx_drops_no_frags = erx_stat;
+ else
+ /* below erx HW counter can actually wrap around after
+ * 65535. Driver accumulates a 32-bit value
+ */
+ accumulate_16bit_val(&rx_stats(rxo)->rx_drops_no_frags,
+ (u16)erx_stat);
+}
+
void be_parse_stats(struct be_adapter *adapter)
{
struct be_erx_stats_v1 *erx = be_erx_stats_from_cmd(adapter);
struct be_rx_obj *rxo;
int i;
+ u32 erx_stat;
if (lancer_chip(adapter)) {
populate_lancer_stats(adapter);
@@ -488,12 +504,8 @@ void be_parse_stats(struct be_adapter *adapter)
/* as erx_v1 is longer than v0, ok to use v1 for v0 access */
for_all_rx_queues(adapter, rxo, i) {
- /* below erx HW counter can actually wrap around after
- * 65535. Driver accumulates a 32-bit value
- */
- accumulate_16bit_val(&rx_stats(rxo)->rx_drops_no_frags,
- (u16)erx->rx_drops_no_fragments \
- [rxo->q.id]);
+ erx_stat = erx->rx_drops_no_fragments[rxo->q.id];
+ populate_erx_stats(adapter, rxo, erx_stat);
}
}
}
@@ -2378,7 +2390,7 @@ static uint be_num_rss_want(struct be_adapter *adapter)
return num;
}
-static void be_msix_enable(struct be_adapter *adapter)
+static int be_msix_enable(struct be_adapter *adapter)
{
#define BE_MIN_MSIX_VECTORS 1
int i, status, num_vec, num_roce_vec = 0;
@@ -2403,13 +2415,17 @@ static void be_msix_enable(struct be_adapter *adapter)
goto done;
} else if (status >= BE_MIN_MSIX_VECTORS) {
num_vec = status;
- if (pci_enable_msix(adapter->pdev, adapter->msix_entries,
- num_vec) == 0)
+ status = pci_enable_msix(adapter->pdev, adapter->msix_entries,
+ num_vec);
+ if (!status)
goto done;
}
dev_warn(dev, "MSIx enable failed\n");
- return;
+ /* INTx is not supported in VFs, so fail probe if enable_msix fails */
+ if (!be_physfn(adapter))
+ return status;
+ return 0;
done:
if (be_roce_supported(adapter)) {
if (num_vec > num_roce_vec) {
@@ -2423,7 +2439,7 @@ done:
} else
adapter->num_msix_vec = num_vec;
dev_info(dev, "enabled %d MSI-x vector(s)\n", adapter->num_msix_vec);
- return;
+ return 0;
}
static inline int be_msix_vec_get(struct be_adapter *adapter,
@@ -2536,8 +2552,11 @@ static int be_close(struct net_device *netdev)
be_roce_dev_close(adapter);
- for_all_evt_queues(adapter, eqo, i)
- napi_disable(&eqo->napi);
+ if (adapter->flags & BE_FLAGS_NAPI_ENABLED) {
+ for_all_evt_queues(adapter, eqo, i)
+ napi_disable(&eqo->napi);
+ adapter->flags &= ~BE_FLAGS_NAPI_ENABLED;
+ }
be_async_mcc_disable(adapter);
@@ -2631,7 +2650,9 @@ static int be_open(struct net_device *netdev)
if (status)
goto err;
- be_irq_register(adapter);
+ status = be_irq_register(adapter);
+ if (status)
+ goto err;
for_all_rx_queues(adapter, rxo, i)
be_cq_notify(adapter, rxo->cq.id, true, 0);
@@ -2645,6 +2666,7 @@ static int be_open(struct net_device *netdev)
napi_enable(&eqo->napi);
be_eq_notify(adapter, eqo->q.id, true, false, 0);
}
+ adapter->flags |= BE_FLAGS_NAPI_ENABLED;
status = be_cmd_link_status_query(adapter, NULL, &link_status, 0);
if (!status)
@@ -3100,7 +3122,9 @@ static int be_setup(struct be_adapter *adapter)
if (status)
goto err;
- be_msix_enable(adapter);
+ status = be_msix_enable(adapter);
+ if (status)
+ goto err;
status = be_evt_queues_create(adapter);
if (status)
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 256ae789c143..d175bbd3ffd3 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -2496,10 +2496,12 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2,
skb->ip_summed = re->skb->ip_summed;
skb->csum = re->skb->csum;
skb->rxhash = re->skb->rxhash;
+ skb->vlan_proto = re->skb->vlan_proto;
skb->vlan_tci = re->skb->vlan_tci;
pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr,
length, PCI_DMA_FROMDEVICE);
+ re->skb->vlan_proto = 0;
re->skb->vlan_tci = 0;
re->skb->rxhash = 0;
re->skb->ip_summed = CHECKSUM_NONE;
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 59c43918883e..21a5b291b4b3 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -555,8 +555,8 @@ static int cpsw_poll(struct napi_struct *napi, int budget)
cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
prim_cpsw = cpsw_get_slave_priv(priv, 0);
if (prim_cpsw->irq_enabled == false) {
- cpsw_enable_irq(priv);
prim_cpsw->irq_enabled = true;
+ cpsw_enable_irq(priv);
}
}
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index f7f623a5390e..577c72d5f369 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -100,6 +100,9 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
rx->size);
kfree_skb(rx->ax_skb);
+ rx->ax_skb = NULL;
+ rx->size = 0U;
+
return 0;
}
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index 09699054b54f..03e8a15d7deb 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -256,8 +256,9 @@ static int mdio_read(struct net_device *dev, int phy_id, int loc)
static void mdio_write(struct net_device *dev, int phy_id, int loc, int val)
{
pegasus_t *pegasus = netdev_priv(dev);
+ u16 data = val;
- write_mii_word(pegasus, phy_id, loc, (__u16 *)&val);
+ write_mii_word(pegasus, phy_id, loc, &data);
}
static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata)
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 5a88e72090ce..834e405fb57a 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -548,6 +548,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x19d2, 0x0265, 4)}, /* ONDA MT8205 4G LTE */
{QMI_FIXED_INTF(0x19d2, 0x0284, 4)}, /* ZTE MF880 */
{QMI_FIXED_INTF(0x19d2, 0x0326, 4)}, /* ZTE MF821D */
+ {QMI_FIXED_INTF(0x19d2, 0x0412, 4)}, /* Telewell TW-LTE 4G */
{QMI_FIXED_INTF(0x19d2, 0x1008, 4)}, /* ZTE (Vodafone) K3570-Z */
{QMI_FIXED_INTF(0x19d2, 0x1010, 4)}, /* ZTE (Vodafone) K3571-Z */
{QMI_FIXED_INTF(0x19d2, 0x1012, 4)},
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index a2865f17c667..37984e6d4e99 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -51,9 +51,17 @@
* This is the maximum slots a skb can have. If a guest sends a skb
* which exceeds this limit it is considered malicious.
*/
-#define MAX_SKB_SLOTS_DEFAULT 20
-static unsigned int max_skb_slots = MAX_SKB_SLOTS_DEFAULT;
-module_param(max_skb_slots, uint, 0444);
+#define FATAL_SKB_SLOTS_DEFAULT 20
+static unsigned int fatal_skb_slots = FATAL_SKB_SLOTS_DEFAULT;
+module_param(fatal_skb_slots, uint, 0444);
+
+/*
+ * To avoid confusion, we define XEN_NETBK_LEGACY_SLOTS_MAX indicating
+ * the maximum slots a valid packet can use. Now this value is defined
+ * to be XEN_NETIF_NR_SLOTS_MIN, which is supposed to be supported by
+ * all backend.
+ */
+#define XEN_NETBK_LEGACY_SLOTS_MAX XEN_NETIF_NR_SLOTS_MIN
typedef unsigned int pending_ring_idx_t;
#define INVALID_PENDING_RING_IDX (~0U)
@@ -928,18 +936,20 @@ static void netbk_fatal_tx_err(struct xenvif *vif)
static int netbk_count_requests(struct xenvif *vif,
struct xen_netif_tx_request *first,
- RING_IDX first_idx,
struct xen_netif_tx_request *txp,
int work_to_do)
{
RING_IDX cons = vif->tx.req_cons;
int slots = 0;
int drop_err = 0;
+ int more_data;
if (!(first->flags & XEN_NETTXF_more_data))
return 0;
do {
+ struct xen_netif_tx_request dropped_tx = { 0 };
+
if (slots >= work_to_do) {
netdev_err(vif->dev,
"Asked for %d slots but exceeds this limit\n",
@@ -951,28 +961,32 @@ static int netbk_count_requests(struct xenvif *vif,
/* This guest is really using too many slots and
* considered malicious.
*/
- if (unlikely(slots >= max_skb_slots)) {
+ if (unlikely(slots >= fatal_skb_slots)) {
netdev_err(vif->dev,
"Malicious frontend using %d slots, threshold %u\n",
- slots, max_skb_slots);
+ slots, fatal_skb_slots);
netbk_fatal_tx_err(vif);
return -E2BIG;
}
/* Xen network protocol had implicit dependency on
- * MAX_SKB_FRAGS. XEN_NETIF_NR_SLOTS_MIN is set to the
- * historical MAX_SKB_FRAGS value 18 to honor the same
- * behavior as before. Any packet using more than 18
- * slots but less than max_skb_slots slots is dropped
+ * MAX_SKB_FRAGS. XEN_NETBK_LEGACY_SLOTS_MAX is set to
+ * the historical MAX_SKB_FRAGS value 18 to honor the
+ * same behavior as before. Any packet using more than
+ * 18 slots but less than fatal_skb_slots slots is
+ * dropped
*/
- if (!drop_err && slots >= XEN_NETIF_NR_SLOTS_MIN) {
+ if (!drop_err && slots >= XEN_NETBK_LEGACY_SLOTS_MAX) {
if (net_ratelimit())
netdev_dbg(vif->dev,
"Too many slots (%d) exceeding limit (%d), dropping packet\n",
- slots, XEN_NETIF_NR_SLOTS_MIN);
+ slots, XEN_NETBK_LEGACY_SLOTS_MAX);
drop_err = -E2BIG;
}
+ if (drop_err)
+ txp = &dropped_tx;
+
memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + slots),
sizeof(*txp));
@@ -1002,10 +1016,16 @@ static int netbk_count_requests(struct xenvif *vif,
netbk_fatal_tx_err(vif);
return -EINVAL;
}
- } while ((txp++)->flags & XEN_NETTXF_more_data);
+
+ more_data = txp->flags & XEN_NETTXF_more_data;
+
+ if (!drop_err)
+ txp++;
+
+ } while (more_data);
if (drop_err) {
- netbk_tx_err(vif, first, first_idx + slots);
+ netbk_tx_err(vif, first, cons + slots);
return drop_err;
}
@@ -1042,7 +1062,7 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
struct pending_tx_info *first = NULL;
/* At this point shinfo->nr_frags is in fact the number of
- * slots, which can be as large as XEN_NETIF_NR_SLOTS_MIN.
+ * slots, which can be as large as XEN_NETBK_LEGACY_SLOTS_MAX.
*/
nr_slots = shinfo->nr_frags;
@@ -1404,12 +1424,12 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
struct sk_buff *skb;
int ret;
- while ((nr_pending_reqs(netbk) + XEN_NETIF_NR_SLOTS_MIN
+ while ((nr_pending_reqs(netbk) + XEN_NETBK_LEGACY_SLOTS_MAX
< MAX_PENDING_REQS) &&
!list_empty(&netbk->net_schedule_list)) {
struct xenvif *vif;
struct xen_netif_tx_request txreq;
- struct xen_netif_tx_request txfrags[max_skb_slots];
+ struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX];
struct page *page;
struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1];
u16 pending_idx;
@@ -1470,8 +1490,7 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
continue;
}
- ret = netbk_count_requests(vif, &txreq, idx,
- txfrags, work_to_do);
+ ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do);
if (unlikely(ret < 0))
continue;
@@ -1498,7 +1517,7 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
pending_idx = netbk->pending_ring[index];
data_len = (txreq.size > PKT_PROT_LEN &&
- ret < XEN_NETIF_NR_SLOTS_MIN) ?
+ ret < XEN_NETBK_LEGACY_SLOTS_MAX) ?
PKT_PROT_LEN : txreq.size;
skb = alloc_skb(data_len + NET_SKB_PAD + NET_IP_ALIGN,
@@ -1777,7 +1796,7 @@ static inline int rx_work_todo(struct xen_netbk *netbk)
static inline int tx_work_todo(struct xen_netbk *netbk)
{
- if ((nr_pending_reqs(netbk) + XEN_NETIF_NR_SLOTS_MIN
+ if ((nr_pending_reqs(netbk) + XEN_NETBK_LEGACY_SLOTS_MAX
< MAX_PENDING_REQS) &&
!list_empty(&netbk->net_schedule_list))
return 1;
@@ -1862,11 +1881,11 @@ static int __init netback_init(void)
if (!xen_domain())
return -ENODEV;
- if (max_skb_slots < XEN_NETIF_NR_SLOTS_MIN) {
+ if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) {
printk(KERN_INFO
- "xen-netback: max_skb_slots too small (%d), bump it to XEN_NETIF_NR_SLOTS_MIN (%d)\n",
- max_skb_slots, XEN_NETIF_NR_SLOTS_MIN);
- max_skb_slots = XEN_NETIF_NR_SLOTS_MIN;
+ "xen-netback: fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n",
+ fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX);
+ fatal_skb_slots = XEN_NETBK_LEGACY_SLOTS_MAX;
}
xen_netbk_group_nr = num_online_cpus();
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index 451687cb9685..0d8465728473 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -1592,6 +1592,7 @@ static int __init sunsu_init(void)
static void __exit sunsu_exit(void)
{
+ platform_driver_unregister(&su_driver);
if (sunsu_reg.nr)
sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr);
}
diff --git a/ipc/sem.c b/ipc/sem.c
index 4734e9c2a98a..899b598b63be 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -264,12 +264,13 @@ static inline void sem_unlock(struct sem_array *sma, int locknum)
struct sem *sem = sma->sem_base + locknum;
spin_unlock(&sem->lock);
}
- rcu_read_unlock();
}
/*
* sem_lock_(check_) routines are called in the paths where the rw_mutex
* is not held.
+ *
+ * The caller holds the RCU read lock.
*/
static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
int id, struct sembuf *sops, int nsops, int *locknum)
@@ -277,12 +278,9 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
struct kern_ipc_perm *ipcp;
struct sem_array *sma;
- rcu_read_lock();
ipcp = ipc_obtain_object(&sem_ids(ns), id);
- if (IS_ERR(ipcp)) {
- sma = ERR_CAST(ipcp);
- goto err;
- }
+ if (IS_ERR(ipcp))
+ return ERR_CAST(ipcp);
sma = container_of(ipcp, struct sem_array, sem_perm);
*locknum = sem_lock(sma, sops, nsops);
@@ -294,10 +292,7 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
return container_of(ipcp, struct sem_array, sem_perm);
sem_unlock(sma, *locknum);
- sma = ERR_PTR(-EINVAL);
-err:
- rcu_read_unlock();
- return sma;
+ return ERR_PTR(-EINVAL);
}
static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id)
@@ -323,15 +318,13 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns
static inline void sem_lock_and_putref(struct sem_array *sma)
{
- rcu_read_lock();
sem_lock(sma, NULL, -1);
ipc_rcu_putref(sma);
}
static inline void sem_putref(struct sem_array *sma)
{
- sem_lock_and_putref(sma);
- sem_unlock(sma, -1);
+ ipc_rcu_putref(sma);
}
static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
@@ -435,6 +428,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
sma->sem_nsems = nsems;
sma->sem_ctime = get_seconds();
sem_unlock(sma, -1);
+ rcu_read_unlock();
return sma->sem_perm.id;
}
@@ -874,6 +868,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
/* Remove the semaphore set from the IDR */
sem_rmid(ns, sma);
sem_unlock(sma, -1);
+ rcu_read_unlock();
wake_up_sem_queue_do(&tasks);
ns->used_sems -= sma->sem_nsems;
@@ -953,8 +948,8 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
memset(&tbuf, 0, sizeof(tbuf));
+ rcu_read_lock();
if (cmd == SEM_STAT) {
- rcu_read_lock();
sma = sem_obtain_object(ns, semid);
if (IS_ERR(sma)) {
err = PTR_ERR(sma);
@@ -962,7 +957,6 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
}
id = sma->sem_perm.id;
} else {
- rcu_read_lock();
sma = sem_obtain_object_check(ns, semid);
if (IS_ERR(sma)) {
err = PTR_ERR(sma);
@@ -1055,6 +1049,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
/* maybe some queued-up processes were waiting for this */
do_smart_update(sma, NULL, 0, 0, &tasks);
sem_unlock(sma, -1);
+ rcu_read_unlock();
wake_up_sem_queue_do(&tasks);
return 0;
}
@@ -1081,17 +1076,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
nsems = sma->sem_nsems;
err = -EACCES;
- if (ipcperms(ns, &sma->sem_perm,
- cmd == SETALL ? S_IWUGO : S_IRUGO)) {
- rcu_read_unlock();
- goto out_wakeup;
- }
+ if (ipcperms(ns, &sma->sem_perm, cmd == SETALL ? S_IWUGO : S_IRUGO))
+ goto out_rcu_wakeup;
err = security_sem_semctl(sma, cmd);
- if (err) {
- rcu_read_unlock();
- goto out_wakeup;
- }
+ if (err)
+ goto out_rcu_wakeup;
err = -EACCES;
switch (cmd) {
@@ -1104,19 +1094,23 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
if(nsems > SEMMSL_FAST) {
if (!ipc_rcu_getref(sma)) {
sem_unlock(sma, -1);
+ rcu_read_unlock();
err = -EIDRM;
goto out_free;
}
sem_unlock(sma, -1);
+ rcu_read_unlock();
sem_io = ipc_alloc(sizeof(ushort)*nsems);
if(sem_io == NULL) {
sem_putref(sma);
return -ENOMEM;
}
+ rcu_read_lock();
sem_lock_and_putref(sma);
if (sma->sem_perm.deleted) {
sem_unlock(sma, -1);
+ rcu_read_unlock();
err = -EIDRM;
goto out_free;
}
@@ -1124,6 +1118,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
for (i = 0; i < sma->sem_nsems; i++)
sem_io[i] = sma->sem_base[i].semval;
sem_unlock(sma, -1);
+ rcu_read_unlock();
err = 0;
if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
err = -EFAULT;
@@ -1161,9 +1156,11 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
goto out_free;
}
}
+ rcu_read_lock();
sem_lock_and_putref(sma);
if (sma->sem_perm.deleted) {
sem_unlock(sma, -1);
+ rcu_read_unlock();
err = -EIDRM;
goto out_free;
}
@@ -1185,10 +1182,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
/* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */
}
err = -EINVAL;
- if (semnum < 0 || semnum >= nsems) {
- rcu_read_unlock();
- goto out_wakeup;
- }
+ if (semnum < 0 || semnum >= nsems)
+ goto out_rcu_wakeup;
sem_lock(sma, NULL, -1);
curr = &sma->sem_base[semnum];
@@ -1210,7 +1205,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
out_unlock:
sem_unlock(sma, -1);
-out_wakeup:
+out_rcu_wakeup:
+ rcu_read_unlock();
wake_up_sem_queue_do(&tasks);
out_free:
if(sem_io != fast_sem_io)
@@ -1272,7 +1268,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
err = security_sem_semctl(sma, cmd);
if (err) {
rcu_read_unlock();
- goto out_unlock;
+ goto out_up;
}
switch(cmd){
@@ -1295,6 +1291,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
out_unlock:
sem_unlock(sma, -1);
+ rcu_read_unlock();
out_up:
up_write(&sem_ids(ns).rw_mutex);
return err;
@@ -1443,9 +1440,11 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
}
/* step 3: Acquire the lock on semaphore array */
+ rcu_read_lock();
sem_lock_and_putref(sma);
if (sma->sem_perm.deleted) {
sem_unlock(sma, -1);
+ rcu_read_unlock();
kfree(new);
un = ERR_PTR(-EIDRM);
goto out;
@@ -1472,7 +1471,6 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
success:
spin_unlock(&ulp->lock);
- rcu_read_lock();
sem_unlock(sma, -1);
out:
return un;
@@ -1579,22 +1577,16 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
}
error = -EFBIG;
- if (max >= sma->sem_nsems) {
- rcu_read_unlock();
- goto out_wakeup;
- }
+ if (max >= sma->sem_nsems)
+ goto out_rcu_wakeup;
error = -EACCES;
- if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) {
- rcu_read_unlock();
- goto out_wakeup;
- }
+ if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
+ goto out_rcu_wakeup;
error = security_sem_semop(sma, sops, nsops, alter);
- if (error) {
- rcu_read_unlock();
- goto out_wakeup;
- }
+ if (error)
+ goto out_rcu_wakeup;
/*
* semid identifiers are not unique - find_alloc_undo may have
@@ -1648,6 +1640,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
sleep_again:
current->state = TASK_INTERRUPTIBLE;
sem_unlock(sma, locknum);
+ rcu_read_unlock();
if (timeout)
jiffies_left = schedule_timeout(jiffies_left);
@@ -1669,6 +1662,7 @@ sleep_again:
goto out_free;
}
+ rcu_read_lock();
sma = sem_obtain_lock(ns, semid, sops, nsops, &locknum);
/*
@@ -1680,6 +1674,7 @@ sleep_again:
* Array removed? If yes, leave without sem_unlock().
*/
if (IS_ERR(sma)) {
+ rcu_read_unlock();
goto out_free;
}
@@ -1709,7 +1704,8 @@ sleep_again:
out_unlock_free:
sem_unlock(sma, locknum);
-out_wakeup:
+out_rcu_wakeup:
+ rcu_read_unlock();
wake_up_sem_queue_do(&tasks);
out_free:
if(sops != fast_sops)
@@ -1801,6 +1797,7 @@ void exit_sem(struct task_struct *tsk)
* exactly the same semid. Nothing to do.
*/
sem_unlock(sma, -1);
+ rcu_read_unlock();
continue;
}
@@ -1841,6 +1838,7 @@ void exit_sem(struct task_struct *tsk)
INIT_LIST_HEAD(&tasks);
do_smart_update(sma, NULL, 0, 1, &tasks);
sem_unlock(sma, -1);
+ rcu_read_unlock();
wake_up_sem_queue_do(&tasks);
kfree_rcu(un, rcu);
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 8af508536d36..3a8c8fd63c88 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -628,7 +628,7 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev,
netdev_features_t features)
{
struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
- u32 old_features = features;
+ netdev_features_t old_features = features;
features &= real_dev->vlan_features;
features |= NETIF_F_RXCSUM;
diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c
index c3530a81a33b..950663d4d330 100644
--- a/net/bridge/br_stp_timer.c
+++ b/net/bridge/br_stp_timer.c
@@ -107,7 +107,7 @@ static void br_tcn_timer_expired(unsigned long arg)
br_debug(br, "tcn timer expired\n");
spin_lock(&br->lock);
- if (br->dev->flags & IFF_UP) {
+ if (!br_is_root_bridge(br) && (br->dev->flags & IFF_UP)) {
br_transmit_tcn(br);
mod_timer(&br->tcn_timer,jiffies + br->bridge_hello_time);
diff --git a/net/core/dev.c b/net/core/dev.c
index 4040673f806a..40b1fadaf637 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2456,7 +2456,7 @@ EXPORT_SYMBOL(netif_skb_features);
* 2. skb is fragmented and the device does not support SG.
*/
static inline int skb_needs_linearize(struct sk_buff *skb,
- int features)
+ netdev_features_t features)
{
return skb_is_nonlinear(skb) &&
((skb_has_frag_list(skb) &&
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 5a934ef90f8b..22efdaa76ebf 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1421,7 +1421,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
void __user *useraddr = ifr->ifr_data;
u32 ethcmd;
int rc;
- u32 old_features;
+ netdev_features_t old_features;
if (!dev || !netif_device_present(dev))
return -ENODEV;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index c61b3bb87a16..d01be2a3ae53 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1293,6 +1293,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
SKB_GSO_GRE |
+ SKB_GSO_TCPV6 |
SKB_GSO_UDP_TUNNEL |
0)))
goto out;
diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c
index d2d5a99fba09..cc22363965d2 100644
--- a/net/ipv4/gre.c
+++ b/net/ipv4/gre.c
@@ -121,6 +121,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
int ghl = GRE_HEADER_SECTION;
struct gre_base_hdr *greh;
int mac_len = skb->mac_len;
+ __be16 protocol = skb->protocol;
int tnl_hlen;
bool csum;
@@ -150,7 +151,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
/* setup inner skb. */
if (greh->protocol == htons(ETH_P_TEB)) {
- struct ethhdr *eth = eth_hdr(skb);
+ struct ethhdr *eth = (struct ethhdr *)skb_inner_mac_header(skb);
skb->protocol = eth->h_proto;
} else {
skb->protocol = greh->protocol;
@@ -199,6 +200,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
skb_reset_mac_header(skb);
skb_set_network_header(skb, mac_len);
skb->mac_len = mac_len;
+ skb->protocol = protocol;
} while ((skb = skb->next));
out:
return segs;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 6abbe6455129..0ae038a4c7a8 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2311,8 +2311,10 @@ static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
struct sk_buff *segs = ERR_PTR(-EINVAL);
int mac_len = skb->mac_len;
int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
- int outer_hlen;
+ struct ethhdr *inner_eth = (struct ethhdr *)skb_inner_mac_header(skb);
+ __be16 protocol = skb->protocol;
netdev_features_t enc_features;
+ int outer_hlen;
if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
goto out;
@@ -2322,6 +2324,8 @@ static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
skb_reset_mac_header(skb);
skb_set_network_header(skb, skb_inner_network_offset(skb));
skb->mac_len = skb_inner_network_offset(skb);
+ inner_eth = (struct ethhdr *)skb_mac_header(skb);
+ skb->protocol = inner_eth->h_proto;
/* segment inner packet. */
enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
@@ -2358,6 +2362,7 @@ static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
}
skb->ip_summed = CHECKSUM_NONE;
+ skb->protocol = protocol;
} while ((skb = skb->next));
out:
return segs;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index dd5cd49b0e09..8ec1bca7f859 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -742,36 +742,33 @@ static void prb_open_block(struct tpacket_kbdq_core *pkc1,
smp_rmb();
- if (likely(TP_STATUS_KERNEL == BLOCK_STATUS(pbd1))) {
+ /* We could have just memset this but we will lose the
+ * flexibility of making the priv area sticky
+ */
- /* We could have just memset this but we will lose the
- * flexibility of making the priv area sticky
- */
- BLOCK_SNUM(pbd1) = pkc1->knxt_seq_num++;
- BLOCK_NUM_PKTS(pbd1) = 0;
- BLOCK_LEN(pbd1) = BLK_PLUS_PRIV(pkc1->blk_sizeof_priv);
- getnstimeofday(&ts);
- h1->ts_first_pkt.ts_sec = ts.tv_sec;
- h1->ts_first_pkt.ts_nsec = ts.tv_nsec;
- pkc1->pkblk_start = (char *)pbd1;
- pkc1->nxt_offset = pkc1->pkblk_start + BLK_PLUS_PRIV(pkc1->blk_sizeof_priv);
- BLOCK_O2FP(pbd1) = (__u32)BLK_PLUS_PRIV(pkc1->blk_sizeof_priv);
- BLOCK_O2PRIV(pbd1) = BLK_HDR_LEN;
- pbd1->version = pkc1->version;
- pkc1->prev = pkc1->nxt_offset;
- pkc1->pkblk_end = pkc1->pkblk_start + pkc1->kblk_size;
- prb_thaw_queue(pkc1);
- _prb_refresh_rx_retire_blk_timer(pkc1);
+ BLOCK_SNUM(pbd1) = pkc1->knxt_seq_num++;
+ BLOCK_NUM_PKTS(pbd1) = 0;
+ BLOCK_LEN(pbd1) = BLK_PLUS_PRIV(pkc1->blk_sizeof_priv);
- smp_wmb();
+ getnstimeofday(&ts);
- return;
- }
+ h1->ts_first_pkt.ts_sec = ts.tv_sec;
+ h1->ts_first_pkt.ts_nsec = ts.tv_nsec;
- WARN(1, "ERROR block:%p is NOT FREE status:%d kactive_blk_num:%d\n",
- pbd1, BLOCK_STATUS(pbd1), pkc1->kactive_blk_num);
- dump_stack();
- BUG();
+ pkc1->pkblk_start = (char *)pbd1;
+ pkc1->nxt_offset = pkc1->pkblk_start + BLK_PLUS_PRIV(pkc1->blk_sizeof_priv);
+
+ BLOCK_O2FP(pbd1) = (__u32)BLK_PLUS_PRIV(pkc1->blk_sizeof_priv);
+ BLOCK_O2PRIV(pbd1) = BLK_HDR_LEN;
+
+ pbd1->version = pkc1->version;
+ pkc1->prev = pkc1->nxt_offset;
+ pkc1->pkblk_end = pkc1->pkblk_start + pkc1->kblk_size;
+
+ prb_thaw_queue(pkc1);
+ _prb_refresh_rx_retire_blk_timer(pkc1);
+
+ smp_wmb();
}
/*
@@ -862,10 +859,6 @@ static void prb_retire_current_block(struct tpacket_kbdq_core *pkc,
prb_close_block(pkc, pbd, po, status);
return;
}
-
- WARN(1, "ERROR-pbd[%d]:%p\n", pkc->kactive_blk_num, pbd);
- dump_stack();
- BUG();
}
static int prb_curr_blk_in_use(struct tpacket_kbdq_core *pkc,
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index 25e159c2feb4..e5f3da507823 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -584,8 +584,7 @@ static int tipc_bcbearer_send(struct sk_buff *buf,
{
int bp_index;
- /*
- * Prepare broadcast link message for reliable transmission,
+ /* Prepare broadcast link message for reliable transmission,
* if first time trying to send it;
* preparation is skipped for broadcast link protocol messages
* since they are sent in an unreliable manner and don't need it
@@ -611,30 +610,43 @@ static int tipc_bcbearer_send(struct sk_buff *buf,
for (bp_index = 0; bp_index < MAX_BEARERS; bp_index++) {
struct tipc_bearer *p = bcbearer->bpairs[bp_index].primary;
struct tipc_bearer *s = bcbearer->bpairs[bp_index].secondary;
+ struct tipc_bearer *b = p;
+ struct sk_buff *tbuf;
if (!p)
- break; /* no more bearers to try */
+ break; /* No more bearers to try */
+
+ if (tipc_bearer_blocked(p)) {
+ if (!s || tipc_bearer_blocked(s))
+ continue; /* Can't use either bearer */
+ b = s;
+ }
- tipc_nmap_diff(&bcbearer->remains, &p->nodes, &bcbearer->remains_new);
+ tipc_nmap_diff(&bcbearer->remains, &b->nodes,
+ &bcbearer->remains_new);
if (bcbearer->remains_new.count == bcbearer->remains.count)
- continue; /* bearer pair doesn't add anything */
+ continue; /* Nothing added by bearer pair */
- if (!tipc_bearer_blocked(p))
- tipc_bearer_send(p, buf, &p->bcast_addr);
- else if (s && !tipc_bearer_blocked(s))
- /* unable to send on primary bearer */
- tipc_bearer_send(s, buf, &s->bcast_addr);
- else
- /* unable to send on either bearer */
- continue;
+ if (bp_index == 0) {
+ /* Use original buffer for first bearer */
+ tipc_bearer_send(b, buf, &b->bcast_addr);
+ } else {
+ /* Avoid concurrent buffer access */
+ tbuf = pskb_copy(buf, GFP_ATOMIC);
+ if (!tbuf)
+ break;
+ tipc_bearer_send(b, tbuf, &b->bcast_addr);
+ kfree_skb(tbuf); /* Bearer keeps a clone */
+ }
+ /* Swap bearers for next packet */
if (s) {
bcbearer->bpairs[bp_index].primary = s;
bcbearer->bpairs[bp_index].secondary = p;
}
if (bcbearer->remains_new.count == 0)
- break; /* all targets reached */
+ break; /* All targets reached */
bcbearer->remains = bcbearer->remains_new;
}