diff --git a/Makefile b/Makefile index 2e7cbda..b011199 100644 --- a/Makefile +++ b/Makefile @@ -559,9 +559,13 @@ endif # $(dot-config) all: vmlinux ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE -KBUILD_CFLAGS += -Os +KBUILD_CFLAGS += -Os -fno-caller-saves else -KBUILD_CFLAGS += -O2 +ifdef CONFIG_COMCERTO_CC_OPTIMIZE_O3 +KBUILD_CFLAGS += -O3 -fno-reorder-blocks -fno-tree-ch -fno-caller-saves +else +KBUILD_CFLAGS += -O2 -fno-reorder-blocks -fno-tree-ch -fno-caller-saves +endif endif include $(srctree)/arch/$(SRCARCH)/Makefile @@ -622,6 +626,9 @@ endif NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) CHECKFLAGS += $(NOSTDINC_FLAGS) +# improve gcc optimization +CFLAGS += $(call cc-option,-funit-at-a-time,) + # warn about C99 declaration after statement KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 082bd36..ec1c78a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -266,6 +266,16 @@ config ARCH_INTEGRATOR help Support for ARM's Integrator platform. +config ARCH_COMCERTO + bool "Mindspeed Comcerto" + select ARCH_SUPPORTS_MSI + select NEED_MACH_MEMORY_H + select ARCH_REQUIRE_GPIOLIB + help + This enables support for Mindspeed's Comcerto development boards. + If you would like to build your kernel to run on one of these boards + then you must say 'Y' here. Otherwise say 'N' + config ARCH_REALVIEW bool "ARM Ltd. RealView family" select ARM_AMBA @@ -1100,6 +1110,8 @@ source "arch/arm/mach-vt8500/Kconfig" source "arch/arm/mach-w90x900/Kconfig" +source "arch/arm/mach-comcerto/Kconfig" + # Definitions to make life easier config ARCH_ACORN bool @@ -1452,7 +1464,7 @@ config SMP depends on REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP || \ MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \ ARCH_EXYNOS4 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || \ - ARCH_MSM_SCORPIONMP || ARCH_SHMOBILE || ARCH_HIGHBANK || SOC_IMX6Q + ARCH_MSM_SCORPIONMP || ARCH_SHMOBILE || ARCH_HIGHBANK || SOC_IMX6Q || ARCH_COMCERTO depends on MMU select USE_GENERIC_SMP_HELPERS select HAVE_ARM_SCU if !ARCH_MSM_SCORPIONMP @@ -1516,6 +1528,11 @@ config HAVE_ARM_SCU help This option enables support for the ARM system coherency unit +config SCU_SPECULATIVE_LINE_FILLS + bool "SCU speculative line fills" + depends on HAVE_ARM_SCU && CACHE_PL310 + default n + config HAVE_ARM_TWD bool depends on SMP diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 362c7ca..3625ec0 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -26,7 +26,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-dwarf2-cfi-asm) # Do not use arch/arm/defconfig - it's always outdated. # Select a platform tht is kept up-to-date -KBUILD_DEFCONFIG := versatile_defconfig +KBUILD_DEFCONFIG := c2krtsm_defconfig # defines filename extension depending memory management type. ifeq ($(CONFIG_MMU),) @@ -138,6 +138,10 @@ machine-$(CONFIG_ARCH_AT91) := at91 machine-$(CONFIG_ARCH_BCMRING) := bcmring machine-$(CONFIG_ARCH_CLPS711X) := clps711x machine-$(CONFIG_ARCH_CNS3XXX) := cns3xxx +machine-$(CONFIG_ARCH_COMCERTO) := comcerto +ifeq ($(CONFIG_ARCH_COMCERTO),y) +textofs-$(CONFIG_ZONE_DMA) := 0x04008000 +endif machine-$(CONFIG_ARCH_DAVINCI) := davinci machine-$(CONFIG_ARCH_DOVE) := dove machine-$(CONFIG_ARCH_EBSA110) := ebsa110 @@ -199,6 +203,8 @@ machine-$(CONFIG_MACH_SPEAR320) := spear3xx machine-$(CONFIG_MACH_SPEAR600) := spear6xx machine-$(CONFIG_ARCH_ZYNQ) := zynq + + # Platform directory name. This list is sorted alphanumerically # by CONFIG_* macro name. plat-$(CONFIG_ARCH_MXC) := mxc diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 5954a1a..c0aa22b 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -33,6 +33,11 @@ CFLAGS_string.o := -Os # # Architecture dependencies # + +ifeq ($(CONFIG_ARCH_COMCERTO),y) +OBJS += head-comcerto.o +endif + ifeq ($(CONFIG_ARCH_ACORN),y) OBJS += ll_char_wr.o font.o endif diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 410a546..c133fba 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -43,6 +43,8 @@ #include #include +#include + static DEFINE_RAW_SPINLOCK(irq_controller_lock); /* Address of GIC 0 CPU interface */ @@ -90,30 +92,48 @@ static inline unsigned int gic_irq(struct irq_data *d) static void gic_mask_irq(struct irq_data *d) { u32 mask = 1 << (gic_irq(d) % 32); + unsigned long flags; + + if ((gic_irq(d) == 87) || (gic_irq(d) == 66) || (gic_irq(d) == 33)) { + return; + } raw_spin_lock(&irq_controller_lock); + flags = msp_lock_frqsave(); writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); if (gic_arch_extn.irq_mask) gic_arch_extn.irq_mask(d); + msp_unlock_frqrestore(flags); raw_spin_unlock(&irq_controller_lock); } static void gic_unmask_irq(struct irq_data *d) { u32 mask = 1 << (gic_irq(d) % 32); + unsigned long flags; + + if ((gic_irq(d) == 87) || (gic_irq(d) == 66) || (gic_irq(d) == 33)) { + return; + } raw_spin_lock(&irq_controller_lock); + flags = msp_lock_frqsave(); if (gic_arch_extn.irq_unmask) gic_arch_extn.irq_unmask(d); writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); + msp_unlock_frqrestore(flags); raw_spin_unlock(&irq_controller_lock); } static void gic_eoi_irq(struct irq_data *d) { if (gic_arch_extn.irq_eoi) { + unsigned long flags; + raw_spin_lock(&irq_controller_lock); + flags = msp_lock_frqsave(); gic_arch_extn.irq_eoi(d); + msp_unlock_frqrestore(flags); raw_spin_unlock(&irq_controller_lock); } @@ -308,6 +328,14 @@ static void __init gic_dist_init(struct gic_chip_data *gic) for (i = 32; i < gic_irqs; i += 32) writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); +#ifdef CONFIG_COMCERTO_MSP + /* + * Set SPI interrupts are nonSecure + */ + for (i = 32; i < gic_irqs; i += 32) + writel_relaxed(0xffffffff, base + GIC_DIST_SECURITY_BIT + i * 4 / 32); +#endif /* CONFIG_COMCERTO_MSP */ + /* * Setup the Linux IRQ subsystem. */ @@ -325,7 +353,14 @@ static void __init gic_dist_init(struct gic_chip_data *gic) irq_set_chip_data(irq, gic); } +#ifdef CONFIG_COMCERTO_MSP + /* + * Enable NonSecure interrupts in Distributor + */ + writel_relaxed(3, base + GIC_DIST_CTRL); +#else /* !CONFIG_COMCERTO_MSP */ writel_relaxed(1, base + GIC_DIST_CTRL); +#endif /* CONFIG_COMCERTO_MSP */ } static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) @@ -348,9 +383,62 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4); writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); + +#ifdef CONFIG_COMCERTO_MSP + /* + * Set PPI and SGI interrupts are nonSecure + */ + writel_relaxed(0xffffffff, dist_base + GIC_DIST_SECURITY_BIT); + + /* + * Enable NonSecure interrupts in CPU interface, + * Secure interrupts go to FIQ line, + * Secure read returns valid NonSecure interrupt ID + */ + writel_relaxed(0xf, base + GIC_CPU_CTRL); +#else /* !CONFIG_COMCERTO_MSP */ writel_relaxed(1, base + GIC_CPU_CTRL); +#endif /* CONFIG_COMCERTO_MSP */ +} + +#ifdef CONFIG_COMCERTO_MSP + +static void __cpuinit gic_cpu_init_irq_only(struct gic_chip_data *gic) +{ + void __iomem *dist_base = gic->dist_base; + void __iomem *base = gic->cpu_base; + int i; + + /* + * Deal with the banked PPI and SGI interrupts - disable all + * PPI interrupts, ensure all SGI interrupts are enabled. + */ + writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR); + writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET); + + /* + * Set priority on PPI and SGI interrupts + */ + for (i = 0; i < 32; i += 4) + writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4); + + writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); + + /* + * Set PPI and SGI interrupts are nonSecure + */ + writel_relaxed(0xffffffff, dist_base + GIC_DIST_SECURITY_BIT); + + /* + * Enable NonSecure interrupts in CPU interface, + * Secure interrupts go to IRQ line, + * Secure read returns valid NonSecure interrupt ID + */ + writel_relaxed(0x7, base + GIC_CPU_CTRL); } +#endif /* CONFIG_COMCERTO_MSP */ + #ifdef CONFIG_CPU_PM /* * Saves the GIC distributor registers during suspend or idle. Must be called @@ -625,7 +713,12 @@ void __cpuinit gic_secondary_init(unsigned int gic_nr) { BUG_ON(gic_nr >= MAX_GIC_NR); +#ifdef CONFIG_COMCERTO_MSP + /* run alternative secondary_boot gic init */ + gic_cpu_init_irq_only(&gic_data[gic_nr]); +#else /* !CONFIG_COMCERTO_MSP */ gic_cpu_init(&gic_data[gic_nr]); +#endif /* CONFIG_COMCERTO_MSP */ } #ifdef CONFIG_SMP @@ -645,7 +738,16 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) dsb(); /* this always happens on GIC0 */ + +#ifdef CONFIG_COMCERTO_MSP +#define GIC_SGI_SATT (1 << 15) + /* + * Send SGI from Secure write to NonSecure target + */ + writel_relaxed(map << 16 | GIC_SGI_SATT | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); +#else /* !CONFIG_COMCERTO_MSP */ writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); +#endif /* CONFIG_COMCERTO_MSP */ } #endif diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index 0e9ce8d..7f02c54 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -109,7 +109,7 @@ int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); #define ELF_CORE_COPY_TASK_REGS dump_task_regs #define CORE_DUMP_USE_REGSET -#define ELF_EXEC_PAGESIZE 4096 +#define ELF_EXEC_PAGESIZE (PAGE_SIZE) /* This is the location that an ET_DYN program is loaded if exec'ed. Typical use of this is to invoke "./ld.so someprog" to test out a new version of diff --git a/arch/arm/include/asm/hardware/entry-macro-gic.S b/arch/arm/include/asm/hardware/entry-macro-gic.S index 74ebc80..c5ba98a 100644 --- a/arch/arm/include/asm/hardware/entry-macro-gic.S +++ b/arch/arm/include/asm/hardware/entry-macro-gic.S @@ -34,6 +34,22 @@ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp +#ifdef CONFIG_COMCERTO_MSP + ldr \irqstat, [\base, #GIC_CPU_HIGHPRI] + bic \irqnr, \irqstat, #0x1c00 + cmp \irqnr, #33 + cmpne \irqnr, #66 + cmpne \irqnr, #87 + cmpeq \irqnr, \irqnr + bne 1001f + + mov \irqnr, \irqnr /* breakpoint here */ + + beq 1002f + +1001: +#endif /* CONFIG_COMCERTO_MSP */ + ldr \irqstat, [\base, #GIC_CPU_INTACK] /* bits 12-10 = src CPU, 9-0 = int # */ @@ -43,6 +59,11 @@ cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr + +#ifdef CONFIG_COMCERTO_MSP +1002: +#endif /* CONFIG_COMCERTO_MSP */ + .endm /* We assume that irqstat (the raw value of the IRQ acknowledge diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h index 3e91f22..7177ea1 100644 --- a/arch/arm/include/asm/hardware/gic.h +++ b/arch/arm/include/asm/hardware/gic.h @@ -22,6 +22,7 @@ #define GIC_DIST_CTRL 0x000 #define GIC_DIST_CTR 0x004 +#define GIC_DIST_SECURITY_BIT 0x080 #define GIC_DIST_ENABLE_SET 0x100 #define GIC_DIST_ENABLE_CLEAR 0x180 #define GIC_DIST_PENDING_SET 0x200 diff --git a/arch/arm/include/asm/kexec.h b/arch/arm/include/asm/kexec.h index c2b9b4b..ddd7fbc 100644 --- a/arch/arm/include/asm/kexec.h +++ b/arch/arm/include/asm/kexec.h @@ -10,7 +10,7 @@ /* Maximum address we can use for the control code buffer */ #define KEXEC_CONTROL_MEMORY_LIMIT (-1UL) -#define KEXEC_CONTROL_PAGE_SIZE 4096 +#define KEXEC_CONTROL_PAGE_SIZE (PAGE_SIZE) #define KEXEC_ARCH KEXEC_ARCH_ARM diff --git a/arch/arm/include/asm/mach/map.h b/arch/arm/include/asm/mach/map.h index b36f365..520d5b6 100644 --- a/arch/arm/include/asm/mach/map.h +++ b/arch/arm/include/asm/mach/map.h @@ -30,6 +30,8 @@ struct map_desc { #define MT_MEMORY_DTCM 12 #define MT_MEMORY_ITCM 13 #define MT_MEMORY_SO 14 +#define MT_MSP 15 +#define MT_MSP_NCNB 16 #ifdef CONFIG_MMU extern void iotable_init(struct map_desc *, int); diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index a8997d7..a8941af 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -35,28 +35,31 @@ * TASK_SIZE - the maximum size of a user space task. * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area */ +#include +#if !defined(CONFIG_COMCERTO_64K_PAGES) #define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET) #define TASK_SIZE (UL(CONFIG_PAGE_OFFSET) - UL(0x01000000)) #define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3) - +#else +#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET) +#define TASK_SIZE ((UL(CONFIG_PAGE_OFFSET) - UL(0x01000000)) & ~(UL((1 << PMD_SHIFT)-1))) // Must be aligned on PMD size (kernel/user space can share same PMD) +#define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3) +#endif /* * The maximum size of a 26-bit user space task. */ #define TASK_SIZE_26 UL(0x04000000) +#ifndef CONFIG_COMCERTO_ZONE_DMA_NCNB /* * The module space lives between the addresses given by TASK_SIZE * and PAGE_OFFSET - it must be within 32MB of the kernel text. */ #ifndef CONFIG_THUMB2_KERNEL -#define MODULES_VADDR (PAGE_OFFSET - 16*1024*1024) +#define MODULES_VADDR (PAGE_OFFSET - 16*1024*1024) #else /* smaller range for Thumb-2 symbols relocation (2^24)*/ -#define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024) -#endif - -#if TASK_SIZE > MODULES_VADDR -#error Top of user space clashes with start of module space +#define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024) #endif /* @@ -68,6 +71,22 @@ #define MODULES_END (PAGE_OFFSET) #endif +#else +/* Move module space into the hole reserved for MSP/PFE so we can have a bigger DMA zone */ +#define MODULES_END ((COMCERTO_DDR_SHARED_BASE + COMCERTO_DDR_SHARED_SIZE - PLAT_PHYS_OFFSET + PAGE_OFFSET) & PMD_MASK) // convert SHARED_END to virt and align on lower PMD boundary + +#ifndef CONFIG_THUMB2_KERNEL +#define MODULES_VADDR (MODULES_END - 16*1024*1024) +#else +#define MODULES_VADDR (MODULES_END - 10*1024*1024) // Relocations will be guaranteed to work as long as kernel size is less than 6MB +#endif + +#endif + +#if TASK_SIZE > MODULES_VADDR +#error Top of user space clashes with start of module space +#endif + /* * The XIP kernel gets mapped at the bottom of the module vm area. * Since we use sections to map it, this macro replaces the physical address diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h index ca94653..0cb69e04 100644 --- a/arch/arm/include/asm/page.h +++ b/arch/arm/include/asm/page.h @@ -11,7 +11,11 @@ #define _ASMARM_PAGE_H /* PAGE_SHIFT determines the page size */ +#if !defined(CONFIG_COMCERTO_64K_PAGES) #define PAGE_SHIFT 12 +#else +#define PAGE_SHIFT 16 +#endif #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) diff --git a/arch/arm/include/asm/param.h b/arch/arm/include/asm/param.h index 8b24bf94..88367aa 100644 --- a/arch/arm/include/asm/param.h +++ b/arch/arm/include/asm/param.h @@ -18,7 +18,11 @@ # define HZ 100 #endif +#if !defined(CONFIG_COMCERTO_64K_PAGES) #define EXEC_PAGESIZE 4096 +#else +#define EXEC_PAGESIZE 65536 +#endif #ifndef NOGROUP #define NOGROUP (-1) diff --git a/arch/arm/include/asm/pgalloc.h b/arch/arm/include/asm/pgalloc.h index 3e08fd3..1a9413a 100644 --- a/arch/arm/include/asm/pgalloc.h +++ b/arch/arm/include/asm/pgalloc.h @@ -108,8 +108,17 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, pmdval_t prot) { pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot; +#if !defined(CONFIG_COMCERTO_64K_PAGES) pmdp[0] = __pmd(pmdval); pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); +#else + int i, off = 0; + for (i = 0; i < LINKED_PMDS; i++) { + pmdp[i] = __pmd(pmdval + off); + off += 1024; // Each PMD points to a 1kB 2nd-level table + } + +#endif flush_pmd_entry(pmdp); } diff --git a/arch/arm/include/asm/pgtable-2level-hwdef.h b/arch/arm/include/asm/pgtable-2level-hwdef.h index 5cfba15..01bbb91 100644 --- a/arch/arm/include/asm/pgtable-2level-hwdef.h +++ b/arch/arm/include/asm/pgtable-2level-hwdef.h @@ -65,7 +65,13 @@ /* * - extended small page/tiny page */ +#if !defined(CONFIG_COMCERTO_64K_PAGES) #define PTE_EXT_XN (_AT(pteval_t, 1) << 0) /* v6 */ +#define PTE_EXT_TEX(x) (_AT(pteval_t, (x)) << 6) /* v5 */ +#else +#define PTE_EXT_XN (_AT(pteval_t, 1) << 15) /* v6 */ +#define PTE_EXT_TEX(x) (_AT(pteval_t, (x)) << 12) /* v5 */ +#endif #define PTE_EXT_AP_MASK (_AT(pteval_t, 3) << 4) #define PTE_EXT_AP0 (_AT(pteval_t, 1) << 4) #define PTE_EXT_AP1 (_AT(pteval_t, 2) << 4) @@ -73,7 +79,6 @@ #define PTE_EXT_AP_UNO_SRW (PTE_EXT_AP0) #define PTE_EXT_AP_URO_SRW (PTE_EXT_AP1) #define PTE_EXT_AP_URW_SRW (PTE_EXT_AP1|PTE_EXT_AP0) -#define PTE_EXT_TEX(x) (_AT(pteval_t, (x)) << 6) /* v5 */ #define PTE_EXT_APX (_AT(pteval_t, 1) << 9) /* v6 */ #define PTE_EXT_COHERENT (_AT(pteval_t, 1) << 9) /* XScale3 */ #define PTE_EXT_SHARED (_AT(pteval_t, 1) << 10) /* v6 */ diff --git a/arch/arm/include/asm/pgtable-2level-types.h b/arch/arm/include/asm/pgtable-2level-types.h index 66cb5b0..ad48bf6 100644 --- a/arch/arm/include/asm/pgtable-2level-types.h +++ b/arch/arm/include/asm/pgtable-2level-types.h @@ -24,12 +24,17 @@ typedef u32 pteval_t; typedef u32 pmdval_t; +#if !defined(CONFIG_COMCERTO_64K_PAGES) #undef STRICT_MM_TYPECHECKS +#else +#define STRICT_MM_TYPECHECKS 1 +#endif #ifdef STRICT_MM_TYPECHECKS /* * These are used to make use of C type-checking.. */ +#if !defined(CONFIG_COMCERTO_64K_PAGES) typedef struct { pteval_t pte; } pte_t; typedef struct { pmdval_t pmd; } pmd_t; typedef struct { pmdval_t pgd[2]; } pgd_t; @@ -45,6 +50,23 @@ typedef struct { pteval_t pgprot; } pgprot_t; #define __pgprot(x) ((pgprot_t) { (x) } ) #else +#include +typedef struct { pteval_t pte[16]; } pte_t; +typedef struct { pmdval_t pmd; } pmd_t; +typedef struct { pmdval_t pgd[LINKED_PMDS]; } pgd_t; +typedef struct { pteval_t pgprot; } pgprot_t; + +#define pte_val(x) ((x).pte[0]) +#define pmd_val(x) ((x).pmd) +#define pgd_val(x) ((x).pgd[0]) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { {(x)} } ) +#define __pmd(x) ((pmd_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) +#endif + +#else /* * .. while these make it easier on the compiler */ diff --git a/arch/arm/include/asm/pgtable-2level.h b/arch/arm/include/asm/pgtable-2level.h index 1cb80c4..da432c2 100644 --- a/arch/arm/include/asm/pgtable-2level.h +++ b/arch/arm/include/asm/pgtable-2level.h @@ -28,6 +28,15 @@ * which contain the state information Linux needs. We, therefore, end up * with 512 entries in the "PTE" level. * + * 64k pages support (Mindspeed COMCERTO): + * We cheat even more and tell Linux that we have 256 entries in the first + * level, each of which is 64 bytes (16 hardware pointers). The 2nd level + * contains 16 hardware PTE tables, or 4096 hardware entries. However, + * since 64kB pages are done by duplicating 4kB entries, there will only by + * 256 entries in the Linux "PTE" level (and the PTE entry will be larger). + * All defines are now also derived from the LINKED_PMDS_SHIFT macro, which + * determines how many PMDs point into a single 2nd-level table. + * * This leads to the page tables having the following layout: * * pgd pte @@ -68,6 +77,7 @@ * until either the TLB entry is evicted under pressure, or a context * switch which changes the user space mapping occurs. */ +#if !defined(CONFIG_COMCERTO_64K_PAGES) #define PTRS_PER_PTE 512 #define PTRS_PER_PMD 1 #define PTRS_PER_PGD 2048 @@ -82,6 +92,21 @@ */ #define PMD_SHIFT 21 #define PGDIR_SHIFT 21 +#else +#define LINKED_PMDS_SHIFT 4 +#define LINKED_PMDS (1 << LINKED_PMDS_SHIFT) /* number of PMDs pointing to the same 2nd-level page */ +#define PTRS_PER_PGD (4096 / LINKED_PMDS) /* one pgdir table contains 4096 entries */ +#define PGDIR_SHIFT (20 + LINKED_PMDS_SHIFT) /* one pgdir entry can map 1MB (2^20) */ +#define PMD_SHIFT (PGDIR_SHIFT) +#define PTET_SIZE_SHIFT 6 /* a HW PTE entry is 16*4bytes */ +#define PTE_HWTABLE_PTRS (1 << (10 + LINKED_PMDS_SHIFT - PTET_SIZE_SHIFT)) /* one HW PTE table is 1kB (2^10) */ + +#define PTRS_PER_PTE (PTE_HWTABLE_PTRS) +#define PTRS_PER_PMD 1 + +#define PTE_HWTABLE_OFF 32768 //(PTRS_PER_PTE * sizeof(pte_t)) +#define PTE_HWTABLE_SIZE (1 << (10 + LINKED_PMDS_SHIFT)) +#endif #define PMD_SIZE (1UL << PMD_SHIFT) #define PMD_MASK (~(PMD_SIZE-1)) diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index 0b04d3c..54ce5a6 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -252,6 +252,14 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd) #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte_val(pte),ext) #define pte_clear(mm,addr,ptep) do {__sync_outer_cache(ptep, __pte(0)); set_pte_ext(ptep, __pte(0), 0); } while (0) +#define pte_none(pte) (!pte_val(pte)) +#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT) +#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY)) +#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY) +#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG) +#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN)) +#define pte_special(pte) (0) + #if !defined(CONFIG_L2X0_INSTRUCTION_ONLY) static inline void __sync_outer_cache(pte_t *ptep, pte_t pteval) { @@ -260,6 +268,10 @@ static inline void __sync_outer_cache(pte_t *ptep, pte_t pteval) extern void __sync_outer_cache(pte_t *ptep, pte_t pteval); #endif +#define pte_present_user(pte) \ + ((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \ + (L_PTE_PRESENT | L_PTE_USER)) + #if __LINUX_ARM_ARCH__ < 6 static inline void __sync_icache_dcache(pte_t pteval) { @@ -272,6 +284,7 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pteval) { unsigned long ext = 0; + __sync_outer_cache(ptep, pteval); if (addr < TASK_SIZE && pte_present_user(pteval)) { __sync_icache_dcache(pteval); diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h index 9e92cb20..b9c911c 100644 --- a/arch/arm/include/asm/proc-fns.h +++ b/arch/arm/include/asm/proc-fns.h @@ -65,7 +65,7 @@ extern struct processor { * Set a possibly extended PTE. Non-extended PTEs should * ignore 'ext'. */ - void (*set_pte_ext)(pte_t *ptep, pte_t pte, unsigned int ext); + void (*set_pte_ext)(pte_t *ptep, pteval_t pte, unsigned int ext); /* Suspend/resume */ unsigned int suspend_size; @@ -79,7 +79,7 @@ extern void cpu_proc_fin(void); extern int cpu_do_idle(void); extern void cpu_dcache_clean_area(void *, int); extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm); -extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte, unsigned int ext); +extern void cpu_set_pte_ext(pte_t *ptep, pteval_t pte, unsigned int ext); extern void cpu_reset(unsigned long addr) __attribute__((noreturn)); /* These three are private to arch/arm/kernel/suspend.c */ diff --git a/arch/arm/include/asm/shmparam.h b/arch/arm/include/asm/shmparam.h index a5223b3..73cdb5a 100644 --- a/arch/arm/include/asm/shmparam.h +++ b/arch/arm/include/asm/shmparam.h @@ -6,7 +6,12 @@ * or page size, whichever is greater since the cache aliases * every size/ways bytes. */ +#if !defined(CONFIG_COMCERTO_64K_PAGES) #define SHMLBA (4 * PAGE_SIZE) /* attach addr a multiple of this */ +#else +#define SHMLBA (PAGE_SIZE) /* attach addr a multiple of this */ +#endif + /* * Enforce SHMLBA in shmat diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h index ef9ffba9..d289c78 100644 --- a/arch/arm/include/asm/smp_twd.h +++ b/arch/arm/include/asm/smp_twd.h @@ -24,5 +24,8 @@ extern void __iomem *twd_base; void twd_timer_setup(struct clock_event_device *); void twd_timer_stop(struct clock_event_device *); +#ifdef CONFIG_LOCAL_TIMERS +int twd_timer_ack(void); +#endif #endif diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 7b5cc8d..7d6a8ac 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -15,8 +15,13 @@ #include #include +#if !defined(CONFIG_COMCERTO_64K_PAGES) #define THREAD_SIZE_ORDER 1 #define THREAD_SIZE 8192 +#else +#define THREAD_SIZE_ORDER 0 +#define THREAD_SIZE 65536 +#endif #define THREAD_START_SP (THREAD_SIZE - 8) #ifndef __ASSEMBLY__ diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index 02b2f82..49ff231 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -471,6 +471,7 @@ static inline void local_flush_tlb_kernel_page(unsigned long kaddr) * these operations. This is typically used when we are removing * PMD entries. */ +#if !defined(CONFIG_COMCERTO_64K_PAGES) static inline void flush_pmd_entry(void *pmd) { const unsigned int __tlb_flag = __cpu_tlb_flags; @@ -499,6 +500,39 @@ static inline void clean_pmd_entry(void *pmd) asm("mcr p15, 1, %0, c15, c9, 1 @ L2 flush_pmd" : : "r" (pmd) : "cc"); } +#else +static inline void flush_pmd_entry(void *pmd) +{ + const unsigned int __tlb_flag = __cpu_tlb_flags; + char *p = (char *)pmd; + + if (tlb_flag(TLB_DCLEAN)) { + while (p < ((char *)pmd + (LINKED_PMDS * sizeof(u32)))) { // A PMD contains LINKED_PMDS pointers to the 2nd-level table + asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd" + : : "r" (p) : "cc"); + p += 32; //Next cache line + } + } + + if (tlb_flag(TLB_WB)) + dsb(); +} + +static inline void clean_pmd_entry(void *pmd) +{ + const unsigned int __tlb_flag = __cpu_tlb_flags; + char *p = (char *)pmd; + + if (tlb_flag(TLB_DCLEAN)) { + while (p < ((char *)pmd + (LINKED_PMDS * sizeof(u32)))) { // A PMD contains LINKED_PMDS pointers to the 2nd-level table + asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd" + : : "r" (p) : "cc"); + p += 32; //Next cache line + } + } +} + +#endif #undef tlb_flag #undef always_tlb_flags diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index b2a27b6..aacae19 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -14,7 +14,7 @@ #include #include "entry-header.S" - +#include .align 5 /* @@ -583,6 +583,7 @@ ENDPROC(sys_fstatfs64_wrapper) */ sys_mmap2: #if PAGE_SHIFT > 12 +#define PGOFF_MASK ((1 << (PAGE_SHIFT - 12)) - 1) tst r5, #PGOFF_MASK moveq r5, r5, lsr #PAGE_SHIFT - 12 streq r5, [sp, #4] diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 9d95a46..49f7648 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -103,11 +103,17 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm +#if !defined(CONFIG_COMCERTO_64K_PAGES) .macro get_thread_info, rd mov \rd, sp, lsr #13 mov \rd, \rd, lsl #13 .endm - +#else + .macro get_thread_info, rd + mov \rd, sp, lsr #16 + mov \rd, \rd, lsl #16 + .endm +#endif @ @ 32-bit wide "mov pc, reg" @ @@ -149,11 +155,18 @@ movs pc, lr @ return & move spsr_svc into cpsr .endm +#if !defined(CONFIG_COMCERTO_64K_PAGES) .macro get_thread_info, rd mov \rd, sp lsr \rd, \rd, #13 mov \rd, \rd, lsl #13 .endm +#else + .macro get_thread_info, rd + mov \rd, sp, lsr #16 + mov \rd, \rd, lsl #16 + .endm +#endif @ @ 32-bit wide "mov pc, reg" diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 3606e85..dc4d0eaa 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -44,9 +44,17 @@ .globl swapper_pg_dir .equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE - +/* +* Mindspeed: +* Need to break the function in case text offset is too big +* this is the case when using zone_dma +* There is probably a more elegant way to to that +* original code: +* add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE +*/ .macro pgtbl, rd, phys - add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE + ldr \rd, =TEXT_OFFSET - PG_DIR_SIZE + add \rd, \phys, \rd .endm #ifdef CONFIG_XIP_KERNEL diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 1e9be5d..4057690 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -81,6 +81,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, return -ENOEXEC; } + if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) && + ELF_ST_BIND(sym->st_info) == STB_WEAK) + continue; + loc = dstsec->sh_addr + rel->r_offset; switch (ELF32_R_TYPE(rel->r_info)) { diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index bfa0eeb..8944805 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -351,7 +351,10 @@ asmlinkage void __cpuinit secondary_start_kernel(void) * now. */ local_irq_enable(); + +#ifndef CONFIG_COMCERTO_MSP local_fiq_enable(); +#endif /* !CONFIG_COMCERTO_MSP */ /* * OK, it's off to the idle thread for us diff --git a/arch/arm/kernel/smp_scu.c b/arch/arm/kernel/smp_scu.c index 8f5dd79..ff84772 100644 --- a/arch/arm/kernel/smp_scu.c +++ b/arch/arm/kernel/smp_scu.c @@ -52,6 +52,10 @@ void scu_enable(void __iomem *scu_base) if (scu_ctrl & 1) return; +#ifdef CONFIG_SCU_SPECULATIVE_LINE_FILLS + scu_ctrl |= (1 << 3); +#endif + scu_ctrl |= 1; __raw_writel(scu_ctrl, scu_base + SCU_CTRL); diff --git a/arch/arm/lib/copy_page.S b/arch/arm/lib/copy_page.S index 6ee2f67..eb0f586 100644 --- a/arch/arm/lib/copy_page.S +++ b/arch/arm/lib/copy_page.S @@ -28,7 +28,7 @@ ENTRY(copy_page) stmfd sp!, {r4, lr} @ 2 PLD( pld [r1, #0] ) PLD( pld [r1, #L1_CACHE_BYTES] ) - mov r2, #COPY_COUNT @ 1 + ldr r2, =COPY_COUNT @ 1 ldmia r1!, {r3, r4, ip, lr} @ 4+1 1: PLD( pld [r1, #2 * L1_CACHE_BYTES]) PLD( pld [r1, #3 * L1_CACHE_BYTES]) diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 4e1ef6e..9aa8350 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -752,6 +752,13 @@ config CPU_CACHE_ROUND_ROBIN Say Y here to use the predictable round-robin cache replacement policy. Unless you specifically require this or are unsure, say N. +config CPU_SPECULATIVE_ACCESS_DISABLED + bool "Disable speculative accesses" + depends on CPU_V7 && ARCH_COMCERTO + help + Disable speculative accesses for the processor. This reduces overhead of cache maintenance for + DMA mapped memory zones but may reduce overall system memory performance. If unsure, say N. + config CPU_BPREDICT_DISABLE bool "Disable branch prediction" depends on CPU_ARM1020 || CPU_V6 || CPU_V6K || CPU_MOHAWK || CPU_XSC3 || CPU_V7 || CPU_FA526 @@ -821,7 +828,7 @@ config CACHE_L2X0 REALVIEW_EB_A9MP || ARCH_IMX_V6_V7 || MACH_REALVIEW_PBX || \ ARCH_NOMADIK || ARCH_OMAP4 || ARCH_EXYNOS4 || ARCH_TEGRA || \ ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE || \ - ARCH_PRIMA2 || ARCH_ZYNQ || ARCH_CNS3XXX || ARCH_HIGHBANK + ARCH_PRIMA2 || ARCH_ZYNQ || ARCH_CNS3XXX || ARCH_HIGHBANK || ARCH_COMCERTO default y select OUTER_CACHE select OUTER_CACHE_SYNC @@ -836,6 +843,51 @@ config CACHE_PL310 This option enables optimisations for the PL310 cache controller. +config L2X0_INSTRUCTION_ONLY + bool "Use L2 cache for instructions only" + depends on CACHE_PL310 + default n + +config PL310_EXCLUSIVE_CACHE + bool "Exclusive cache" + depends on CACHE_PL310 + default n + +config PL310_EARLY_WRITE_RESPONSE + bool "Early write response" + depends on CACHE_PL310 + default n + +config PL310_FULL_LINE_OF_ZERO + bool "Full line of zero" + depends on CACHE_PL310 + default n + +config PL310_STORE_BUFFER_DEVICE_LIMITATION + bool "Store buffer device limitation" + depends on CACHE_PL310 + default n + +config PL310_INSTRUCTION_PREFETCH + bool "Instruction prefetch" + depends on CACHE_PL310 + default n + +config PL310_DATA_PREFETCH + bool "Data prefetch" + depends on CACHE_PL310 + default n + +config PL310_DOUBLE_LINE_FILL + bool "Double line fill" + depends on CACHE_PL310 + default n + +config PL310_INCR_DOUBLE_LINE_FILL + bool "Incremental double line fill" + depends on PL310_DOUBLE_LINE_FILL + default n + config CACHE_TAUROS2 bool "Enable the Tauros2 L2 cache controller" depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4) @@ -866,7 +918,7 @@ config ARM_L1_CACHE_SHIFT config ARM_DMA_MEM_BUFFERABLE bool "Use non-cacheable memory for DMA" if (CPU_V6 || CPU_V6K) && !CPU_V7 depends on !(MACH_REALVIEW_PB1176 || REALVIEW_EB_ARM11MP || \ - MACH_REALVIEW_PB11MP) + MACH_REALVIEW_PB11MP || COMCERTO_DDR_ECC_SUPPORT) default y if CPU_V6 || CPU_V6K || CPU_V7 help Historically, the kernel has used strongly ordered mappings to diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index db7bcc0..3e504ec 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -299,8 +299,10 @@ static void l2x0_unlock(__u32 cache_id) lockregs = 1; for (i = 0; i < lockregs; i++) { +#ifndef CONFIG_L2X0_INSTRUCTION_ONLY writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE + i * L2X0_LOCKDOWN_STRIDE); +#endif writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE + i * L2X0_LOCKDOWN_STRIDE); } @@ -311,6 +313,7 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) __u32 aux; __u32 cache_id; __u32 way_size = 0; + __u32 prefetch = 0; int ways; const char *type; @@ -330,11 +333,23 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) else ways = 8; type = "L310"; + #ifdef CONFIG_PL310_ERRATA_753970 /* Unmapped register. */ sync_reg_offset = L2X0_DUMMY_REG; #endif outer_cache.set_debug = pl310_set_debug; + + prefetch = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL); + +#ifdef CONFIG_PL310_DOUBLE_LINE_FILL + prefetch |= (1 << 30) | (1 << 24); +#ifdef CONFIG_PL310_INCR_DOUBLE_LINE_FILL + prefetch |= (1 << 23); +#endif +#endif + writel_relaxed(prefetch, l2x0_base + L2X0_PREFETCH_CTRL); + break; case L2X0_CACHE_ID_PART_L210: ways = (aux >> 13) & 0xf; @@ -385,8 +400,8 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) outer_cache.disable = l2x0_disable; printk(KERN_INFO "%s cache controller enabled\n", type); - printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n", - ways, cache_id, aux, l2x0_size); + printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, PREFETCH_CTRL 0x%08x, Cache size: %d B\n", + ways, cache_id, aux, prefetch, l2x0_size); } #ifdef CONFIG_OF diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index a655d3d..0c9258e 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -211,9 +211,16 @@ ENTRY(v7_coherent_user_range) * isn't mapped, just try the next page. */ 9001: +#if !defined(CONFIG_COMCERTO_64K_PAGES) mov r12, r12, lsr #12 mov r12, r12, lsl #12 add r12, r12, #4096 +#else + mov r12, r12, lsr #16 + mov r12, r12, lsl #16 + add r12, r12, #65536 + +#endif b 3b UNWIND(.fnend ) ENDPROC(v7_coherent_kern_range) diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index 3d9a155..8205008 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c @@ -20,9 +20,16 @@ #include "mm.h" +#if !defined(CONFIG_COMCERTO_64K_PAGES) #if SHMLBA > 16384 #error FIX ME #endif +#else +#if SHMLBA > PAGE_SIZE +#error FIX ME +#endif +#endif + #define from_address (0xffff8000) #define to_address (0xffffc000) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index e1dd92c..1bbdf55 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -29,6 +29,10 @@ #include "mm.h" +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB +extern unsigned long arm_dma_zone_size; +#endif + static u64 get_coherent_dma_mask(struct device *dev) { u64 mask = (u64)arm_dma_limit; @@ -168,7 +172,7 @@ static int __init consistent_init(void) pte_t *pte; int i = 0; unsigned long base = consistent_base; - unsigned long num_ptes = (CONSISTENT_END - base) >> PMD_SHIFT; + unsigned long num_ptes = (CONSISTENT_END - base + PMD_SIZE -1) >> PMD_SHIFT; consistent_pte = kmalloc(num_ptes * sizeof(pte_t), GFP_KERNEL); if (!consistent_pte) { @@ -195,8 +199,9 @@ static int __init consistent_init(void) ret = -ENOMEM; break; } +#if !defined(CONFIG_COMCERTO_64K_PAGES) WARN_ON(!pmd_none(*pmd)); - +#endif pte = pte_alloc_kernel(pmd, base); if (!pte) { printk(KERN_ERR "%s: no pte tables\n", __func__); @@ -205,8 +210,8 @@ static int __init consistent_init(void) } consistent_pte[i++] = pte; - base += PMD_SIZE; - } while (base < CONSISTENT_END); + base = (base + PMD_SIZE) & PMD_MASK; + } while ((base-1) < (CONSISTENT_END - 1)); return ret; } @@ -455,6 +460,19 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr } EXPORT_SYMBOL(dma_free_coherent); +static inline void __dmac_unmap_area(const void *kaddr, size_t size, + int dir) +{ +#if !defined(CONFIG_CPU_SPECULATIVE_ACCESS_DISABLED) + dmac_unmap_area(kaddr, size, dir); +#else + size_t size_inv = min_t(size_t, 32, size); + + dmac_unmap_area(kaddr, size_inv, dir); + dmac_unmap_area(kaddr + size - size_inv, size_inv, dir); +#endif +} + /* * Make an area consistent for devices. * Note: Drivers should NOT use this function directly, as it will break @@ -464,35 +482,55 @@ EXPORT_SYMBOL(dma_free_coherent); void ___dma_single_cpu_to_dev(const void *kaddr, size_t size, enum dma_data_direction dir) { - unsigned long paddr; + unsigned long paddr = __pa(kaddr); BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB + if ((paddr + size) <= arm_dma_zone_size) + return; +#endif + dmac_map_area(kaddr, size, dir); - paddr = __pa(kaddr); +#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY) if (dir == DMA_FROM_DEVICE) { outer_inv_range(paddr, paddr + size); } else { outer_clean_range(paddr, paddr + size); } /* FIXME: non-speculating: flush on bidirectional mappings? */ +#endif } EXPORT_SYMBOL(___dma_single_cpu_to_dev); void ___dma_single_dev_to_cpu(const void *kaddr, size_t size, enum dma_data_direction dir) { + unsigned long paddr = __pa(kaddr); + BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); - /* FIXME: non-speculating: not required */ +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB + if ((paddr + size) <= arm_dma_zone_size) + return; +#endif + +#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY) /* don't bother invalidating if DMA to device */ if (dir != DMA_TO_DEVICE) { - unsigned long paddr = __pa(kaddr); +#if !defined(CONFIG_CPU_SPECULATIVE_ACCESS_DISABLED) outer_inv_range(paddr, paddr + size); +#else + size_t size_inv = min_t(size_t, 32, size); + + outer_inv_range(paddr, paddr + size_inv); + outer_inv_range(paddr + size - size_inv, paddr + size); +#endif } +#endif - dmac_unmap_area(kaddr, size, dir); + __dmac_unmap_area(kaddr, size, dir); } EXPORT_SYMBOL(___dma_single_dev_to_cpu); @@ -545,16 +583,22 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset, void ___dma_page_cpu_to_dev(struct page *page, unsigned long off, size_t size, enum dma_data_direction dir) { - unsigned long paddr; + unsigned long paddr = page_to_phys(page) + off; + +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB + if ((paddr + size) <= arm_dma_zone_size) + return; +#endif dma_cache_maint_page(page, off, size, dir, dmac_map_area); - paddr = page_to_phys(page) + off; +#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY) if (dir == DMA_FROM_DEVICE) { outer_inv_range(paddr, paddr + size); } else { outer_clean_range(paddr, paddr + size); } +#endif /* FIXME: non-speculating: flush on bidirectional mappings? */ } EXPORT_SYMBOL(___dma_page_cpu_to_dev); @@ -564,12 +608,25 @@ void ___dma_page_dev_to_cpu(struct page *page, unsigned long off, { unsigned long paddr = page_to_phys(page) + off; - /* FIXME: non-speculating: not required */ +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB + if ((paddr + size) <= arm_dma_zone_size) + return; +#endif + +#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY) /* don't bother invalidating if DMA to device */ - if (dir != DMA_TO_DEVICE) + if (dir != DMA_TO_DEVICE) { +#if !defined(CONFIG_CPU_SPECULATIVE_ACCESS_DISABLED) outer_inv_range(paddr, paddr + size); +#else + size_t size_inv = min_t(size_t, 32, size); - dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); + outer_inv_range(paddr, paddr + size_inv); + outer_inv_range(paddr + size - size_inv, paddr + size); +#endif + } +#endif + dma_cache_maint_page(page, off, size, dir, __dmac_unmap_area); /* * Mark the D-cache clean for this page to avoid extra flushing. diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index fe61cab..4c0cf13 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -229,6 +229,26 @@ static void __flush_dcache_aliases(struct address_space *mapping, struct page *p flush_dcache_mmap_unlock(mapping); } +#if defined(CONFIG_L2X0_INSTRUCTION_ONLY) + +void __sync_outer_cache(pte_t *ptep, pte_t pteval) +{ + if (pte_present(*ptep) && pte_exec(*ptep) && (!pte_present(pteval) || !pte_exec(pteval))) { + unsigned long phys = __pfn_to_phys(pte_pfn(*ptep)); + +// printk(KERN_INFO "outer flush range: %x %x %lx-%lx\n", pte_val(*ptep), pteval, phys, phys + PAGE_SIZE); + outer_flush_range(phys, phys + PAGE_SIZE); + } +} + +static void sync_outer_cache(struct page *page) +{ + unsigned long phys = page_to_phys(page); + + outer_flush_range(phys, phys + PAGE_SIZE); +} +#endif + #if __LINUX_ARM_ARCH__ >= 6 void __sync_icache_dcache(pte_t pteval) { @@ -298,6 +318,10 @@ void flush_dcache_page(struct page *page) __flush_dcache_aliases(mapping, page); else if (mapping) __flush_icache_all(); + +#if defined(CONFIG_L2X0_INSTRUCTION_ONLY) + sync_outer_cache(page); +#endif set_bit(PG_dcache_clean, &page->flags); } } diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index cc3f35d..b6e9589 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -291,7 +291,11 @@ static void __init arm_bootmem_free(unsigned long min, unsigned long max_low, if (arm_dma_zone_size) { arm_adjust_dma_zone(zone_size, zhole_size, arm_dma_zone_size >> PAGE_SHIFT); +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB + arm_dma_limit = 0xffffffff; +#else arm_dma_limit = PHYS_OFFSET + arm_dma_zone_size - 1; +#endif } else arm_dma_limit = 0xffffffff; #endif @@ -426,9 +430,15 @@ static inline int free_area(unsigned long pfn, unsigned long end, char *s) for (; pfn < end; pfn++) { struct page *page = pfn_to_page(pfn); +#if defined(CONFIG_L2X0_INSTRUCTION_ONLY) + unsigned long phys = page_to_phys(page); +#endif ClearPageReserved(page); init_page_count(page); __free_page(page); +#if defined(CONFIG_L2X0_INSTRUCTION_ONLY) + outer_flush_range(phys, phys + PAGE_SIZE); +#endif pages++; } diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 082fa18..b10c27b 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -281,6 +281,18 @@ static struct mem_type mem_types[] = { PMD_SECT_UNCACHED | PMD_SECT_XN, .domain = DOMAIN_KERNEL, }, + [MT_MSP] = { + .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_CACHED | L_PTE_XN, + .prot_l1 = PMD_TYPE_TABLE, + .prot_sect = PROT_SECT_DEVICE | PMD_SECT_WB, + .domain = DOMAIN_IO, + }, + [MT_MSP_NCNB] = { + .prot_pte = PROT_PTE_DEVICE | L_PTE_XN, + .prot_l1 = PMD_TYPE_TABLE, + .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S, + .domain = DOMAIN_IO, + }, }; const struct mem_type *get_mem_type(unsigned int type) @@ -580,8 +592,7 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr, if (((addr | end | phys) & ~SECTION_MASK) == 0) { pmd_t *p = pmd; - if (addr & SECTION_SIZE) - pmd++; + pmd += (addr & (~PGDIR_MASK)) >> SECTION_SHIFT; do { *pmd = __pmd(phys | type->prot_sect); @@ -1021,7 +1032,10 @@ static void __init kmap_init(void) static void __init map_lowmem(void) { struct memblock_region *reg; - +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB + extern unsigned long arm_dma_zone_size; + phys_addr_t length_ncnb = arm_dma_zone_size, length_ncnb_now = 0; +#endif /* Map all the lowmem memory banks. */ for_each_memblock(memory, reg) { phys_addr_t start = reg->base; @@ -1032,7 +1046,23 @@ static void __init map_lowmem(void) end = lowmem_limit; if (start >= end) break; +#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB + if (length_ncnb > 0) + { + length_ncnb_now = min(length_ncnb, end - start); + map.pfn = __phys_to_pfn(start); + map.virtual = __phys_to_virt(start); + map.length = length_ncnb_now; + map.type = MT_MSP_NCNB; + printk("Comcerto: zone_dma mapping size=%lx type=%lx\n", (unsigned long) map.length, (unsigned long) map.type); + create_mapping(&map); + start += length_ncnb_now; + length_ncnb -= length_ncnb_now; + if (start == end) + continue; + } +#endif map.pfn = __phys_to_pfn(start); map.virtual = __phys_to_virt(start); map.length = end - start; diff --git a/arch/arm/mm/pgd.c b/arch/arm/mm/pgd.c index b2027c1..27b9016 100644 --- a/arch/arm/mm/pgd.c +++ b/arch/arm/mm/pgd.c @@ -27,7 +27,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm) pmd_t *new_pmd, *init_pmd; pte_t *new_pte, *init_pte; - new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2); + new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, get_order(16384)); if (!new_pgd) goto no_pgd; @@ -74,7 +74,7 @@ no_pte: no_pmd: pud_free(mm, new_pud); no_pud: - free_pages((unsigned long)new_pgd, 2); + free_pages((unsigned long)new_pgd, get_order(16384)); no_pgd: return NULL; } @@ -111,5 +111,5 @@ no_pud: pgd_clear(pgd); pud_free(mm, pud); no_pgd: - free_pages((unsigned long) pgd_base, 2); + free_pages((unsigned long) pgd_base, get_order(16384)); } diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 43c6981..555746c 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -46,7 +46,7 @@ ENTRY(cpu_v7_proc_fin) mrc p15, 0, r0, c1, c0, 0 @ ctrl register bic r0, r0, #0x1000 @ ...i............ bic r0, r0, #0x0006 @ .............ca. - mcr p15, 0, r0, c1, c0, 0 @ disable caches +# mcr p15, 0, r0, c1, c0, 0 @ disable caches mov pc, lr ENDPROC(cpu_v7_proc_fin) @@ -149,7 +149,11 @@ ENTRY(cpu_v7_set_pte_ext) bic r3, r1, #0x000003f0 bic r3, r3, #PTE_TYPE_MASK orr r3, r3, r2 +#if !defined(CONFIG_COMCERTO_64K_PAGES) orr r3, r3, #PTE_EXT_AP0 | 2 +#else + orr r3, r3, #PTE_EXT_AP0 | 1 +#endif tst r1, #1 << 4 orrne r3, r3, #PTE_EXT_TEX(1) @@ -170,10 +174,26 @@ ENTRY(cpu_v7_set_pte_ext) tstne r1, #L_PTE_NONE moveq r3, #0 +#if !defined(CONFIG_COMCERTO_64K_PAGES) ARM( str r3, [r0, #2048]! ) THUMB( add r0, r0, #2048 ) THUMB( str r3, [r0] ) mcr p15, 0, r0, c7, c10, 1 @ flush_pte + #else + ldr r1, =32768 @ PTE_HWTABLE_OFF + add r1, r1, r0 + add r2, r1, #64 @ 16*4 bytes + 1: + sub r2, r2, #4 + str r3, [r2]! + cmp r1, r2 + blo 1b + mcr p15, 0, r1, c7, c10, 1 @ flush_pte + add r1, r1, #32 @ 1 cache line, since HW PTE takes 64 bytes + mcr p15, 0, r1, c7, c10, 1 @ flush_pte + #endif + + #endif mov pc, lr ENDPROC(cpu_v7_set_pte_ext) @@ -274,9 +294,16 @@ ENDPROC(cpu_v7_do_resume) * - cache type register is implemented */ __v7_ca5mp_setup: + mov r10, #(1 << 0) @ TLB ops broadcasting + b 1f + __v7_ca9mp_setup: mov r10, #(1 << 0) @ TLB ops broadcasting +#if !defined(CONFIG_CPU_SPECULATIVE_ACCESS_DISABLED) + orr r10, #(3 << 1) @ L1 data prefetch, L2 prefetch hints enable +#endif b 1f + __v7_ca15mp_setup: mov r10, #0 1: diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index ccbe16f..3a670a5 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -212,6 +212,7 @@ ecbat91 MACH_ECBAT91 ECBAT91 1072 onearm MACH_ONEARM ONEARM 1075 smdk2443 MACH_SMDK2443 SMDK2443 1084 fsg MACH_FSG FSG 1091 +comcerto MACH_COMCERTO COMCERTO 1094 at91sam9260ek MACH_AT91SAM9260EK AT91SAM9260EK 1099 glantank MACH_GLANTANK GLANTANK 1100 n2100 MACH_N2100 N2100 1101 diff --git a/arch/microblaze/boot/dts/system.dts b/arch/microblaze/boot/dts/system.dts index 7cb6578..3f85df2 120000 --- a/arch/microblaze/boot/dts/system.dts +++ b/arch/microblaze/boot/dts/system.dts @@ -1 +1,367 @@ -../../platform/generic/system.dts \ No newline at end of file +/* + * Device Tree Generator version: 1.1 + * + * (C) Copyright 2007-2008 Xilinx, Inc. + * (C) Copyright 2007-2009 Michal Simek + * + * Michal SIMEK + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * CAUTION: This file is automatically generated by libgen. + * Version: Xilinx EDK 10.1.03 EDK_K_SP3.6 + * + * XPS project directory: Xilinx-ML505-ll_temac-sgdma-MMU-FDT-edk101 + */ + +/dts-v1/; +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "xlnx,microblaze"; + hard-reset-gpios = <&LEDs_8Bit 2 1>; + model = "testing"; + DDR2_SDRAM: memory@90000000 { + device_type = "memory"; + reg = < 0x90000000 0x10000000 >; + } ; + aliases { + ethernet0 = &Hard_Ethernet_MAC; + serial0 = &RS232_Uart_1; + } ; + chosen { + bootargs = "console=ttyUL0,115200 highres=on"; + linux,stdout-path = "/plb@0/serial@84000000"; + } ; + cpus { + #address-cells = <1>; + #cpus = <0x1>; + #size-cells = <0>; + microblaze_0: cpu@0 { + clock-frequency = <125000000>; + compatible = "xlnx,microblaze-7.10.d"; + d-cache-baseaddr = <0x90000000>; + d-cache-highaddr = <0x9fffffff>; + d-cache-line-size = <0x10>; + d-cache-size = <0x2000>; + device_type = "cpu"; + i-cache-baseaddr = <0x90000000>; + i-cache-highaddr = <0x9fffffff>; + i-cache-line-size = <0x10>; + i-cache-size = <0x2000>; + model = "microblaze,7.10.d"; + reg = <0>; + timebase-frequency = <125000000>; + xlnx,addr-tag-bits = <0xf>; + xlnx,allow-dcache-wr = <0x1>; + xlnx,allow-icache-wr = <0x1>; + xlnx,area-optimized = <0x0>; + xlnx,cache-byte-size = <0x2000>; + xlnx,d-lmb = <0x1>; + xlnx,d-opb = <0x0>; + xlnx,d-plb = <0x1>; + xlnx,data-size = <0x20>; + xlnx,dcache-addr-tag = <0xf>; + xlnx,dcache-always-used = <0x1>; + xlnx,dcache-byte-size = <0x2000>; + xlnx,dcache-line-len = <0x4>; + xlnx,dcache-use-fsl = <0x1>; + xlnx,debug-enabled = <0x1>; + xlnx,div-zero-exception = <0x1>; + xlnx,dopb-bus-exception = <0x0>; + xlnx,dynamic-bus-sizing = <0x1>; + xlnx,edge-is-positive = <0x1>; + xlnx,family = "virtex5"; + xlnx,endianness = <0x1>; + xlnx,fpu-exception = <0x1>; + xlnx,fsl-data-size = <0x20>; + xlnx,fsl-exception = <0x0>; + xlnx,fsl-links = <0x0>; + xlnx,i-lmb = <0x1>; + xlnx,i-opb = <0x0>; + xlnx,i-plb = <0x1>; + xlnx,icache-always-used = <0x1>; + xlnx,icache-line-len = <0x4>; + xlnx,icache-use-fsl = <0x1>; + xlnx,ill-opcode-exception = <0x1>; + xlnx,instance = "microblaze_0"; + xlnx,interconnect = <0x1>; + xlnx,interrupt-is-edge = <0x0>; + xlnx,iopb-bus-exception = <0x0>; + xlnx,mmu-dtlb-size = <0x4>; + xlnx,mmu-itlb-size = <0x2>; + xlnx,mmu-tlb-access = <0x3>; + xlnx,mmu-zones = <0x10>; + xlnx,number-of-pc-brk = <0x1>; + xlnx,number-of-rd-addr-brk = <0x0>; + xlnx,number-of-wr-addr-brk = <0x0>; + xlnx,opcode-0x0-illegal = <0x1>; + xlnx,pvr = <0x2>; + xlnx,pvr-user1 = <0x0>; + xlnx,pvr-user2 = <0x0>; + xlnx,reset-msr = <0x0>; + xlnx,sco = <0x0>; + xlnx,unaligned-exceptions = <0x1>; + xlnx,use-barrel = <0x1>; + xlnx,use-dcache = <0x1>; + xlnx,use-div = <0x1>; + xlnx,use-ext-brk = <0x1>; + xlnx,use-ext-nm-brk = <0x1>; + xlnx,use-extended-fsl-instr = <0x0>; + xlnx,use-fpu = <0x2>; + xlnx,use-hw-mul = <0x2>; + xlnx,use-icache = <0x1>; + xlnx,use-interrupt = <0x1>; + xlnx,use-mmu = <0x3>; + xlnx,use-msr-instr = <0x1>; + xlnx,use-pcmp-instr = <0x1>; + } ; + } ; + mb_plb: plb@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "xlnx,plb-v46-1.03.a", "xlnx,plb-v46-1.00.a", "simple-bus"; + ranges ; + FLASH: flash@a0000000 { + bank-width = <2>; + compatible = "xlnx,xps-mch-emc-2.00.a", "cfi-flash"; + reg = < 0xa0000000 0x2000000 >; + xlnx,family = "virtex5"; + xlnx,include-datawidth-matching-0 = <0x1>; + xlnx,include-datawidth-matching-1 = <0x0>; + xlnx,include-datawidth-matching-2 = <0x0>; + xlnx,include-datawidth-matching-3 = <0x0>; + xlnx,include-negedge-ioregs = <0x0>; + xlnx,include-plb-ipif = <0x1>; + xlnx,include-wrbuf = <0x1>; + xlnx,max-mem-width = <0x10>; + xlnx,mch-native-dwidth = <0x20>; + xlnx,mch-plb-clk-period-ps = <0x1f40>; + xlnx,mch-splb-awidth = <0x20>; + xlnx,mch0-accessbuf-depth = <0x10>; + xlnx,mch0-protocol = <0x0>; + xlnx,mch0-rddatabuf-depth = <0x10>; + xlnx,mch1-accessbuf-depth = <0x10>; + xlnx,mch1-protocol = <0x0>; + xlnx,mch1-rddatabuf-depth = <0x10>; + xlnx,mch2-accessbuf-depth = <0x10>; + xlnx,mch2-protocol = <0x0>; + xlnx,mch2-rddatabuf-depth = <0x10>; + xlnx,mch3-accessbuf-depth = <0x10>; + xlnx,mch3-protocol = <0x0>; + xlnx,mch3-rddatabuf-depth = <0x10>; + xlnx,mem0-width = <0x10>; + xlnx,mem1-width = <0x20>; + xlnx,mem2-width = <0x20>; + xlnx,mem3-width = <0x20>; + xlnx,num-banks-mem = <0x1>; + xlnx,num-channels = <0x0>; + xlnx,priority-mode = <0x0>; + xlnx,synch-mem-0 = <0x0>; + xlnx,synch-mem-1 = <0x0>; + xlnx,synch-mem-2 = <0x0>; + xlnx,synch-mem-3 = <0x0>; + xlnx,synch-pipedelay-0 = <0x2>; + xlnx,synch-pipedelay-1 = <0x2>; + xlnx,synch-pipedelay-2 = <0x2>; + xlnx,synch-pipedelay-3 = <0x2>; + xlnx,tavdv-ps-mem-0 = <0x1adb0>; + xlnx,tavdv-ps-mem-1 = <0x3a98>; + xlnx,tavdv-ps-mem-2 = <0x3a98>; + xlnx,tavdv-ps-mem-3 = <0x3a98>; + xlnx,tcedv-ps-mem-0 = <0x1adb0>; + xlnx,tcedv-ps-mem-1 = <0x3a98>; + xlnx,tcedv-ps-mem-2 = <0x3a98>; + xlnx,tcedv-ps-mem-3 = <0x3a98>; + xlnx,thzce-ps-mem-0 = <0x88b8>; + xlnx,thzce-ps-mem-1 = <0x1b58>; + xlnx,thzce-ps-mem-2 = <0x1b58>; + xlnx,thzce-ps-mem-3 = <0x1b58>; + xlnx,thzoe-ps-mem-0 = <0x1b58>; + xlnx,thzoe-ps-mem-1 = <0x1b58>; + xlnx,thzoe-ps-mem-2 = <0x1b58>; + xlnx,thzoe-ps-mem-3 = <0x1b58>; + xlnx,tlzwe-ps-mem-0 = <0x88b8>; + xlnx,tlzwe-ps-mem-1 = <0x0>; + xlnx,tlzwe-ps-mem-2 = <0x0>; + xlnx,tlzwe-ps-mem-3 = <0x0>; + xlnx,twc-ps-mem-0 = <0x2af8>; + xlnx,twc-ps-mem-1 = <0x3a98>; + xlnx,twc-ps-mem-2 = <0x3a98>; + xlnx,twc-ps-mem-3 = <0x3a98>; + xlnx,twp-ps-mem-0 = <0x11170>; + xlnx,twp-ps-mem-1 = <0x2ee0>; + xlnx,twp-ps-mem-2 = <0x2ee0>; + xlnx,twp-ps-mem-3 = <0x2ee0>; + xlnx,xcl0-linesize = <0x4>; + xlnx,xcl0-writexfer = <0x1>; + xlnx,xcl1-linesize = <0x4>; + xlnx,xcl1-writexfer = <0x1>; + xlnx,xcl2-linesize = <0x4>; + xlnx,xcl2-writexfer = <0x1>; + xlnx,xcl3-linesize = <0x4>; + xlnx,xcl3-writexfer = <0x1>; + } ; + Hard_Ethernet_MAC: xps-ll-temac@81c00000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "xlnx,compound"; + ranges ; + ethernet@81c00000 { + compatible = "xlnx,xps-ll-temac-1.01.b", "xlnx,xps-ll-temac-1.00.a"; + device_type = "network"; + interrupt-parent = <&xps_intc_0>; + interrupts = < 5 2 >; + llink-connected = <&PIM3>; + local-mac-address = [ 00 0a 35 00 00 00 ]; + reg = < 0x81c00000 0x40 >; + xlnx,bus2core-clk-ratio = <0x1>; + xlnx,phy-type = <0x1>; + xlnx,phyaddr = <0x1>; + xlnx,rxcsum = <0x0>; + xlnx,rxfifo = <0x1000>; + xlnx,temac-type = <0x0>; + xlnx,txcsum = <0x0>; + xlnx,txfifo = <0x1000>; + } ; + } ; + IIC_EEPROM: i2c@81600000 { + compatible = "xlnx,xps-iic-2.00.a"; + interrupt-parent = <&xps_intc_0>; + interrupts = < 6 2 >; + reg = < 0x81600000 0x10000 >; + xlnx,clk-freq = <0x7735940>; + xlnx,family = "virtex5"; + xlnx,gpo-width = <0x1>; + xlnx,iic-freq = <0x186a0>; + xlnx,scl-inertial-delay = <0x0>; + xlnx,sda-inertial-delay = <0x0>; + xlnx,ten-bit-adr = <0x0>; + } ; + LEDs_8Bit: gpio@81400000 { + compatible = "xlnx,xps-gpio-1.00.a"; + interrupt-parent = <&xps_intc_0>; + interrupts = < 7 2 >; + reg = < 0x81400000 0x10000 >; + xlnx,all-inputs = <0x0>; + xlnx,all-inputs-2 = <0x0>; + xlnx,dout-default = <0x0>; + xlnx,dout-default-2 = <0x0>; + xlnx,family = "virtex5"; + xlnx,gpio-width = <0x8>; + xlnx,interrupt-present = <0x1>; + xlnx,is-bidir = <0x1>; + xlnx,is-bidir-2 = <0x1>; + xlnx,is-dual = <0x0>; + xlnx,tri-default = <0xffffffff>; + xlnx,tri-default-2 = <0xffffffff>; + #gpio-cells = <2>; + gpio-controller; + } ; + + gpio-leds { + compatible = "gpio-leds"; + + heartbeat { + label = "Heartbeat"; + gpios = <&LEDs_8Bit 4 1>; + linux,default-trigger = "heartbeat"; + }; + + yellow { + label = "Yellow"; + gpios = <&LEDs_8Bit 5 1>; + }; + + red { + label = "Red"; + gpios = <&LEDs_8Bit 6 1>; + }; + + green { + label = "Green"; + gpios = <&LEDs_8Bit 7 1>; + }; + } ; + RS232_Uart_1: serial@84000000 { + clock-frequency = <125000000>; + compatible = "xlnx,xps-uartlite-1.00.a"; + current-speed = <115200>; + device_type = "serial"; + interrupt-parent = <&xps_intc_0>; + interrupts = < 8 0 >; + port-number = <0>; + reg = < 0x84000000 0x10000 >; + xlnx,baudrate = <0x1c200>; + xlnx,data-bits = <0x8>; + xlnx,family = "virtex5"; + xlnx,odd-parity = <0x0>; + xlnx,use-parity = <0x0>; + } ; + SysACE_CompactFlash: sysace@83600000 { + compatible = "xlnx,xps-sysace-1.00.a"; + interrupt-parent = <&xps_intc_0>; + interrupts = < 4 2 >; + reg = < 0x83600000 0x10000 >; + xlnx,family = "virtex5"; + xlnx,mem-width = <0x10>; + } ; + debug_module: debug@84400000 { + compatible = "xlnx,mdm-1.00.d"; + reg = < 0x84400000 0x10000 >; + xlnx,family = "virtex5"; + xlnx,interconnect = <0x1>; + xlnx,jtag-chain = <0x2>; + xlnx,mb-dbg-ports = <0x1>; + xlnx,uart-width = <0x8>; + xlnx,use-uart = <0x1>; + xlnx,write-fsl-ports = <0x0>; + } ; + mpmc@90000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "xlnx,mpmc-4.02.a"; + ranges ; + PIM3: sdma@84600180 { + compatible = "xlnx,ll-dma-1.00.a"; + interrupt-parent = <&xps_intc_0>; + interrupts = < 2 2 1 2 >; + reg = < 0x84600180 0x80 >; + } ; + } ; + xps_intc_0: interrupt-controller@81800000 { + #interrupt-cells = <0x2>; + compatible = "xlnx,xps-intc-1.00.a"; + interrupt-controller ; + reg = < 0x81800000 0x10000 >; + xlnx,kind-of-intr = <0x100>; + xlnx,num-intr-inputs = <0x9>; + } ; + xps_timer_1: timer@83c00000 { + compatible = "xlnx,xps-timer-1.00.a"; + interrupt-parent = <&xps_intc_0>; + interrupts = < 3 2 >; + reg = < 0x83c00000 0x10000 >; + xlnx,count-width = <0x20>; + xlnx,family = "virtex5"; + xlnx,gen0-assert = <0x1>; + xlnx,gen1-assert = <0x1>; + xlnx,one-timer-only = <0x0>; + xlnx,trig0-assert = <0x1>; + xlnx,trig1-assert = <0x1>; + } ; + } ; +} ; diff --git a/crypto/Kconfig b/crypto/Kconfig index 527a857..a122aed 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -31,7 +31,7 @@ config CRYPTO_FIPS this is. config CRYPTO_ALGAPI - tristate + tristate "ALGAPI" select CRYPTO_ALGAPI2 help This option provides the API for cryptographic algorithms. @@ -40,7 +40,7 @@ config CRYPTO_ALGAPI2 tristate config CRYPTO_AEAD - tristate + tristate "AEAD" select CRYPTO_AEAD2 select CRYPTO_ALGAPI @@ -49,7 +49,7 @@ config CRYPTO_AEAD2 select CRYPTO_ALGAPI2 config CRYPTO_BLKCIPHER - tristate + tristate "BLKCIPHER" select CRYPTO_BLKCIPHER2 select CRYPTO_ALGAPI @@ -60,7 +60,7 @@ config CRYPTO_BLKCIPHER2 select CRYPTO_WORKQUEUE config CRYPTO_HASH - tristate + tristate "HASH" select CRYPTO_HASH2 select CRYPTO_ALGAPI @@ -69,7 +69,7 @@ config CRYPTO_HASH2 select CRYPTO_ALGAPI2 config CRYPTO_RNG - tristate + tristate "RNG" select CRYPTO_RNG2 select CRYPTO_ALGAPI @@ -923,3 +923,6 @@ config CRYPTO_USER_API_SKCIPHER source "drivers/crypto/Kconfig" endif # if CRYPTO + +source "crypto/ocf/Kconfig" + diff --git a/crypto/Makefile b/crypto/Makefile index 9e6eee2..3cde9f8 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o +obj-$(CONFIG_OCF_OCF) += ocf/ + # # generic algorithms and the async_tx api # diff --git a/drivers/Kconfig b/drivers/Kconfig index b5e6f24..e116562 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" source "drivers/spi/Kconfig" +source "drivers/spi2/Kconfig" + source "drivers/pps/Kconfig" source "drivers/ptp/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 1b31421..2fc7c1c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_ATA) += ata/ obj-$(CONFIG_TARGET_CORE) += target/ obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_SPI) += spi/ +obj-$(CONFIG_SPI2) += spi2/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 6692108..35fc80c 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -21,8 +21,17 @@ #include #include #include +#include +#include #include "ahci.h" +#ifdef CONFIG_ARCH_M86XXX +/* SATA Clocks */ +static struct clk *sata_oob_clk; /* Core clock */ +static struct clk *sata_pmu_clk; /* PMU alive clock */ +static struct clk *sata_clk; /* Sata AXI ref clock */ +#endif + enum ahci_type { AHCI, /* standard platform ahci */ IMX53_AHCI, /* ahci on i.mx53 */ @@ -62,6 +71,50 @@ static struct scsi_host_template ahci_platform_sht = { AHCI_SHT("ahci_platform"), }; +#ifdef CONFIG_PM +static int ahci_platform_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ata_host *host = platform_get_drvdata(pdev); + int ret=0; + if (host) + ret = ata_host_suspend(host, state); + +#ifdef CONFIG_ARCH_M86XXX + if (!ret) /* sucessfully done the host suspend */ + { + /* No do the clock disable PMU,OOB,AXI here */ + clk_disable(sata_clk); + clk_disable(sata_oob_clk); + clk_disable(sata_pmu_clk); + } +#endif + + return ret; +} + +static int ahci_platform_resume(struct platform_device *pdev) +{ + struct ata_host *host = platform_get_drvdata(pdev); + +#ifdef CONFIG_ARCH_M86XXX + /* Do the clock enable here PMU,OOB,AXI */ + clk_enable(sata_clk); + clk_enable(sata_oob_clk); + clk_enable(sata_pmu_clk); +#endif + + if (host) + ata_host_resume(host); + + return 0; +} +#else +#define ahci_platform_suspend NULL +#define ahci_platform_resume NULL +#endif + + + static int __init ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -76,7 +129,53 @@ static int __init ahci_probe(struct platform_device *pdev) int n_ports; int i; int rc; +#ifdef CONFIG_ARCH_M86XXX + /* Get the Reference and Enable the SATA clocks here */ + + sata_clk = clk_get(NULL,"sata"); + /* Error Handling , if no SATA(AXI) clock reference: return error */ + if (IS_ERR(sata_clk)) { + pr_err("%s: Unable to obtain SATA(AXI) clock: %ld\n",__func__,PTR_ERR(sata_clk)); + return PTR_ERR(sata_clk); + } + + /*Enable the SATA(AXI) clock here */ + rc = clk_enable(sata_clk); + if (rc){ + pr_err("%s: SATA(AXI) clock enable failed \n",__func__); + return rc; + } + sata_oob_clk = clk_get(NULL,"sata_oob"); + /* Error Handling , if no SATA_OOB clock reference: return error */ + if (IS_ERR(sata_oob_clk)) { + pr_err("%s: Unable to obtain SATA_OOB clock: %ld\n",__func__,PTR_ERR(sata_oob_clk)); + return PTR_ERR(sata_oob_clk); + } + + sata_pmu_clk = clk_get(NULL,"sata_pmu"); + /* Error Handling , if no SATA_PMU clock reference: return error */ + if (IS_ERR(sata_pmu_clk)) { + pr_err("%s: Unable to obtain SATA_PMU clock: %ld\n",__func__,PTR_ERR(sata_pmu_clk)); + return PTR_ERR(sata_pmu_clk); + } + /*Enable the SATA(PMU and OOB) clocks here */ + rc = clk_enable(sata_oob_clk); + if (rc){ + pr_err("%s: SATA_OOB clock enable failed \n",__func__); + return rc; + } + + rc = clk_enable(sata_pmu_clk); + if (rc){ + pr_err("%s: SATA_PMU clock enable failed \n",__func__); + return rc; + } + /* Set the SATA PMU clock to 30 MHZ and OOB clock to 125MHZ */ + clk_set_rate(sata_oob_clk,125000000); + clk_set_rate(sata_pmu_clk,30000000); + +#endif mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { dev_err(dev, "no mmio space\n"); @@ -164,6 +263,12 @@ static int __init ahci_probe(struct platform_device *pdev) if (ap->flags & ATA_FLAG_EM) ap->em_message_type = hpriv->em_msg_type; +#ifdef CONFIG_ARCH_M86XXX + /* Optimized PFE/SATA DDR interaction, + limit burst size of SATA controller */ + writel(0 , ahci_port_base(ap) + 0x70); +#endif + /* disabled/not-implemented port */ if (!(hpriv->port_map & (1 << i))) ap->ops = &ata_dummy_port_ops; @@ -198,6 +303,28 @@ static int __devexit ahci_remove(struct platform_device *pdev) if (pdata && pdata->exit) pdata->exit(dev); +#ifdef CONFIG_ARCH_M86XXX + /* Disbale the SATA clocks Here */ + clk_disable(sata_clk); + clk_put(sata_clk); + clk_disable(sata_oob_clk); + clk_put(sata_oob_clk); + clk_disable(sata_pmu_clk); + clk_put(sata_pmu_clk); + + /*Putting SATA in reset state + * Sata axi clock domain in reset state + * Serdes 1/2 in reset state, this depends upon PCIE1 and SGMII + * sata 0/1 serdes controller in reset state + */ + c2000_block_reset(COMPONENT_AXI_SATA,1); + + c2000_block_reset(COMPONENT_SERDES1,1); + c2000_block_reset(COMPONENT_SERDES_SATA0,1); + + c2000_block_reset(COMPONENT_SERDES2,1); + c2000_block_reset(COMPONENT_SERDES_SATA1,1); +#endif return 0; } @@ -210,13 +337,17 @@ static const struct of_device_id ahci_of_match[] = { MODULE_DEVICE_TABLE(of, ahci_of_match); static struct platform_driver ahci_driver = { - .remove = __devexit_p(ahci_remove), - .driver = { - .name = "ahci", - .owner = THIS_MODULE, - .of_match_table = ahci_of_match, + .remove = __devexit_p(ahci_remove), +#ifdef CONFIG_PM + .suspend = ahci_platform_suspend, + .resume = ahci_platform_resume, +#endif + .driver = { + .name = "ahci", + .owner = THIS_MODULE, + .of_match_table = ahci_of_match, }, - .id_table = ahci_devtype, + .id_table = ahci_devtype, }; static int __init ahci_init(void) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index de2802c..c832779 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1739,6 +1739,22 @@ static void ahci_port_intr(struct ata_port *ap) } } +/* +*/ + +#if defined (CONFIG_COMCERTO_AHCI_PROF) +unsigned int ahci_time_counter[256]; // 4 ms -> 1S +unsigned int ahci_data_counter[256]; // 4K-> 1020K +unsigned int ahci_int_before_req; +static struct timeval last_ahci_req; +unsigned int init_ahci_prof = 0; +unsigned int enable_ahci_prof = 0; +extern struct timeval ahci_last_qc_comp[32]; +extern unsigned int ahci_last_qc_comp_flag[32]; +#endif + +static struct timeval time; + irqreturn_t ahci_interrupt(int irq, void *dev_instance) { struct ata_host *host = dev_instance; @@ -1806,6 +1822,33 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) void __iomem *port_mmio = ahci_port_base(ap); struct ahci_port_priv *pp = ap->private_data; +#if defined(CONFIG_COMCERTO_AHCI_PROF) + struct timeval now; + + if (enable_ahci_prof) { + do_gettimeofday(&now); + + if (init_ahci_prof) { + int diff_time_ms; + diff_time_ms = ((now.tv_sec - last_ahci_req.tv_sec) * 1000) + ((now.tv_usec - last_ahci_req.tv_usec) / 1000); + if (diff_time_ms < 1000) {//Don't record more than 1s + ahci_time_counter[diff_time_ms >> 3]++; + } + else + ahci_time_counter[255]++; + } + else { + init_ahci_prof = 1; + } + last_ahci_req = now; + + if (qc->nbytes < (1 << 21)) + ahci_data_counter[(qc->nbytes >> 13) & 0xFF]++; + else + ahci_data_counter[255]++; + } +#endif + /* Keep track of the currently active link. It will be used * in completion path to determine whether NCQ phase is in * progress. @@ -1822,6 +1865,12 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) writel(fbs, port_mmio + PORT_FBS); pp->fbs_last_dev = qc->dev->link->pmp; } +#if defined(CONFIG_COMCERTO_AHCI_PROF) + if (enable_ahci_prof) { + ahci_last_qc_comp[qc->tag] = now; + ahci_last_qc_comp_flag[qc->tag] = 1; + } +#endif writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5d8fc3d..60f3f74 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4707,6 +4707,14 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) #endif /* __BIG_ENDIAN */ } +#if defined(CONFIG_COMCERTO_AHCI_PROF) +unsigned int ahci_qc_comp_counter[33]; +struct timeval ahci_last_qc_comp[32]; +unsigned int ahci_last_qc_comp_flag[32]; +unsigned int ahci_qc_no_free_slot = 0; +extern unsigned int enable_ahci_prof; +#endif + /** * ata_qc_new - Request an available ATA command, for queueing * @ap: target port @@ -4747,6 +4755,13 @@ static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) } } +#if defined(CONFIG_COMCERTO_AHCI_PROF) + if (enable_ahci_prof) + if (qc == NULL) { + ahci_qc_no_free_slot++; + } +#endif + return qc; } @@ -4790,6 +4805,11 @@ void ata_qc_free(struct ata_queued_cmd *qc) struct ata_port *ap; unsigned int tag; +#if defined(CONFIG_COMCERTO_AHCI_PROF) + struct timeval now; + int diff_time_ms; +#endif + WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ ap = qc->ap; @@ -4798,6 +4818,26 @@ void ata_qc_free(struct ata_queued_cmd *qc) if (likely(ata_tag_valid(tag))) { qc->tag = ATA_TAG_POISON; clear_bit(tag, &ap->qc_allocated); + +#if defined(CONFIG_COMCERTO_AHCI_PROF) + if (enable_ahci_prof) { + if (ahci_last_qc_comp_flag[tag]) { + int inx = 32; + + do_gettimeofday(&now); + + diff_time_ms = ((now.tv_sec - ahci_last_qc_comp[tag].tv_sec) * 1000) + + ((now.tv_usec - ahci_last_qc_comp[tag].tv_usec) / 1000); + + if (diff_time_ms < 512) + inx = diff_time_ms >> 4; + + ahci_qc_comp_counter[inx]++; + + ahci_last_qc_comp_flag[tag] = 0; + } + } +#endif } } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index b96544a..1955d73 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1146,3 +1146,192 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) return async_error; } EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); + + +/* + * Code added to suppoprt device SUSPEND(L1 and L2 ) and RESUME + * (L1 = clock gating L2 = clock gating + reset) . + * Depends upon config option CONFIG_PM_SYSFS_MANUAL + * + */ + +#ifdef CONFIG_PM_SYSFS_MANUAL + +static DEFINE_MUTEX(dpm_lock); + +/** + * dpm_manual_resume - resume the device . + * uses device_resume and device_complete to acheive feature. + * part of the code borrowed from dpm_resume and dpm_resume_complete. + * @dev: Device. + * @state: State to enter. +*/ + +void dpm_manual_resume(struct device *dev,pm_message_t state) +{ + int error; + struct list_head list; + ktime_t starttime = ktime_get(); + + might_sleep(); + + /* Device resume prepare starts here */ + mutex_lock(&dpm_list_mtx); + pm_transition = state; + INIT_COMPLETION(dev->power.completion); + mutex_unlock(&dpm_list_mtx); + + error = device_resume(dev, state, false); + if (error) { + suspend_stats.failed_resume++; + dpm_save_failed_step(SUSPEND_RESUME); + dpm_save_failed_dev(dev_name(dev)); + pm_dev_err(dev, state, "", error); + } + + mutex_lock(&dpm_list_mtx); + if (!list_empty(&dev->power.entry)) + list_move_tail(&dev->power.entry, &dpm_prepared_list); + mutex_unlock(&dpm_list_mtx); + + /* DPM complete start */ + INIT_LIST_HEAD(&list); + mutex_lock(&dpm_list_mtx); + dev->power.is_prepared = false; + list_move(&dev->power.entry, &list); + mutex_unlock(&dpm_list_mtx); + + device_complete(dev, state); + dev->power.power_state=state; + dpm_show_time(starttime, state, NULL); +} + + +/** + * dpm_manual_resume_start - Start the process for Power one device back to work. + * @dev: Device. + * @state: State to enter. + * Code inspired from dpm_resume_end(). + * + * Bring one device back to the on state by first powering it + * on, then restoring state. We only operate on devices that aren't + * already on. + */ + +void dpm_manual_resume_start(struct device * dev,pm_message_t state) +{ + mutex_lock(&dpm_lock); + if (dev->power.power_state.event == state.event){ + printk(KERN_ERR "PM: We are already in the resume state \n"); + goto done; + } + /* Device resume starts from here */ + dpm_manual_resume(dev,state); +done: + mutex_unlock(&dpm_lock); + +} + +/** + * dpm_manual_prepare - prepare the device for power transition. + * Part of the code borrowed from dpm_prapare. + * @dev: Device. + * @state: State to enter. + */ +static int dpm_manual_prepare(struct device * dev , pm_message_t state) +{ + /* This part of code is borrowed from dpm_prepare + * make the deice for prepare. + */ + int error = 0; + might_sleep(); + + /* Call the device prepare */ + error = device_prepare(dev, state); + + mutex_lock(&dpm_list_mtx); + if (error){ + printk(KERN_INFO "PM: Device %s not prepared " "for power transition: code %d\n", + dev_name(dev), error); + goto done; + } + dev->power.is_prepared = true; + if (!list_empty(&dev->power.entry)) + list_move_tail(&dev->power.entry, &dpm_prepared_list); + +done: + mutex_unlock(&dpm_list_mtx); + return error; +} + +/** + * dpm_manual_suspend - Helper routing to call the device_suspend. + * Part of the code borrowed from dpm_supend(). + * @dev: Device. + * @state: State to enter. + */ +static int dpm_manual_suspend(struct device * dev, pm_message_t state) +{ + ktime_t starttime; + int error=0; + + might_sleep(); + + mutex_lock(&dpm_list_mtx); + pm_transition = state; + mutex_unlock(&dpm_list_mtx); + + error = device_suspend(dev); + + mutex_lock(&dpm_list_mtx); + if (error){ + pm_dev_err(dev, state, "", error); + dpm_save_failed_dev(dev_name(dev)); + } + if (!list_empty(&dev->power.entry)) + list_move(&dev->power.entry, &dpm_suspended_list); + mutex_unlock(&dpm_list_mtx); + + dev->power.power_state=state; + dpm_show_time(starttime, state, NULL); + return error; +} + +/** + * dpm_manual_suspend_start - Put one device in Power of L1/L2 state. + * Power off L1 - clock gating , Power off L2 - clock gating + device reset + * Part of the code borrowed from dpm_suspend_start. + * @dev: Device. + * @state: State to enter. + */ +int dpm_manual_suspend_start(struct device * dev, pm_message_t state) +{ + int error=0; + + /* Start the global mutex value*/ + mutex_lock(&dpm_lock); + + if (dev->power.power_state.event == state.event){ + if ( state.event == PM_EVENT_SUSPEND ) + printk(KERN_ERR "PM: We are already in the suspend (power off L1) state \n"); +#if 0 + else if ( state.event == PM_EVENT_SUSPEND_L2) + printk(KERN_ERR "PM: We are already in the suspend (Power off L2) state \n"); +#endif + goto done; + } + + /* Devce PM prepare starts from here */ + error=dpm_manual_prepare(dev,state); + + if (error){ + suspend_stats.failed_prepare++; + dpm_save_failed_step(SUSPEND_PREPARE); + goto done; + }else + error = dpm_manual_suspend(dev,state); +done: + mutex_unlock(&dpm_lock); + return error; +} +#endif diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 95706fa..fe10b841 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -330,6 +330,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) { struct device_opp *dev_opp; struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); + int c = 0; if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); @@ -341,10 +342,15 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) return opp; list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + ++c; if (temp_opp->available) { /* go to the next node, before choosing prev */ if (temp_opp->rate > *freq) + { + if (c == 1) + opp = temp_opp; break; + } else opp = temp_opp; } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 9bf6232..8e9d09d 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -81,3 +81,15 @@ static inline int wakeup_sysfs_add(struct device *dev) { return 0; } static inline void wakeup_sysfs_remove(struct device *dev) {} #endif + +/* Added for SYSFS support to handle from Device power management from + * user space. Manual PM configuration. + */ + +#ifdef CONFIG_PM_SYSFS_MANUAL + +extern int dpm_manual_suspend_start(struct device * , pm_message_t ); +extern void dpm_manual_resume_start(struct device * , pm_message_t); + +#endif /* CONFIG_PM_SYSFS_MANUAL */ + diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index adf41be0..1d2db86 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -89,6 +89,12 @@ * value are used only if the driver calls pm_runtime_use_autosuspend(). * * wakeup_count - Report the number of wakeup events related to the device + * + * MSPD: Added the support for manual PM operation for NON-CPU devices. + * This is operated through power/state file . operates in two states + * PM_EVENT_SUSPEND= Power off L1 state (device Clock gating ) + * PM_EVENT_SUSPEND_L2=Power off L2 state ( device Clock gating + reset). + * */ static const char enabled[] = "enabled"; @@ -97,6 +103,44 @@ static const char disabled[] = "disabled"; const char power_group_name[] = "power"; EXPORT_SYMBOL_GPL(power_group_name); +#ifdef CONFIG_PM_SYSFS_MANUAL +static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) +{ + if (dev->power.power_state.event == PM_EVENT_SUSPEND) /* Power off L1 state */ + return sprintf(buf, "2\n"); +#if 0 + else if (dev->power.power_state.event == PM_EVENT_SUSPEND_L2) /* Power off L2 state */ + return sprintf(buf, "3\n"); +#endif + else + return sprintf(buf, "0\n"); +} + +static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n) +{ + pm_message_t state; + int error = -EINVAL; + + if ((n == 2) && (buf[0] == '2')) { + state.event = PM_EVENT_SUSPEND; + error = dpm_manual_suspend_start(dev, state); /* Power off L1 state */ + } +#if 0 + if ((n == 2) && (buf[0] == '3')) { + state.event = PM_EVENT_SUSPEND_L2; + error = dpm_manual_suspend(dev, state); /* Power off L2 state */ + } +#endif + if ((n == 2) && (buf[0] == '0')) { + state.event = PM_EVENT_RESUME; + dpm_manual_resume_start(dev, state); + error = 0; + } + return error ? error : n; +} +static DEVICE_ATTR(state, 0644, state_show, state_store); +#endif + #ifdef CONFIG_PM_RUNTIME static const char ctrl_auto[] = "auto"; static const char ctrl_on[] = "on"; @@ -432,11 +476,13 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr, return -EINVAL; return n; } - static DEVICE_ATTR(async, 0644, async_show, async_store); #endif /* CONFIG_PM_ADVANCED_DEBUG */ static struct attribute *power_attrs[] = { +#ifdef CONFIG_PM_SYSFS_MANUAL + &dev_attr_state.attr, +#endif #ifdef CONFIG_PM_ADVANCED_DEBUG #ifdef CONFIG_PM_SLEEP &dev_attr_async.attr, diff --git a/drivers/char/random.c b/drivers/char/random.c index 3dadca4..953005f 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -840,7 +840,7 @@ void add_disk_randomness(struct gendisk *disk) */ void random_input_words(__u32 *buf, size_t wordcount, int ent_count) { - mix_pool_bytes(&input_pool, buf, wordcount*4); + mix_pool_bytes(&input_pool, buf, wordcount*4, null); credit_entropy_bits(&input_pool, ent_count); diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index e24a2a1..c08b968 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -45,7 +45,7 @@ config CPU_FREQ_STAT_DETAILS choice prompt "Default CPUFreq governor" default CPU_FREQ_DEFAULT_GOV_USERSPACE if CPU_FREQ_SA1100 || CPU_FREQ_SA1110 - default CPU_FREQ_DEFAULT_GOV_PERFORMANCE + default CPU_FREQ_DEFAULT_GOV_ONDEMAND help This option sets which CPUFreq governor shall be loaded at startup. If in doubt, select 'performance'. diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 72a0044..ad6b9bfb 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -30,3 +30,8 @@ config ARM_EXYNOS4210_CPUFREQ SoC (S5PV310 or S5PC210). If in doubt, say N. + +config CPU_FREQ_C2K + bool "CPUfreq driver for Comcerto" + depends on ARCH_COMCERTO && CPU_FREQ + default y diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index a48bc02..5cc3681 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o ################################################################################## # PowerPC platform drivers obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o +obj-$(CONFIG_CPU_FREQ_C2K) += c2k-cpufreq.o diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 987a165..c23af68 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -923,8 +923,10 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) #ifdef CONFIG_HOTPLUG_CPU for_each_online_cpu(sibling) { struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling); + if (cp && cp->governor && (cpumask_test_cpu(cpu, cp->related_cpus))) { + policy->governor = cp->governor; found = 1; break; diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 8f04910..85302e6 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -65,4 +65,10 @@ config DEVFREQ_GOV_USERSPACE comment "DEVFREQ Drivers" +config COMCERTO_DEVFREQ_SUPPORT + bool "Enable Comcerto devfreq" + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + Adds devfreq support in Comcerto. + endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 4564a89..af24e66 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o +obj-$(CONFIG_COMCERTO_DEVFREQ_SUPPORT) += c2k-devfreq.o diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index a842317..cbbd8f8 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -122,6 +122,22 @@ config MV_XOR ---help--- Enable support for the Marvell XOR engine. +config COMCERTO_XOR + tristate "Mindspeed Comcerto 2000 XOR engine support" + depends on ARCH_M86XXX + select DMA_ENGINE + select ASYNC_TX_ENABLE_CHANNEL_SWITCH + ---help--- + Enable support for the Mindspeed XOR engine. + +config COMCERTO_DMA_BASIC + tristate "Mindspeed Comcerto 2000 MDMA engine basic support" + depends on ARCH_M86XXX + select DMA_ENGINE + select ASYNC_TX_ENABLE_CHANNEL_SWITCH + ---help--- + Enable support for the Mindspeed mdma engine. + config MX3_IPU bool "MX3x Image Processing Unit support" depends on SOC_IMX31 || SOC_IMX35 diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 30cf3b1..4e08999 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -26,3 +26,5 @@ obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o +obj-$(CONFIG_COMCERTO_XOR) += comcerto_xor.o +obj-$(CONFIG_COMCERTO_DMA_BASIC) += c2k_dma.o diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4e04157..ea83fe8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -147,6 +147,13 @@ config GPIO_XILINX help Say yes here to support the Xilinx FPGA GPIO device +config GPIO_C2K + def_bool y + depends on ARCH_M86XXX + bool "Comcerto 2000 GPIO support" + help + Say yes here to support the Comcerto 2000 GPIO device + config GPIO_VR41XX tristate "NEC VR4100 series General-purpose I/O Uint support" depends on CPU_VR41XX diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4e018d6..924348b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -62,3 +62,5 @@ obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o +obj-$(CONFIG_GPIO_C2K) += gpio-c2k.o + diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8251ce8..c0b10a3 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -2,6 +2,7 @@ # Makefile for sensor chip drivers. # +obj-y += wd-fan.o obj-$(CONFIG_HWMON) += hwmon.o obj-$(CONFIG_HWMON_VID) += hwmon-vid.o diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 5f13c62..3151581 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -44,7 +44,7 @@ config I2C_CHARDEV programs use the I2C bus. Information on how to do this is contained in the file . - This support is also available as a module. If so, the module + This support is also available as a module. If so, the module will be called i2c-dev. config I2C_MUX @@ -86,6 +86,7 @@ config I2C_SMBUS source drivers/i2c/algos/Kconfig source drivers/i2c/busses/Kconfig +source drivers/i2c/chips/Kconfig config I2C_DEBUG_CORE bool "I2C Core debugging messages" diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index beee6b2..a29d46d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o -obj-y += algos/ busses/ muxes/ +obj-y += algos/ busses/ chips/ muxes/ ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG CFLAGS_i2c-core.o := -Wno-deprecated-declarations diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 949ea640..d4e6ead 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -312,6 +312,16 @@ config I2C_AU1550 This driver can also be built as a module. If so, the module will be called i2c-au1550. +config I2C_COMCERTO + tristate "Comcerto I2C interface" + depends on I2C && (ARCH_COMCERTO) + help + If you say yes to this option, support will be included for the + Comcerto I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-comcerto. + config I2C_BLACKFIN_TWI tristate "Blackfin TWI I2C support" depends on BLACKFIN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index d6b8779..37f2423 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o +obj-$(CONFIG_I2C_COMCERTO) += i2c-comcerto.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a244fc6..099bffc 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -4,6 +4,7 @@ # Each configuration option enables a list of files. +obj-y += btn-wd.o obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e4f6bf5..9674596 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -44,6 +44,12 @@ obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o +## WD LED Drivers (NB: Temporarily must be built as default) +obj-$(CONFIG_NEW_LEDS) += leds-wd.o +#obj-y += led-class-3g.o +#obj-y += leds-apollo3g.o + + # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o @@ -54,3 +60,5 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + + diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 6d5628b..872cb1d 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -20,8 +20,62 @@ #include #include #include +#include #include "leds.h" +#define MAX_USERS 32 +#define N_COLORS 4 +#define N_EVENTS 3 +#define USR_LEN 81 +#define EVENT_LEN 81 +#define INDEX_LEN 8 + + +/* LED users */ +#define EV_NAS_SYSTEM 0 /* Overall system: NAS ready, booting, shutdown... */ +#define EV_DISK_SMART 1 /* Disk SMART including temp., error lba, ...*/ +#define EV_DISK_IO 2 /* Disk read/write error */ +#define EV_RAID_CFG 3 /* RAID setup failure: assembling, formatting, rebuild ...*/ +#define EV_FW_UPDATE 4 /* NAS firmware update */ +#define EV_NETWORK 5 /* Network connectivity error */ +#define EV_VM 6 /* Volume manager */ + +char Led_user_arr[MAX_USERS][USR_LEN] = { "EV_NAS_SYSTEM", \ + "EV_DISK_SMART", \ + "EV_DISK_IO" , \ + "EV_RAID_CFG" , \ + "EV_FW_UPDATE" , \ + "EV_NETWORK" , \ + "EV_VM", \ + }; +/* LED event types */ +#define LED_STAT_OK 0 /* Happy user, normal operation */ +#define LED_STAT_ERR 1 /* User error, needs led indication */ +#define LED_STAT_IN_PROG 2 /* User doing something important, needs led indication */ + +char *Led_ev_arr[] = { "LED_STAT_OK", "LED_STAT_ERR", "LED_STAT_IN_PROG" }; + +char Color_map[MAX_USERS][N_EVENTS] = { {'g','r','w'}, /* EV_NAS_SYSTEM */ \ + {'g','y','w'}, /* EV_DISK_SMART */ \ + {'g','r','w'}, /* EV_DISK_IO */ \ + {'g','r','w'}, /* EV_RAID_CFG */ \ + {'g','r','w'}, /* EV_FW_UPDATE */ \ + {'g','y','w'}, /* EV_NETWORK */ \ + {'g','r','w'}, /* EV_VM */ \ + }; + +char Blink_map[MAX_USERS][N_EVENTS] = { {'n','n','n'}, /* EV_NAS_SYSTEM */ \ + {'n','y','n'}, /* EV_DISK_SMART */ \ + {'n','n','n'}, /* EV_DISK_IO */ \ + {'n','n','n'}, /* EV_RAID_CFG */ \ + {'n','n','n'}, /* EV_FW_UPDATE */ \ + {'n','y','n'}, /* EV_NETWORK */ \ + {'n','n','n'}, /* EV_VM */ \ + }; + +u32 Led_error_bits = 0; +int N_USERS = 7; /* default number of users */ + static struct class *leds_class; static void led_update_brightness(struct led_classdev *led_cdev) @@ -31,7 +85,7 @@ static void led_update_brightness(struct led_classdev *led_cdev) } static ssize_t led_brightness_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); @@ -42,7 +96,7 @@ static ssize_t led_brightness_show(struct device *dev, } static ssize_t led_brightness_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) + struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); ssize_t ret = -EINVAL; @@ -52,15 +106,13 @@ static ssize_t led_brightness_store(struct device *dev, if (isspace(*after)) count++; - if (count == size) { ret = count; - if (state == LED_OFF) led_trigger_remove(led_cdev); led_set_brightness(led_cdev, state); } - + printk(KERN_DEBUG "We are here 10\n"); return ret; } @@ -72,7 +124,278 @@ static ssize_t led_max_brightness_show(struct device *dev, return sprintf(buf, "%u\n", led_cdev->max_brightness); } +static void led_update_color(struct led_classdev *led_cdev) +{ + if (led_cdev->color_get) + led_cdev->color = led_cdev->color_get(led_cdev); +} + +static ssize_t led_color_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + char * readbuf[] = {"off", "red", "green", "blue", "yellow", "white"} ; + /* no lock needed for this */ + led_update_color(led_cdev); + + return sprintf(buf, "%s\n", readbuf[led_cdev->color]); +} + +static ssize_t led_color_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + unsigned long state = 9; + char user[USR_LEN], event[EVENT_LEN], index_str[INDEX_LEN], color; + int i = 0, j = 0, found = 0, tmp = 0, edit_policy = 0; + int reg_user = -1, reg_event = -1, reg_color = -1; + const char * cptr = NULL; + long int index = -1; + char blink; + int reg_blink = 'n'; + + cptr = &buf[0]; + + /* check for 'register' event */ + // NB: Format of register event is + // register:event,status,color + if( cptr[8] == ':' ) { + if( !memcmp("register", cptr, 8) ) { + edit_policy = 1; + cptr = &buf[9]; + } + } + + /* parse user name */ + for( i = 0; i < (USR_LEN -1) && cptr[i]; i++ ) { + if( cptr[i] == ',' ) { + break; + } + user[i] = cptr[i]; + } + + /* null terminate user buf */ + user[i] = '\0'; + i++; /* skips the ',' delimiter */ + + + for( j = 0; (j < EVENT_LEN -1) && cptr[i] ; j++,i++ ) { + if( (cptr[i] == ',') || (cptr[i] == '\0') || (cptr[i] == '\n') ) { + if( cptr[i] == ',' ) { + cptr = &cptr[i+1]; + } + break; + } + event[j] = cptr[i]; + } + /* null terminate event buf */ + event[j] = '\0'; + + /* if editing policy, parse the color */ + if( edit_policy ) { + if( cptr != NULL ) { + reg_color = cptr[0]; /* r,g,b,y,w */ + if( reg_color != 'r' && reg_color != 'g' && + reg_color != 'b' && reg_color != 'y' && reg_color != 'w' ) { + reg_color = -1; /* invalid color */ + } + + /** TBD: Get the value of reg_blink from cptr */ + } + } else { + /* scan index for some users */ + if( !strcmp(user, Led_user_arr[EV_DISK_SMART]) || + !strcmp(user, Led_user_arr[EV_DISK_IO]) ) { + if( cptr != NULL ) { + for( i = 0; (i < INDEX_LEN -1) && cptr[i] ; i++ ) { + if( (cptr[i] == ',') || (cptr[i] == '\0') || (cptr[i] == '\n') ) { + break; + } + index_str[i] = cptr[i]; + } + } + } + + /* null terminate index_str */ + index_str[i] = '\0'; + if( i ) { + tmp = strict_strtol(index_str, 10, &index); + if( !tmp && (index >= 0) ) { + /* + * TODO: insert code to fulfill req's. Currently not required. + */ + /*printk(KERN_INFO "\nindex %ld\n", index);*/ + } + } + } /* if( !edit_policy ) */ + + /* Validate user and event */ + found = 0; + for( i = 0; i < N_USERS; i++ ) { + if( !strcmp( Led_user_arr[i], user ) ) { + found = 1; + break; + } + } + + if( found || edit_policy) { + reg_user = i; + /* new user registration */ + if( ! found ) { + if( N_USERS == MAX_USERS ) { + /* only support up to 32 users */ + return (ssize_t)size; + } + reg_user = N_USERS++; + + strcpy(Led_user_arr[reg_user], user); + } + found = 0; + for( j = 0; j < N_EVENTS; j++ ) { + if( ! strcmp(Led_ev_arr[j], event) ) { + if( j == LED_STAT_ERR ) { + Led_error_bits |= (1 << i); /* register error for this user */ + } + else if( j == LED_STAT_OK ) { + Led_error_bits &= ~(1 << i); /* clear error for this user */ + } + found = 1; + reg_event = j; + break; + } + } + } + + /* if this is a register event, do just that */ + if( edit_policy ) { + /* valid event above and color */ + if( (reg_event != -1) && (reg_color != -1) ) { + Color_map[reg_user][reg_event] = reg_color; + + /** TBD: Add support for registering blink with register: interface*/ + reg_blink = 'n'; + Blink_map[reg_user][reg_event] = reg_blink; + } + /*printk( KERN_INFO "reg_user = %d, reg_event= %d, reg_color = %c\n", reg_user, reg_event, reg_color, reg_blink);*/ + return (ssize_t)size; + } + + /* Be nice ! support older led mechanism */ + color = buf[0]; + blink = 'x'; + + /* If valid user and event, retrieve color & blink map */ + if( found ) { + /* if a canceling event and other error(s) existing, don't do anything */ + if( (j == LED_STAT_OK) && (Led_error_bits != 0) ) { + /* Do nothing */ + } else { + color = Color_map[i][j]; + blink = Blink_map[i][j]; + } + /*printk(KERN_INFO "\nUser= %s, event= %s, color %c, %08x\n", user, event, color, blink, Led_error_bits);*/ + } + + switch (color) { + case 'o': /* off */ + state = STATE_LED_OFF; + break; + case 'r': /* red */ + state = STATE_LED_RED; + break; + case 'g': /* green */ + state = STATE_LED_GREEN; + break; + case 'b': /* blue */ + state = STATE_LED_BLUE; + break; + case 'y': /* yellow */ + state = STATE_LED_YELLOW; + break; + case 'w': /* white */ + state = STATE_LED_ALL; + break; + default: /* error */ + state = -1; + break; + } + + /* do nothing if no color change is required */ + if( state == -1 ) { + return (ssize_t)size; + } + + /* printk(KERN_DEBUG "Calling led_set_color with value %c, blink is %c\n", color, blink); */ + led_set_color(led_cdev, state); + + /** blink the led */ + { + int val = -1; + + /* printk(KERN_DEBUG "Calling led_set_blink with value %c\n", blink); */ + + switch (blink) { + case 'y': /** yes */ + val = 1; + break; + case 'n': /** no */ + val = 0; + break; + case 'f': /** forced */ + val = 2; + break; + default: + break; + } + + if( val >= 0 ) { + led_set_blink( led_cdev, val ); + } + } + + + return (ssize_t)size; +} + +static ssize_t led_blink_show(struct device *dev, struct device_attribute *attr, + char *buf) { + char *blinkStr = "on"; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + if( led_cdev->blink == 0 ) { + blinkStr = "on"; + } else if (led_cdev->blink == 1 ) { + blinkStr = "blink"; + } else if (led_cdev->blink == 2 ) { + blinkStr = "pulse"; + } + + return sprintf(buf, "%s\n", blinkStr); +} + +static ssize_t led_blink_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) { + int val = 0; + struct led_classdev * led_cdev = dev_get_drvdata(dev); + + if (buf[0] == 'b') { + val = 1; + } else if (buf[0] == 'o') { + val = 0; + } else if (buf[0] == 'f') { + val = 2; + } else if (buf[0] == 'p') { + val = 3; + } else { + val = 0; + } + + led_set_blink(led_cdev, val); + + return (ssize_t)size; +} + static struct device_attribute led_class_attrs[] = { + __ATTR(color, 0644, led_color_show, led_color_store), + __ATTR(blink, 0644, led_blink_show, /*led_set_blink*/led_blink_store), __ATTR(brightness, 0644, led_brightness_show, led_brightness_store), __ATTR(max_brightness, 0444, led_max_brightness_show, NULL), #ifdef CONFIG_LEDS_TRIGGERS @@ -81,6 +404,7 @@ static struct device_attribute led_class_attrs[] = { __ATTR_NULL, }; + static void led_timer_function(unsigned long data) { struct led_classdev *led_cdev = (void *)data; @@ -204,8 +528,7 @@ static int led_resume(struct device *dev) */ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { - led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, - "%s", led_cdev->name); + led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,"%s", led_cdev->name); if (IS_ERR(led_cdev->dev)) return PTR_ERR(led_cdev->dev); @@ -308,6 +631,6 @@ static void __exit leds_exit(void) subsys_initcall(leds_init); module_exit(leds_exit); -MODULE_AUTHOR("John Lenz, Richard Purdie"); +MODULE_AUTHOR("John Lenz, Richard Purdie, Arya Ahmadi-Ardakani"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("LED Class Interface"); diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index e77c7f8..16688ff 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -17,14 +17,42 @@ #include #include +#define STATE_LED_OFF 0 +#define STATE_LED_RED 1 +#define STATE_LED_GREEN 2 +#define STATE_LED_BLUE 3 +#define STATE_LED_YELLOW 4 +#define STATE_LED_ALL 5 +#define STATE_LED_WHITE 5 /* save as _ALL */ + +static inline void led_set_color(struct led_classdev *led_cdev, + enum led_brightness value) +{ + led_cdev->color = value; + if (!(led_cdev->flags & LED_SUSPENDED)) + led_cdev->color_set(led_cdev, value); +} + +static inline int led_get_color(struct led_classdev *led_cdev) +{ + return led_cdev->color; +} +static inline void led_set_blink(struct led_classdev *led_cdev, + enum led_brightness value) +{ + led_cdev->blink = value; + led_cdev->blink_set_3g(led_cdev, value); +} + static inline void led_set_brightness(struct led_classdev *led_cdev, - enum led_brightness value) + enum led_brightness value) { if (value > led_cdev->max_brightness) value = led_cdev->max_brightness; led_cdev->brightness = value; if (!(led_cdev->flags & LED_SUSPENDED)) led_cdev->brightness_set(led_cdev, value); + printk(KERN_DEBUG "We are here 8\n"); } static inline int led_get_brightness(struct led_classdev *led_cdev) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 4035b6d..2a65c65 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -23,6 +23,14 @@ config MTD_TESTS WARNING: some of the tests will ERASE entire MTD device which they test. Do not use these tests unless you really know what you do. +config MTD_ROOTFS_ROOT_DEV + bool "Automatically set 'rootfs' partition to be root filesystem" + default y + +config MTD_ROOTFS_SPLIT + bool "Automatically split 'rootfs' partition for squashfs" + default y + config MTD_REDBOOT_PARTS tristate "RedBoot partition table parsing" ---help--- diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 45dcb8b..cebc106 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o +obj-$(CONFIG_MTD_COMCERTO_NOR) += comcerto-nor.o diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 1d90e26..71e2006 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1026,6 +1026,12 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) break; } + case MTDREFRESH: + { + ret = mtd_device_refresh(mtd); + break; + } + default: ret = -ENOTTY; } diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 198da0a..43a018b 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -29,10 +29,14 @@ #include #include #include +#include +#include #include #include "mtdcore.h" +#define MTD_ERASE_PARTIAL 0x8000 /* partition only covers parts of an erase block */ + /* Our partition linked list */ static LIST_HEAD(mtd_partitions); static DEFINE_MUTEX(mtd_partitions_mutex); @@ -50,7 +54,7 @@ struct mtd_part { * the pointer to that structure with this macro. */ #define PART(x) ((struct mtd_part *)(x)) - +#define IS_PART(mtd) (mtd->read == part_read) /* * MTD methods which simply translate the effective address and pass through @@ -256,13 +260,60 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr) return -EROFS; if (instr->addr >= mtd->size) return -EINVAL; + + instr->partial_start = false; + if (mtd->flags & MTD_ERASE_PARTIAL) { + size_t readlen = 0; + u64 mtd_ofs; + + instr->erase_buf = kmalloc(part->master->erasesize, GFP_ATOMIC); + if (!instr->erase_buf) + return -ENOMEM; + + mtd_ofs = part->offset + instr->addr; + instr->erase_buf_ofs = do_div(mtd_ofs, part->master->erasesize); + + if (instr->erase_buf_ofs > 0) { + instr->addr -= instr->erase_buf_ofs; + ret = part->master->read(part->master, + instr->addr + part->offset, + part->master->erasesize, + &readlen, instr->erase_buf); + + instr->partial_start = true; + } else { + mtd_ofs = part->offset + part->mtd.size; + instr->erase_buf_ofs = part->master->erasesize - + do_div(mtd_ofs, part->master->erasesize); + + if (instr->erase_buf_ofs > 0) { + instr->len += instr->erase_buf_ofs; + ret = part->master->read(part->master, + part->offset + instr->addr + + instr->len - part->master->erasesize, + part->master->erasesize, &readlen, + instr->erase_buf); + } else { + ret = 0; + } + } + if (ret < 0) { + kfree(instr->erase_buf); + return ret; + } + + } + instr->addr += part->offset; ret = part->master->erase(part->master, instr); if (ret) { if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; + if (mtd->flags & MTD_ERASE_PARTIAL) + kfree(instr->erase_buf); } + return ret; } @@ -270,7 +321,25 @@ void mtd_erase_callback(struct erase_info *instr) { if (instr->mtd->erase == part_erase) { struct mtd_part *part = PART(instr->mtd); - + size_t wrlen = 0; + + if (instr->mtd->flags & MTD_ERASE_PARTIAL) { + if (instr->partial_start) { + part->master->write(part->master, + instr->addr, instr->erase_buf_ofs, + &wrlen, instr->erase_buf); + instr->addr += instr->erase_buf_ofs; + } else { + instr->len -= instr->erase_buf_ofs; + part->master->write(part->master, + instr->addr + instr->len, + instr->erase_buf_ofs, &wrlen, + instr->erase_buf + + part->master->erasesize - + instr->erase_buf_ofs); + } + kfree(instr->erase_buf); + } if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; @@ -541,18 +610,24 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, if ((slave->mtd.flags & MTD_WRITEABLE) && mtd_mod_by_eb(slave->offset, &slave->mtd)) { /* Doesn't start on a boundary of major erase size */ - /* FIXME: Let it be writable if it is on a boundary of - * _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; - printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", - part->name); + slave->mtd.flags |= MTD_ERASE_PARTIAL; + if (((u32) slave->mtd.size) > master->erasesize) + slave->mtd.flags &= ~MTD_WRITEABLE; + else + slave->mtd.erasesize = slave->mtd.size; } if ((slave->mtd.flags & MTD_WRITEABLE) && - mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { - slave->mtd.flags &= ~MTD_WRITEABLE; - printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", - part->name); + mtd_mod_by_eb(slave->offset + slave->mtd.size, &slave->mtd)) { + slave->mtd.flags |= MTD_ERASE_PARTIAL; + + if ((u32) slave->mtd.size > master->erasesize) + slave->mtd.flags &= ~MTD_WRITEABLE; + else + slave->mtd.erasesize = slave->mtd.size; } + if ((slave->mtd.flags & (MTD_ERASE_PARTIAL|MTD_WRITEABLE)) == MTD_ERASE_PARTIAL) + printk(KERN_WARNING"mtd: partition \"%s\" must either start or end on erase block boundary or be smaller than an erase block -- forcing read-only\n", + part->name); slave->mtd.ecclayout = master->ecclayout; if (master->block_isbad) { @@ -650,6 +725,155 @@ int mtd_del_partition(struct mtd_info *master, int partno) } EXPORT_SYMBOL_GPL(mtd_del_partition); +#ifdef CONFIG_MTD_ROOTFS_SPLIT +#define ROOTFS_SPLIT_NAME "rootfs_data" +#define ROOTFS_REMOVED_NAME "" + +struct squashfs_super_block { + __le32 s_magic; + __le32 pad0[9]; + __le64 bytes_used; +}; + + +static int split_squashfs(struct mtd_info *master, int offset, int *split_offset) +{ + struct squashfs_super_block sb; + int len, ret; + + ret = master->read(master, offset, sizeof(sb), &len, (void *) &sb); + if (ret || (len != sizeof(sb))) { + printk(KERN_ALERT "split_squashfs: error occured while reading " + "from \"%s\"\n", master->name); + return -EINVAL; + } + + if (SQUASHFS_MAGIC != le32_to_cpu(sb.s_magic) ) { + printk(KERN_ALERT "split_squashfs: no squashfs found in \"%s\"\n", + master->name); + *split_offset = 0; + return 0; + } + + if (le64_to_cpu((sb.bytes_used)) <= 0) { + printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n", + master->name); + *split_offset = 0; + return 0; + } + + len = (u32) le64_to_cpu(sb.bytes_used); + len += (offset & 0x000fffff); + len += (master->erasesize - 1); + len &= ~(master->erasesize - 1); + len -= (offset & 0x000fffff); + *split_offset = offset + len; + + return 0; +} + +static int split_rootfs_data(struct mtd_info *master, struct mtd_info *rpart, const struct mtd_partition *part) +{ + struct mtd_partition *dpart; + struct mtd_part *slave = NULL; + struct mtd_part *spart; + int ret, split_offset = 0; + + spart = PART(rpart); + ret = split_squashfs(master, spart->offset, &split_offset); + if (ret) + return ret; + + if (split_offset <= 0) + return 0; + + dpart = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL); + if (dpart == NULL) { + printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n", + ROOTFS_SPLIT_NAME); + return -ENOMEM; + } + + memcpy(dpart, part, sizeof(*part)); + dpart->name = (unsigned char *)&dpart[1]; + strcpy(dpart->name, ROOTFS_SPLIT_NAME); + + dpart->size = rpart->size - (split_offset - spart->offset); + dpart->offset = split_offset; + + if (dpart == NULL) + return 1; + + printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%llX, len=%llX \n", + ROOTFS_SPLIT_NAME, dpart->offset, dpart->size); + + slave = allocate_partition(master, dpart, 0, split_offset); + if (IS_ERR(slave)) + return PTR_ERR(slave); + mutex_lock(&mtd_partitions_mutex); + list_add(&slave->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); + + add_mtd_device(&slave->mtd); + + rpart->split = &slave->mtd; + + return 0; +} + +static int refresh_rootfs_split(struct mtd_info *mtd) +{ + struct mtd_partition tpart; + struct mtd_part *part; + char *name; + //int index = 0; + int offset, size; + int ret; + + part = PART(mtd); + + /* check for the new squashfs offset first */ + ret = split_squashfs(part->master, part->offset, &offset); + if (ret) + return ret; + + if ((offset > 0) && !mtd->split) { + printk(KERN_INFO "%s: creating new split partition for \"%s\"\n", __func__, mtd->name); + /* if we don't have a rootfs split partition, create a new one */ + tpart.name = (char *) mtd->name; + tpart.size = mtd->size; + tpart.offset = part->offset; + + return split_rootfs_data(part->master, &part->mtd, &tpart); + } else if ((offset > 0) && mtd->split) { + /* update the offsets of the existing partition */ + size = mtd->size + part->offset - offset; + + part = PART(mtd->split); + part->offset = offset; + part->mtd.size = size; + printk(KERN_INFO "%s: %s partition \"" ROOTFS_SPLIT_NAME "\", offset: 0x%06x (0x%06x)\n", + __func__, (!strcmp(part->mtd.name, ROOTFS_SPLIT_NAME) ? "updating" : "creating"), + (u32) part->offset, (u32) part->mtd.size); + name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL); + strcpy(name, ROOTFS_SPLIT_NAME); + part->mtd.name = name; + } else if ((offset <= 0) && mtd->split) { + printk(KERN_INFO "%s: removing partition \"%s\"\n", __func__, mtd->split->name); + + /* mark existing partition as removed */ + part = PART(mtd->split); + name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL); + strcpy(name, ROOTFS_REMOVED_NAME); + part->mtd.name = name; + part->offset = 0; + part->mtd.size = 0; + } + + return 0; +} +#endif /* CONFIG_MTD_ROOTFS_SPLIT */ + /* * This function, given a master MTD object and a partition table, creates * and registers slave MTD objects which are bound to the master according to @@ -666,6 +890,9 @@ int add_mtd_partitions(struct mtd_info *master, struct mtd_part *slave; uint64_t cur_offset = 0; int i; +#ifdef CONFIG_MTD_ROOTFS_SPLIT + int ret; +#endif printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); @@ -680,12 +907,53 @@ int add_mtd_partitions(struct mtd_info *master, add_mtd_device(&slave->mtd); + if (!strcmp(parts[i].name, "rootfs")) { +#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV + if (ROOT_DEV == 0) { + printk(KERN_NOTICE "mtd: partition \"rootfs\" " + "set to be root filesystem\n"); + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index); + } +#endif +#ifdef CONFIG_MTD_ROOTFS_SPLIT + ret = split_rootfs_data(master, &slave->mtd, &parts[i]); + /* if (ret == 0) + * j++; */ +#endif + } + cur_offset = slave->offset + slave->mtd.size; } return 0; } +int mtd_device_refresh(struct mtd_info *mtd) +{ + int ret = 0; + + if (IS_PART(mtd)) { + struct mtd_part *part; + struct mtd_info *master; + + part = PART(mtd); + master = part->master; + if (master->refresh_device) + ret = master->refresh_device(master); + } + + if (!ret && mtd->refresh_device) + ret = mtd->refresh_device(mtd); + +#ifdef CONFIG_MTD_ROOTFS_SPLIT + if (!ret && IS_PART(mtd) && !strcmp(mtd->name, "rootfs")) + refresh_rootfs_split(mtd); +#endif + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_device_refresh); + static DEFINE_SPINLOCK(part_parser_lock); static LIST_HEAD(part_parsers); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index cce7b70..e7a9446d 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -46,6 +46,18 @@ config MTD_NAND_ECC_BCH ECC codes. They are used with NAND devices requiring more than 1 bit of error correction. +config NAND_COMCERTO_ECC_8_HW_BCH + bool + depends on MTD_NAND_COMCERTO + default n + prompt "Comcerto ECC-8 syndrome calculation using BCH" + +config NAND_COMCERTO_ECC_24_HW_BCH + bool + depends on MTD_NAND_COMCERTO + default y + prompt "Comcerto ECC-24 syndrome calculation using BCH" + config MTD_SM_COMMON tristate default n @@ -537,4 +549,10 @@ config MTD_NAND_FSMC Enables support for NAND Flash chips on the ST Microelectronics Flexible Static Memory Controller (FSMC) +config MTD_NAND_COMCERTO + tristate "Comcerto NAND driver" + depends on MTD_NAND && (ARCH_COMCERTO) + help + Comcerto NAND Driver. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 618f4ba..0763907 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_COMCERTO) += comcerto_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 46ed296..b183184 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3271,6 +3271,9 @@ int nand_scan_tail(struct mtd_info *mtd) case 128: chip->ecc.layout = &nand_oob_128; break; + case 224: + chip->ecc.layout = &nand_oob_128; + break; default: pr_warn("No oob scheme defined for oobsize %d\n", mtd->oobsize); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index a702443..cd082ad 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -19,6 +19,11 @@ config MARVELL_PHY tristate "Drivers for Marvell PHYs" ---help--- Currently has a driver for the 88E1011S + +config ATHEROS_PHY + tristate "Drivers for Atheros PHYs" + ---help--- + Currently supports AR8035 and AR8327 config DAVICOM_PHY tristate "Drivers for Davicom PHYs" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 2333215..a00faff 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -4,6 +4,7 @@ libphy-objs := phy.o phy_device.o mdio_bus.o obj-$(CONFIG_PHYLIB) += libphy.o obj-$(CONFIG_MARVELL_PHY) += marvell.o +obj-$(CONFIG_ATHEROS_PHY) += atheros.o obj-$(CONFIG_DAVICOM_PHY) += davicom.o obj-$(CONFIG_CICADA_PHY) += cicada.o obj-$(CONFIG_LXT_PHY) += lxt.o diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index e8be47d..6dac7baf 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -273,6 +273,42 @@ static int bcm50610_a0_workaround(struct phy_device *phydev) return err; } + +#define CONFIG_WD +#ifdef CONFIG_WD +// 5/12/2012 ShaunA - added support for BCM54610 PHY +static int bcm54610_config_init(struct phy_device *phydev) +{ + int regb, rega; + + phy_write(phydev, 0x1C, 0x2C00); + regb = phy_read(phydev, 0x1C); + + phy_write(phydev, 0x1C, 0xAC8C); + + phy_write(phydev, 0x1C, 0x2C00); + rega = phy_read(phydev, 0x1C); + + printk(KERN_INFO "%s: before 0x%04x, after 0x%04x\n", + __FUNCTION__, (regb & 0xffff), (rega & 0xffff)); + + /* the RGMII interface is not half-duplex capable */ + rega = phy_read(phydev, 0x04); + phy_write(phydev, 0x04, rega & ~0x00a0); + + regb = phy_read(phydev, 0x09); + phy_write(phydev, 0x09, regb & ~0x0100); + + printk(KERN_INFO "%s: before 0x%04x, 0x%04x; after 0x%04x, 0x%04x\n", + __FUNCTION__, (rega & 0xffff), (regb & 0xffff), + (phy_read(phydev, 0x04) & 0xffff), (phy_read(phydev, 0x09) & 0xffff)); + + return 0; +} + +#endif + + static int bcm54xx_phydsp_config(struct phy_device *phydev) { int err, err2; @@ -849,10 +885,52 @@ static struct phy_driver bcm5241_driver = { .driver = { .owner = THIS_MODULE }, }; +// 5/12/2012 ShaunA +static struct phy_driver bcm54610_driver = { + .phy_id = PHY_ID_BCM54610, + .phy_id_mask = 0xffffffff, + .name = "Broadcom BCM54610", + .features = PHY_GBIT_FEATURES | + SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, + .config_init = bcm54610_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm54xx_ack_interrupt, + .config_intr = bcm54xx_config_intr, + .driver = { .owner = THIS_MODULE }, +}; + +// JO... +static struct phy_driver bcm54612_driver = { + .phy_id = PHY_ID_BCM54612, + .phy_id_mask = 0xffffffff, + .name = "Broadcom BCM54612", + .features = PHY_GBIT_FEATURES | + SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, + .config_init = bcm54610_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm54xx_ack_interrupt, + .config_intr = bcm54xx_config_intr, + .driver = { .owner = THIS_MODULE }, +}; + static int __init broadcom_init(void) { int ret; +// JCO JO... + ret = phy_driver_register(&bcm54612_driver); + if (ret) + goto out_54612; + +// ShaunA + ret = phy_driver_register(&bcm54610_driver); + if (ret) + goto out_54610; + ret = phy_driver_register(&bcm5411_driver); if (ret) goto out_5411; @@ -909,11 +987,17 @@ out_5461: out_5421: phy_driver_unregister(&bcm5411_driver); out_5411: + phy_driver_unregister(&bcm54610_driver); +out_54610: + phy_driver_unregister(&bcm54612_driver); +out_54612: return ret; } static void __exit broadcom_exit(void) { + phy_driver_unregister(&bcm54610_driver); + phy_driver_unregister(&bcm54612_driver); phy_driver_unregister(&bcm5241_driver); phy_driver_unregister(&bcmac131_driver); phy_driver_unregister(&bcm57780_driver); diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 4782d79..5d11393 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -53,6 +53,9 @@ #include #include #include +#if defined(CONFIG_COMCERTO_FP) +#include +#endif #define PPP_VERSION "2.4.2" @@ -559,6 +562,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct ppp *ppp; int err = -EFAULT, val, val2, i; struct ppp_idle idle; +#if defined(CONFIG_COMCERTO_FP) + struct ppp_idle fppidle; +#endif struct npioctl npi; int unit, cflags; struct slcompress *vj; @@ -738,6 +744,30 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = 0; break; +#if defined(CONFIG_COMCERTO_FP) + case PPPIOCSFPPIDLE: + if (copy_from_user(&fppidle, argp, sizeof(fppidle))) + break; + + ppp_xmit_lock(ppp); + + if (time_after((jiffies - (fppidle.xmit_idle * HZ)) , ppp->last_xmit)) + ppp->last_xmit = (jiffies - fppidle.xmit_idle * HZ); + + ppp_xmit_unlock(ppp); + + ppp_recv_lock(ppp); + + if (time_after((jiffies - (fppidle.recv_idle * HZ)) , ppp->last_recv)) + ppp->last_recv = (jiffies - fppidle.recv_idle * HZ); + + ppp_recv_unlock(ppp); + + err = 0; + break; +#endif + + #ifdef CONFIG_PPP_FILTER case PPPIOCSPASS: { @@ -2825,8 +2855,10 @@ ppp_connect_channel(struct channel *pch, int unit) goto out; write_lock_bh(&pch->upl); ret = -EINVAL; - if (pch->ppp) - goto outl; + if (pch->ppp) { + write_unlock_bh(&pch->upl); + goto out; + } ppp_lock(ppp); if (pch->file.hdrlen > ppp->file.hdrlen) @@ -2839,10 +2871,18 @@ ppp_connect_channel(struct channel *pch, int unit) pch->ppp = ppp; atomic_inc(&ppp->file.refcnt); ppp_unlock(ppp); - ret = 0; - outl: write_unlock_bh(&pch->upl); + +#if defined(CONFIG_COMCERTO_FP) + if ((ppp->dev) && (!ppp->closing)) { + rtnl_lock(); + rtmsg_ifinfo(RTM_NEWLINK, ppp->dev, 0); + rtnl_unlock(); + } +#endif + + ret = 0; out: mutex_unlock(&pn->all_ppp_mutex); return ret; @@ -2868,6 +2908,15 @@ ppp_disconnect_channel(struct channel *pch) if (--ppp->n_channels == 0) wake_up_interruptible(&ppp->file.rwait); ppp_unlock(ppp); + +#if defined(CONFIG_COMCERTO_FP) + if ((ppp->dev) && (!ppp->closing)) { + rtnl_lock(); + rtmsg_ifinfo(RTM_NEWLINK, ppp->dev, 0); + rtnl_unlock(); + } +#endif + if (atomic_dec_and_test(&ppp->file.refcnt)) ppp_destroy_interface(ppp); err = 0; diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 2f319d1..b7d143f 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -856,7 +856,7 @@ static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, goto end; - skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32, + skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32 + NET_SKB_PAD, 0, GFP_KERNEL); if (!skb) { error = -ENOMEM; @@ -864,7 +864,7 @@ static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, } /* Reserve space for headers. */ - skb_reserve(skb, dev->hard_header_len); + skb_reserve(skb, dev->hard_header_len + NET_SKB_PAD); skb_reset_network_header(skb); skb->dev = dev; @@ -940,8 +940,16 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) skb->protocol = cpu_to_be16(ETH_P_PPP_SES); skb->dev = dev; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if((skb->ipsec_offload == 1) && (!skb->sp)) + { + dev_hard_header(skb, dev, ETH_P_PPP_SES, + dev->dev_addr, po->pppoe_pa.remote, data_len); - dev_hard_header(skb, dev, ETH_P_PPP_SES, + } + else +#endif + dev_hard_header(skb, dev, ETH_P_PPP_SES, po->pppoe_pa.remote, NULL, data_len); dev_queue_xmit(skb); @@ -1002,17 +1010,33 @@ static int pppoe_seq_show(struct seq_file *seq, void *v) { struct pppox_sock *po; char *dev_name; +#if defined(CONFIG_COMCERTO_FP) + char *ppp_name; +#endif if (v == SEQ_START_TOKEN) { - seq_puts(seq, "Id Address Device\n"); +#if defined(CONFIG_COMCERTO_FP) + seq_puts(seq, "Id Address Device PPPDevice\n"); +#else + seq_puts(seq, "Id Address Device\n"); +#endif goto out; } po = v; dev_name = po->pppoe_pa.dev; - seq_printf(seq, "%08X %pM %8s\n", - po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name); +#if defined(CONFIG_COMCERTO_FP) + ppp_name = ppp_dev_name(&po->chan); + if (!ppp_name) + goto out; + + seq_printf(seq, "%04X %pM %8s %s\n", + ntohs(po->pppoe_pa.sid), po->pppoe_pa.remote, dev_name, ppp_name); +#else + seq_printf(seq, "%08X %pM %8s\n", + po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name); +#endif out: return 0; } diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 53eb4e5..c76b916 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -762,6 +762,13 @@ config RTC_DRV_S3C This driver can also be build as a module. If so, the module will be called rtc-s3c. +config RTC_DRV_C2K + tristate "Mindspeed C2000 SoC RTC" + depends on ARCH_M86XXX + default y + help + Comcerto C2000 RTC (Realtime Clock) driver. + config RTC_DRV_EP93XX tristate "Cirrus Logic EP93XX" depends on ARCH_EP93XX diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6e69823..fae32d3 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o +obj-$(CONFIG_RTC_DRV_C2K) += rtc-c2k.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 88bc82e..2971c92 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -461,8 +461,8 @@ static DEVICE_ATTR(field, S_IRUGO, sdev_show_##field, NULL); * sdev_rw_attr: create a function and attribute variable for a * read/write field. */ -#define sdev_rw_attr(field, format_string) \ - sdev_show_function(field, format_string) \ +#define sdev_rw_attr(field, r_format_string, w_format_string) \ + sdev_show_function(field, r_format_string) \ \ static ssize_t \ sdev_store_##field (struct device *dev, struct device_attribute *attr, \ @@ -470,7 +470,7 @@ sdev_store_##field (struct device *dev, struct device_attribute *attr, \ { \ struct scsi_device *sdev; \ sdev = to_scsi_device(dev); \ - sscanf (buf, format_string, &sdev->field); \ + sscanf (buf, w_format_string, &sdev->field); \ return count; \ } \ static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, sdev_show_##field, sdev_store_##field); @@ -524,7 +524,7 @@ static int scsi_sdev_check_buf_bit(const char *buf) sdev_rd_attr (device_blocked, "%d\n"); sdev_rd_attr (queue_depth, "%d\n"); sdev_rd_attr (type, "%d\n"); -sdev_rd_attr (scsi_level, "%d\n"); +sdev_rw_attr (scsi_level, "%d\n", "%hhd\n"); sdev_rd_attr (vendor, "%.8s\n"); sdev_rd_attr (model, "%.16s\n"); sdev_rd_attr (rev, "%.4s\n"); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7587796..7aea2c0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -373,6 +373,20 @@ config SPI_XILINX Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" +config SPI_MSPD_LOW_SPEED + tristate "MSPD SPI low-speed controller module" + select SPI_DESIGNWARE + default SPI_MASTER + help + This exposes the MSPD SPI low-speed controller. + +config SPI_MSPD_HIGH_SPEED + tristate "MSPD SPI high-speed controller module" + select SPI_DESIGNWARE + default SPI_MASTER + help + This exposes the MSPD SPI high-speed controller. + config SPI_NUC900 tristate "Nuvoton NUC900 series SPI" depends on ARCH_W90X900 && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 61c3261..41c6530 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,8 +19,26 @@ obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o + +SPI_MSPD=n +ifeq ($(CONFIG_SPI_MSPD_HIGH_SPEED),m) + SPI_MSPD=m +endif +ifeq ($(CONFIG_SPI_MSPD_LOW_SPEED),m) + SPI_MSPD=m +endif +ifeq ($(CONFIG_SPI_MSPD_HIGH_SPEED),y) + SPI_MSPD=y +endif +ifeq ($(CONFIG_SPI_MSPD_LOW_SPEED),y) + SPI_MSPD=y +endif +#obj-$(SPI_MSPD) += spi-c2000-dma.o +#obj-$(SPI_MSPD) += spi-c2000.o + obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o -obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o +obj-$(CONFIG_SPI_DESIGNWARE) += designware_spi.o +#obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index bbdf0cf..642a5ec 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -24,6 +24,12 @@ #include #include #include +#include + +#ifdef CONFIG_C2K_DEVFREQ_DW + #include + #include +#endif #include "spi-dw.h" @@ -62,6 +68,48 @@ struct chip_data { void (*cs_control)(u32 command); }; +#ifdef CONFIG_C2K_DEVFREQ_DW +static devfreq_counters dc; + +static int set_spi_freq(struct c2k_devfreq_data *data, unsigned long *freq) +{ + struct spi_device *spi = container_of(data->dev, struct spi_device, dev); + struct dw_spi *dws = container_of(&spi, struct dw_spi, cur_dev); + struct chip_data *chip; + u32 clk_div; + + /* Only alloc on first setup */ + chip = spi_get_ctldata(spi); + + if (!chip) + { + chip = spi_get_ctldata(spi); + if (!chip) { + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + } + } + + if ((*freq <= data->max_freq) && (*freq >= data->min_freq)) + { + chip->speed_hz = (u32)*freq; + clk_div = dws->max_freq / chip->speed_hz; + clk_div = (clk_div + 1) & 0xfffe; + + chip->clk_div = clk_div; + spi_set_clk(dws, chip->clk_div); + } + else + { + printk (KERN_ERR "%s: Trying to set out of range spi freq: %lu\n\ + ", __func__, *freq); + } + + return 0; +} +#endif + #ifdef CONFIG_DEBUG_FS static int spi_show_regs_open(struct inode *inode, struct file *file) { @@ -394,6 +442,10 @@ static void pump_transfers(unsigned long data) u32 speed = 0; u32 cr0 = 0; +#ifdef CONFIG_C2K_DEVFREQ_DW + devfreq_func_start(&dc); +#endif + /* Get current state information */ message = dws->cur_msg; transfer = dws->cur_transfer; @@ -545,6 +597,10 @@ static void pump_transfers(unsigned long data) if (chip->poll_mode) poll_transfer(dws); +#ifdef CONFIG_C2K_DEVFREQ_DW + devfreq_func_end(&dc); +#endif + return; early_exit: @@ -595,6 +651,9 @@ static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg) { struct dw_spi *dws = spi_master_get_devdata(spi->master); unsigned long flags; +#ifdef CONFIG_C2K_DEVFREQ_DW + devfreq_func_start(&dc); +#endif spin_lock_irqsave(&dws->lock, flags); @@ -623,6 +682,9 @@ static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg) } spin_unlock_irqrestore(&dws->lock, flags); +#ifdef CONFIG_C2K_DEVFREQ_DW + devfreq_func_end(&dc); +#endif return 0; } @@ -797,6 +859,49 @@ static void spi_hw_init(struct dw_spi *dws) } } +#ifdef CONFIG_C2K_DEVFREQ_DW +#define SPI_MAXFREQ_MHZ 2000000 +#define SPI_MINFREQ_MHZ 1000000 +#define POLLING_MS 1000 +#define OPP_TABLE_SIZE 4 + +struct devfreq_dev_profile spi_devfreq_profile; +struct c2k_devfreq_data devfreq_spi_data; +static struct c2k_devfreq_opp_table spi_opp_tbl[OPP_TABLE_SIZE]; + +/* + * intialize OPP table, profile data (initial freq, polling interval), + * max/min freq supported by SPI controller. + */ +static void init_spi_devfreq_data(struct dw_spi *dws) +{ + int i = 0; + struct c2k_devfreq_opp_table opp_tbl[OPP_TABLE_SIZE] = { + {1, 1000000, 0}, + {2, 2000000, 0}, + {3, 4000000, 0}, + {0, 0, 0}, + }; + + while (i < sizeof(opp_tbl)) + { + spi_opp_tbl[i].idx = opp_tbl[i].idx; + spi_opp_tbl[i].freq = opp_tbl[i].freq; + spi_opp_tbl[i].volt = opp_tbl[i].volt; + i++; + } + + spi_devfreq_profile.initial_freq = dws->max_freq; + spi_devfreq_profile.polling_ms = POLLING_MS; + + devfreq_spi_data.devfreq_profile = &spi_devfreq_profile; + devfreq_spi_data.opp_table = &spi_opp_tbl[0]; + devfreq_spi_data.set_freq = set_spi_freq; + devfreq_spi_data.max_freq = SPI_MAXFREQ_MHZ; + devfreq_spi_data.min_freq = SPI_MINFREQ_MHZ; +} +#endif + int __devinit dw_spi_add_host(struct dw_spi *dws) { struct spi_master *master; @@ -810,6 +915,8 @@ int __devinit dw_spi_add_host(struct dw_spi *dws) goto exit; } + clk_enable(dws->clk_spi); + dws->master = master; dws->type = SSI_MOTO_SPI; dws->prev_chip = NULL; @@ -863,6 +970,14 @@ int __devinit dw_spi_add_host(struct dw_spi *dws) } mrst_spi_debugfs_init(dws); + +#ifdef CONFIG_C2K_DEVFREQ_DW + init_spi_devfreq_data(dws); + + ret = c2k_driver_devfreq(&master->dev, &devfreq_spi_data); + if(ret < 0) + goto err_queue_alloc; +#endif return 0; err_queue_alloc: @@ -873,6 +988,7 @@ err_diable_hw: spi_enable_chip(dws, 0); free_irq(dws->irq, dws); err_free_master: + clk_disable(dws->clk_spi); spi_master_put(master); exit: return ret; @@ -896,12 +1012,12 @@ void __devexit dw_spi_remove_host(struct dw_spi *dws) if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); spi_enable_chip(dws, 0); - /* Disable clk */ - spi_set_clk(dws, 0); free_irq(dws->irq, dws); /* Disconnect from the SPI framework */ spi_unregister_master(dws->master); + /* Disable clk */ + clk_disable(dws->clk_spi); } EXPORT_SYMBOL_GPL(dw_spi_remove_host); @@ -913,7 +1029,9 @@ int dw_spi_suspend_host(struct dw_spi *dws) if (ret) return ret; spi_enable_chip(dws, 0); - spi_set_clk(dws, 0); + + clk_disable(dws->clk_spi); + return ret; } EXPORT_SYMBOL_GPL(dw_spi_suspend_host); @@ -922,6 +1040,7 @@ int dw_spi_resume_host(struct dw_spi *dws) { int ret; + clk_enable(dws->clk_spi); spi_hw_init(dws); ret = start_queue(dws); if (ret) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 9c57c07..b4bd29b 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -92,6 +92,7 @@ struct dw_spi_dma_ops { struct dw_spi { struct spi_master *master; struct spi_device *cur_dev; + struct clk *clk_spi; struct device *parent_dev; enum dw_ssi_type type; char name[16]; diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 830cd62..bfb5415 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -1,6 +1,6 @@ config VT bool "Virtual terminal" if EXPERT - depends on !S390 && !UML + depends on !S390 && !UML && !ARCH_COMCERTO select INPUT default y ---help--- diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 33601f8..35d82e0 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -48,6 +48,12 @@ #include "suncore.h" #endif +#ifdef CONFIG_ARCH_M86XXX +#include +#include +static struct clk *uart_clk; /*UART Clock(DUS) depends upon the AXI*/ +#endif + /* * Configuration: * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option @@ -3091,6 +3097,31 @@ static int __devinit serial8250_probe(struct platform_device *dev) struct uart_port port; int ret, i, irqflag = 0; +#ifdef CONFIG_ARCH_M86XXX + unsigned long uart_rate; + + /* Take the Fast-UART device Out-Of-Reset*/ + c2000_block_reset(COMPONENT_AXI_FAST_UART,0); + + /* Get the FAST-UART clk structure from DUS */ + uart_clk = clk_get(NULL,"DUS"); + + if (IS_ERR(uart_clk)) { + pr_err("%s: Unable to get UART clock: %ld\n",__func__,PTR_ERR(uart_clk)); + return PTR_ERR(uart_clk); + } + + /* Enable the FAST-UART Clock */ + ret = clk_enable(uart_clk); + if (ret){ + pr_err("%s: UART clock failed to enable:\n",__func__); + return ret; + } + + /* Get the UART Clock in Hz */ + uart_rate = clk_get_rate(uart_clk); +#endif + memset(&port, 0, sizeof(struct uart_port)); if (share_irqs) @@ -3101,7 +3132,11 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.membase = p->membase; port.irq = p->irq; port.irqflags = p->irqflags; - port.uartclk = p->uartclk; +#ifdef CONFIG_ARCH_M86XXX + port.uartclk = uart_rate; /* Assigning the rate value to the ports */ +#else + port.uartclk = p->uartclk; /* Assigning the rate value got from Platfrom device*/ +#endif port.regshift = p->regshift; port.iotype = p->iotype; port.flags = p->flags; @@ -3124,6 +3159,7 @@ static int __devinit serial8250_probe(struct platform_device *dev) p->irq, ret); } } + return 0; } @@ -3140,9 +3176,19 @@ static int __devexit serial8250_remove(struct platform_device *dev) if (up->port.dev == &dev->dev) serial8250_unregister_port(i); } +#ifdef CONFIG_ARCH_M86XXX + /*Disable the Fast-UART clock here*/ + clk_disable(uart_clk); + clk_put(uart_clk); + + /* Put the Fast-UART device in Reset*/ + c2000_block_reset(COMPONENT_AXI_FAST_UART,1); +#endif + return 0; } +#if CONFIG_PM static int serial8250_suspend(struct platform_device *dev, pm_message_t state) { int i; @@ -3154,6 +3200,16 @@ static int serial8250_suspend(struct platform_device *dev, pm_message_t state) uart_suspend_port(&serial8250_reg, &up->port); } +#ifdef CONFIG_ARCH_M86XXX + /* Now Do the FAST_UART_CLOCK gating here, be sure no other devices + * are using the DUS clock to shutdown the clock. + * Here above clock is derived from DUS , henece it will be not + * gated , unless and until DMA/FAST-SPI will disable the DUS clock + * to make the usecount 0. + */ + clk_disable(uart_clk); +#endif + return 0; } @@ -3161,6 +3217,20 @@ static int serial8250_resume(struct platform_device *dev) { int i; +#ifdef CONFIG_ARCH_M86XXX + /* Now Enable the FAST_UART_CLOCK here , before + * before resuming any opertions. + */ + if (clk_enable(uart_clk)){ + pr_err("%s: Unable to enable FAST-UART clock: \n",__func__); + /* Here we are not able to enable the FAST-UART clock , + * beacause of clk_disable unable to shutdown( usecount is + * not zero ,due to dependancy with DMA and FAST-SPI) clock + * so Let resume the port only . + */ + } +#endif + for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; @@ -3170,12 +3240,15 @@ static int serial8250_resume(struct platform_device *dev) return 0; } +#endif static struct platform_driver serial8250_isa_driver = { .probe = serial8250_probe, .remove = __devexit_p(serial8250_remove), +#if CONFIG_PM .suspend = serial8250_suspend, .resume = serial8250_resume, +#endif .driver = { .name = "serial8250", .owner = THIS_MODULE, diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 791f11b..d7f0d95 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -80,7 +80,8 @@ config USB_ARCH_HAS_EHCI # some non-PCI HCDs implement xHCI config USB_ARCH_HAS_XHCI boolean - default PCI + default y +# default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB @@ -181,4 +182,6 @@ source "drivers/usb/gadget/Kconfig" source "drivers/usb/otg/Kconfig" +source "drivers/usb/dwc_otg/Kconfig" + endif # USB_SUPPORT diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 75eca76..ff19eba 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -49,6 +49,8 @@ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ +obj-$(CONFIG_DWC_OTG) += dwc_otg/ + obj-$(CONFIG_USB_MUSB_HDRC) += musb/ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_OTG_UTILS) += otg/ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 73cd900..ed7d797 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -652,6 +652,71 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size, } EXPORT_SYMBOL_GPL(__usb_get_extra_descriptor); +static struct usb_device *match_device_name(struct usb_device *dev, + const char *name) +{ + struct usb_device *ret_dev = NULL; + int child; + + dev_dbg(&dev->dev, "check for name %s ...\n", name); + + /* see if this device matches */ + if (strcmp(dev_name(&dev->dev), name) == 0 ) { + dev_dbg(&dev->dev, "matched this device!\n"); + ret_dev = usb_get_dev(dev); + goto exit; + } + + /* look through all of the children of this device */ + for (child = 0; child < dev->maxchild; ++child) { + if (dev->children[child]) { + usb_lock_device(dev->children[child]); + ret_dev = match_device_name(dev->children[child], name); + usb_unlock_device(dev->children[child]); + if (ret_dev) + goto exit; + } + } +exit: + return ret_dev; +} + +/** + * usb_find_device_by_name - find a specific usb device in the system + * @name: the name of the device to find + * + * Returns a pointer to a struct usb_device if such a specified usb + * device is present in the system currently. The usage count of the + * device will be incremented if a device is found. Make sure to call + * usb_put_dev() when the caller is finished with the device. + * + * If a device with the specified bus id is not found, NULL is returned. + */ +struct usb_device *usb_find_device_by_name(const char *name) +{ + struct list_head *buslist; + struct usb_bus *bus; + struct usb_device *dev = NULL; + + mutex_lock(&usb_bus_list_lock); + for (buslist = usb_bus_list.next; + buslist != &usb_bus_list; + buslist = buslist->next) { + bus = container_of(buslist, struct usb_bus, bus_list); + if (!bus->root_hub) + continue; + usb_lock_device(bus->root_hub); + dev = match_device_name(bus->root_hub, name); + usb_unlock_device(bus->root_hub); + if (dev) + goto exit; + } +exit: + mutex_unlock(&usb_bus_list_lock); + return dev; +} +EXPORT_SYMBOL_GPL(usb_find_device_by_name); + /** * usb_alloc_coherent - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP * @dev: device the buffer will be used with diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 060e0e2..5d2d92d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -27,6 +27,11 @@ config USB_XHCI_HCD To compile this driver as a module, choose M here: the module will be called xhci-hcd. +config USB_XHCI_PLATFORM + tristate + depends on USB_XHCI_HCD + default y + config USB_XHCI_HCD_DEBUGGING bool "Debugging for the xHCI host controller" depends on USB_XHCI_HCD diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 7ca290f..383fc37 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -15,6 +15,10 @@ xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o xhci-hcd-$(CONFIG_PCI) += xhci-pci.o +ifneq ($(CONFIG_USB_XHCI_PLATFORM), ) + xhci-hcd-y += xhci-plat.o xhci-comcerto2000.o +endif + obj-$(CONFIG_USB_WHCI_HCD) += whci/ obj-$(CONFIG_PCI) += pci-quirks.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2abdad6..aed012c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -756,7 +756,7 @@ static int ehci_run (struct usb_hcd *hcd) "USB %x.%x started, EHCI %x.%02x%s\n", ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), temp >> 8, temp & 0xff, - ignore_oc ? ", overcurrent ignored" : ""); + (ignore_oc || ehci->ignore_oc) ? ", overcurrent ignored" : ""); ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4527b90..f4371f33 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -598,7 +598,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) * always set, seem to clear PORT_OCC and PORT_CSC when writing to * PORT_POWER; that's surprising, but maybe within-spec. */ - if (!ignore_oc) + if (!ignore_oc && !ehci->ignore_oc) mask = PORT_CSC | PORT_PEC | PORT_OCC; else mask = PORT_CSC | PORT_PEC; @@ -823,7 +823,7 @@ static int ehci_hub_control ( if (temp & PORT_PEC) status |= USB_PORT_STAT_C_ENABLE << 16; - if ((temp & PORT_OCC) && !ignore_oc){ + if ((temp & PORT_OCC) && (!ignore_oc && !ehci->ignore_oc)){ status |= USB_PORT_STAT_C_OVERCURRENT << 16; /* diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index b65912d..386f23d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -148,6 +148,7 @@ struct ehci_hcd { /* one per controller */ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ unsigned imx28_write_fix:1; /* For Freescale i.MX28 */ + unsigned ignore_oc:1; /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index e9b0f04..4b436f5 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -119,7 +119,7 @@ static void xhci_print_command_reg(struct xhci_hcd *xhci) xhci_dbg(xhci, " Event Interrupts %s\n", (temp & CMD_EIE) ? "enabled " : "disabled"); xhci_dbg(xhci, " Host System Error Interrupts %s\n", - (temp & CMD_EIE) ? "enabled " : "disabled"); + (temp & CMD_HSEIE) ? "enabled " : "disabled"); xhci_dbg(xhci, " HC has %sfinished light reset\n", (temp & CMD_LRESET) ? "not " : ""); } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a8bbeed..4a7f815 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -57,10 +57,8 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; - /* Ugh, these should be #defines, FIXME */ - /* Using table 11-13 in USB 2.0 spec. */ temp = 0; - /* Bits 1:0 - support port power switching, or power always on */ + /* Bits 1:0 - support per-port power switching, or power always on */ if (HCC_PPC(xhci->hcc_params)) temp |= 0x0001; else @@ -86,9 +84,9 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, ports = xhci->num_usb2_ports; xhci_common_hub_descriptor(xhci, desc, ports); - desc->bDescriptorType = 0x29; + desc->bDescriptorType = USB_DT_HUB; temp = 1 + (ports / 8); - desc->bDescLength = 7 + 2 * temp; + desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * temp; /* The Device Removable bits are reported on a byte granularity. * If the port doesn't exist within that byte, the bit is set to 0. @@ -137,8 +135,8 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, ports = xhci->num_usb3_ports; xhci_common_hub_descriptor(xhci, desc, ports); - desc->bDescriptorType = 0x2a; - desc->bDescLength = 12; + desc->bDescriptorType = USB_DT_SS_HUB; + desc->bDescLength = USB_DT_SS_HUB_SIZE; /* header decode latency should be zero for roothubs, * see section 4.23.5.2. @@ -424,6 +422,34 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, xhci_writel(xhci, temp, port_array[port_id]); } +#if 0 +void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, + __le32 __iomem **port_array, int port_id, u16 wake_mask) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + temp = xhci_port_state_to_neutral(temp); + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) + temp |= PORT_WKCONN_E; + else + temp &= ~PORT_WKCONN_E; + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT) + temp |= PORT_WKDISC_E; + else + temp &= ~PORT_WKDISC_E; + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT) + temp |= PORT_WKOC_E; + else + temp &= ~PORT_WKOC_E; + + xhci_writel(xhci, temp, port_array[port_id]); +} +#endif + /* Test and clear port RWC bit */ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, int port_id, u32 port_bit) @@ -525,6 +551,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, int slot_id; struct xhci_bus_state *bus_state; u16 link_state = 0; + u16 wake_mask = 0; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -671,6 +698,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case SetPortFeature: if (wValue == USB_PORT_FEAT_LINK_STATE) link_state = (wIndex & 0xff00) >> 3; + if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK) + wake_mask = wIndex & 0xff00; wIndex &= 0xff; if (!wIndex || wIndex > max_ports) goto error; @@ -808,6 +837,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; +#if 0 + case USB_PORT_FEAT_REMOTE_WAKE_MASK: + xhci_set_remote_wake_mask(xhci, port_array, + wIndex, wake_mask); + temp = xhci_readl(xhci, port_array[wIndex]); + xhci_dbg(xhci, "set port remote wake mask, " + "actual port %d status = 0x%x\n", + wIndex, temp); + break; +#endif case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; xhci_writel(xhci, temp, port_array[wIndex]); @@ -995,6 +1034,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd) t2 |= PORT_LINK_STROBE | XDEV_U3; set_bit(port_index, &bus_state->bus_suspended); } + /* USB core sets remote wake mask for USB 3.0 hubs, + * including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND + * is enabled, so also enable remote wake here. + */ if (hcd->self.root_hub->do_remote_wakeup) { if (t1 & PORT_CONNECT) { t2 |= PORT_WKOC_E | PORT_WKDISC_E; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 03996da..5a26dc9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -248,28 +248,7 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (ret) goto fail; - prev = ring->first_seg; - while (num_segs > 0) { - struct xhci_segment *next; - - next = xhci_segment_alloc(xhci, flags); - if (!next) { - prev = ring->first_seg; - while (prev) { - next = prev->next; - xhci_segment_free(xhci, prev); - prev = next; - } - goto fail; - } - xhci_link_segments(xhci, prev, next, link_trbs, isoc); - - prev = next; - num_segs--; - } - xhci_link_segments(xhci, prev, ring->first_seg, link_trbs, isoc); - - if (link_trbs) { + /* Only event ring does not use link TRB */ if (type != TYPE_EVENT) { /* See section 4.9.2.1 and 6.4.4.1 */ ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index a464dbc..8b0d37e 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -362,7 +362,7 @@ int __init xhci_register_pci(void) return pci_register_driver(&xhci_pci_driver); } -void __exit xhci_unregister_pci(void) +void xhci_unregister_pci(void) { pci_unregister_driver(&xhci_pci_driver); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5fece6b..4cda0a7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -149,6 +149,11 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) ring->deq_updates++; + /* If this is not event ring, there is one more usable TRB */ + if (ring->type != TYPE_EVENT && + !last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) + ring->num_trbs_free++; + do { /* * Update the dequeue pointer further if that was a link TRB or @@ -156,14 +161,8 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) * link TRBS) */ if (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) { - if (consumer && last_trb_on_last_seg(xhci, ring, + if (ring->type == TYPE_EVENT && last_trb_on_last_seg(xhci, ring, ring->deq_seg, ring->dequeue)) { - if (!in_interrupt()) - xhci_dbg(xhci, "Toggle cycle state " - "for ring %p = %i\n", - ring, - (unsigned int) - ring->cycle_state); ring->cycle_state = (ring->cycle_state ? 0 : 1); } ring->deq_seg = ring->deq_seg->next; @@ -1388,7 +1387,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, */ if (handle_stopped_cmd_ring(xhci, GET_COMP_CODE(le32_to_cpu(event->status)))) { - inc_deq(xhci, xhci->cmd_ring, false); + inc_deq(xhci, xhci->cmd_ring); return; } } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0770fb9..6fe5d76 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -344,7 +344,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) * generate interrupts. Don't even try to enable MSI. */ if (xhci->quirks & XHCI_BROKEN_MSI) - goto legacy_irq; + return 0; /* unregister the legacy interrupt */ if (hcd->irq) @@ -365,7 +365,6 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) return -EINVAL; } - legacy_irq: /* fall back to legacy interrupt*/ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); @@ -1550,6 +1549,9 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; } + xhci_dbg(xhci, "Cancel URB %p\n", urb); + xhci_dbg(xhci, "Event ring:\n"); + xhci_debug_ring(xhci, xhci->event_ring); ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index]; ep_ring = xhci_urb_to_transfer_ring(xhci, urb); @@ -1558,6 +1560,9 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; } + xhci_dbg(xhci, "Endpoint ring:\n"); + xhci_debug_ring(xhci, ep_ring); + urb_priv = urb->hcpriv; i = urb_priv->td_cnt; if (i < urb_priv->length) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 2090a03..a3c9f42 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1233,10 +1233,7 @@ union xhci_trb { /* Allow two commands + a link TRB, along with any reserved command TRBs */ #define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) #define SEGMENT_SIZE (TRBS_PER_SEGMENT*16) -/* SEGMENT_SHIFT should be log2(SEGMENT_SIZE). - * Change this if you change TRBS_PER_SEGMENT! - */ -#define SEGMENT_SHIFT 10 +#define SEGMENT_SHIFT (__ffs(SEGMENT_SIZE)) /* TRB buffer pointers can't cross 64KB boundaries */ #define TRB_MAX_BUFF_SHIFT 16 #define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT) @@ -1273,8 +1270,19 @@ struct xhci_dequeue_state { int new_cycle_state; }; +enum xhci_ring_type { + TYPE_CTRL = 0, + TYPE_ISOC, + TYPE_BULK, + TYPE_INTR, + TYPE_STREAM, + TYPE_COMMAND, + TYPE_EVENT, +}; + struct xhci_ring { struct xhci_segment *first_seg; + struct xhci_segment *last_seg; union xhci_trb *enqueue; struct xhci_segment *enq_seg; unsigned int enq_updates; @@ -1289,6 +1297,10 @@ struct xhci_ring { */ u32 cycle_state; unsigned int stream_id; + unsigned int num_segs; + unsigned int num_trbs_free; + unsigned int num_trbs_free_temp; + enum xhci_ring_type type; bool last_td_was_short; }; @@ -1364,6 +1376,7 @@ struct xhci_bus_state { /* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */ u32 port_c_suspend; u32 suspended_ports; + u32 port_remote_wakeup; unsigned long resume_done[USB_MAXCHILDREN]; }; @@ -1375,7 +1388,7 @@ static inline unsigned int hcd_index(struct usb_hcd *hcd) return 1; } -/* There is one ehci_hci structure per controller */ +/* There is one xhci_hcd structure per controller */ struct xhci_hcd { struct usb_hcd *main_hcd; struct usb_hcd *shared_hcd; @@ -1645,6 +1658,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep, gfp_t mem_flags); void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); +int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, + unsigned int num_trbs, gfp_t flags); void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); @@ -1684,6 +1699,17 @@ static inline int xhci_register_pci(void) { return 0; } static inline void xhci_unregister_pci(void) {} #endif +#if defined(CONFIG_USB_XHCI_PLATFORM) \ + || defined(CONFIG_USB_XHCI_PLATFORM_MODULE) +int xhci_register_plat(void); +void xhci_unregister_plat(void); +#else +static inline int xhci_register_plat(void) +{ return 0; } +static inline void xhci_unregister_plat(void) +{ } +#endif + /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); int handshake(struct xhci_hcd *xhci, void __iomem *ptr, diff --git a/fs/exec.c b/fs/exec.c index 78199eb..254685f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -624,7 +624,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) * when the old and new regions overlap clear from new_end. */ free_pgd_range(&tlb, new_end, old_end, new_end, - vma->vm_next ? vma->vm_next->vm_start : 0); + vma->vm_next ? vma->vm_next->vm_start : mm->task_size); } else { /* * otherwise, clean from old_start; this is done to not touch @@ -633,7 +633,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) * for the others its just a little faster. */ free_pgd_range(&tlb, old_start, old_end, new_end, - vma->vm_next ? vma->vm_next->vm_start : 0); + vma->vm_next ? vma->vm_next->vm_start : mm->task_size); } tlb_finish_mmu(&tlb, new_end, old_end); diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 6199922..38bbd9c 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -243,7 +243,11 @@ const struct file_operations ext4_file_operations = { .release = ext4_release_file, .fsync = ext4_sync_file, .splice_read = generic_file_splice_read, +#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE) + .splice_write = comcerto_file_splice_write, +#else .splice_write = generic_file_splice_write, +#endif .fallocate = ext4_fallocate, }; diff --git a/fs/fcntl.c b/fs/fcntl.c index 22764c7..0ffe61f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -142,6 +142,7 @@ SYSCALL_DEFINE1(dup, unsigned int, fildes) } return ret; } +EXPORT_SYMBOL(sys_dup); #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) diff --git a/fs/file.c b/fs/file.c index 30bfc99..94473dc 100644 --- a/fs/file.c +++ b/fs/file.c @@ -268,6 +268,7 @@ int expand_files(struct files_struct *files, int nr) /* All good, so we try */ return expand_fdtable(files, nr); } +EXPORT_SYMBOL_GPL(expand_files); static int count_open_files(struct fdtable *fdt) { diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 3005ec4..9572c26 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -112,6 +112,17 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) dbg_fsbuild("scanned flash completely\n"); jffs2_dbg_dump_block_lists_nolock(c); + if (c->flags & (1 << 7)) { + printk("%s(): unlocking the mtd device... ", __func__); + if (c->mtd->unlock) + c->mtd->unlock(c->mtd, 0, c->mtd->size); + printk("done.\n"); + + printk("%s(): erasing all blocks after the end marker... ", __func__); + jffs2_erase_pending_blocks(c, -1); + printk("done.\n"); + } + dbg_fsbuild("pass 1 starting\n"); c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 28107ca..04109f9 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -22,6 +22,10 @@ #define DEFAULT_EMPTY_SCAN_SIZE 256 +#if defined CONFIG_MTD_NAND_COMCERTO +#define BIT_FLIP_TOLERENCE 7 +#endif + #define noisy_printk(noise, args...) do { \ if (*(noise)) { \ printk(KERN_NOTICE args); \ @@ -64,6 +68,17 @@ static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) { return DEFAULT_EMPTY_SCAN_SIZE; } +#if defined CONFIG_MTD_NAND_COMCERTO +static inline uint32_t count_zero_bits( uint32_t value) { + uint32_t num_zeros = 0; + size_t i; + for (i = 0; i < sizeof value; ++i, value >>= 1) { + if ((value & 1) == 0) + ++num_zeros; + } +} +#endif + static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; @@ -72,7 +87,7 @@ static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) return ret; if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size))) return ret; - /* Turned wasted size into dirty, since we apparently + /* Turned wasted size into dirty, since we apparently think it's recoverable now. */ jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; @@ -147,8 +162,11 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) /* reset summary info for next eraseblock scan */ jffs2_sum_reset_collected(s); - ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), - buf_size, s); + if (c->flags & (1 << 7)) + ret = BLK_STATE_ALLFF; + else + ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), + buf_size, s); if (ret < 0) goto out; @@ -403,7 +421,7 @@ static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock if (!ref) return -ENOMEM; - /* BEFORE jffs2_build_xattr_subsystem() called, + /* BEFORE jffs2_build_xattr_subsystem() called, * and AFTER xattr_ref is marked as a dead xref, * ref->xid is used to store 32bit xid, xd is not used * ref->ino is used to store 32bit inode-number, ic is not used @@ -476,7 +494,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo struct jffs2_sum_marker *sm; void *sumptr = NULL; uint32_t sumlen; - + if (!buf_size) { /* XIP case. Just look, point at the summary if it's there */ sm = (void *)buf + c->sector_size - sizeof(*sm); @@ -492,9 +510,9 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo buf_len = sizeof(*sm); /* Read as much as we want into the _end_ of the preallocated buffer */ - err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, + err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, jeb->offset + c->sector_size - buf_len, - buf_len); + buf_len); if (err) return err; @@ -513,9 +531,9 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo } if (buf_len < sumlen) { /* Need to read more so that the entire summary node is present */ - err = jffs2_fill_scan_buf(c, sumptr, + err = jffs2_fill_scan_buf(c, sumptr, jeb->offset + c->sector_size - sumlen, - sumlen - buf_len); + sumlen - buf_len); if (err) return err; } @@ -528,7 +546,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo if (buf_size && sumlen > buf_size) kfree(sumptr); - /* If it returns with a real error, bail. + /* If it returns with a real error, bail. If it returns positive, that's a block classification (i.e. BLK_STATE_xxx) so return that too. If it returns zero, fall through to full scan. */ @@ -549,6 +567,17 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo return err; } + if ((buf[0] == 0xde) && + (buf[1] == 0xad) && + (buf[2] == 0xc0) && + (buf[3] == 0xde)) { + /* end of filesystem. erase everything after this point */ + printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); + c->flags |= (1 << 7); + + return BLK_STATE_ALLFF; + } + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ofs = 0; max_ofs = EMPTY_SCAN_SIZE(c->sector_size); @@ -575,6 +604,34 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo else return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ } + +#if defined CONFIG_MTD_NAND_COMCERTO + else if (cleanmarkerfound) { + ofs = 0; + uint32_t num_zeros = 0; + while((ofs < max_ofs) && (num_zeros < BIT_FLIP_TOLERENCE)) { + if (!(*(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)) + num_zeros = count_zero_bits(*(uint32_t *)(&buf[ofs])); + ofs += 4; + } +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + if ((num_zeros < BIT_FLIP_TOLERENCE) && jffs2_cleanmarker_oob(c)) { + /* scan oob, take care of cleanmarker */ + int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); + D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret)); + switch (ret) { + case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; + case 1: return BLK_STATE_ALLDIRTY; + default: return ret; + } + } else { + return BLK_STATE_ALLDIRTY; + } +#endif + + } +#endif + if (ofs) { D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, jeb->offset + ofs)); @@ -674,7 +731,7 @@ scan_more: scan_end = buf_len; goto more_empty; } - + /* See how much more there is to read in this eraseblock... */ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); if (!buf_len) { @@ -910,7 +967,7 @@ scan_more: D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n", jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size)); - + /* mark_node_obsolete can add to wasted !! */ if (jeb->wasted_size) { jeb->dirty_size += jeb->wasted_size; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f255d37..5833845 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -815,7 +815,29 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) ret = 0; - fsnotify_destroy_mark(&i_mark->fsn_mark); + /* + # ITR:75721 + # Abstract + # AAT: Hang during reboot (USB drive is attached) / Kernel crash on shutdown + * Check if this event is for an Inode change - if so + * take a s_umount lock for inode's superblock so that if the + * superblock is being destroyed the access to inode->i_sb is verified to be + * for a valid super_block (or not). + * If it is no longer valid the the group id will be NULL down the line in + * fsnotify_destroy_mark() and this error will lead us to the kernel oops if + * it is the loser of the race: between the umount path of superblock and + * fsnotification path where it will call iput(inode)... + */ + if (i_mark->fsn_mark.flags & FSNOTIFY_MARK_FLAG_INODE) { + struct inode *inode; + inode = i_mark->fsn_mark.i.inode; + if (inode) { + down_read(&inode->i_sb->s_umount); + fsnotify_destroy_mark(&i_mark->fsn_mark); + up_read(&inode->i_sb->s_umount); + } + } else + fsnotify_destroy_mark(&i_mark->fsn_mark); /* match ref taken by inotify_idr_find */ fsnotify_put_mark(&i_mark->fsn_mark); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index f104d56..02a3a91 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -175,8 +175,18 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) * is just a lazy update (and could be a perf win...) */ - if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) - iput(inode); + /* + # ITR:75721 + # Abstract + # AAT: Hang during reboot (USB drive is attached) / Kernel crash on shutdown + # if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) + # iput(inode); + */ + if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) { + /* Lets check if inode belongs to a valid super block or not. */ + if (inode->i_sb && inode->i_sb->s_count > 0) + iput(inode); + } /* * We don't necessarily have a ref on mark from caller so the above iput diff --git a/fs/splice.c b/fs/splice.c index 34c2b2b..598b76d 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -32,6 +32,7 @@ #include #include #include +#include /* * Attempt to steal a page from a pipe buffer. This should perhaps go into @@ -860,6 +861,380 @@ int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd, } EXPORT_SYMBOL(splice_from_pipe_feed); + +#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE) +#if !defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) +#define MSPD_SPLICE_NUM_DMA 100 +#else +#define MSPD_SPLICE_NUM_DMA MDMA_OUTBOUND_BUF_DESC +#endif + +#if defined(CONFIG_COMCERTO_SPLICE_PROF) +unsigned int enable_splice_prof = 0; +#endif + +int comcerto_splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd) +{ + struct page **mspd_splice_pages; + void **mspd_splice_fsdata; + struct pipe_buffer *buf; + const struct pipe_buf_operations *ops; + int ret, ret2 = 0, remaining; + unsigned int curbuf, nrbufs, len, nrbufs_len, done; + loff_t pos, offset; + struct file *file = sd->u.file; + struct address_space *mapping = file->f_mapping; + struct page **page; + void **fsdata; + unsigned int size; +#if !defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + unsigned int buf_len, buf_offset; + char *src, *dst; +#else + struct comcerto_dma_sg *sg; +#endif + + size = (sizeof(struct page *) + sizeof(void *)) * MSPD_SPLICE_NUM_DMA; + +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + size = ALIGN(size, 8) + sizeof(struct comcerto_dma_sg); +#endif + + mspd_splice_pages = kmalloc(size, GFP_KERNEL); + if (!mspd_splice_pages) + return -ENOMEM; + + mspd_splice_fsdata = (void **)(mspd_splice_pages + MSPD_SPLICE_NUM_DMA); + +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + sg = (struct comcerto_dma_sg *)(mspd_splice_fsdata + MSPD_SPLICE_NUM_DMA); + sg = PTR_ALIGN(sg, 8); +#endif + +start: +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + comcerto_dma_sg_init(sg); +#endif + + //Compute length to transfer (in bytes), and make sure data is there + nrbufs_len = 0; + nrbufs = pipe->nrbufs; + curbuf = pipe->curbuf; + while (nrbufs) { + buf = pipe->bufs + curbuf; + + ret = buf->ops->confirm(pipe, buf); + if (unlikely(ret)) { + printk(KERN_WARNING "%s: buf->ops->confirm() failed(%d)\n", __func__, ret); + if (ret == -ENODATA) + ret = 0; + goto err; + } + +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + // Is there a risk of getting the same page more than once (several buffers in a single page)? + ret = comcerto_dma_sg_add_input(sg, buf->page, buf->offset, buf->len, 0); + if (unlikely(ret)) { + printk(KERN_WARNING "%s: out of input bdescs\n", __func__); + break; //We will transfer what we could up to the previous buffer, based on nrbufs_len + } +#endif + + nrbufs_len += buf->len; + + if (nrbufs_len > sd->total_len) { + nrbufs_len = sd->total_len; + break; + } + + // - 2 because first and last pages could be almost empty depending on alignment + if (nrbufs_len > (MSPD_SPLICE_NUM_DMA - 2)*PAGE_CACHE_SIZE) { + nrbufs_len = (MSPD_SPLICE_NUM_DMA - 2)*PAGE_CACHE_SIZE; + break; + } + curbuf = (curbuf + 1) & (pipe->buffers - 1); + nrbufs--; + } + + if (unlikely(nrbufs_len == 0)) { + printk(KERN_WARNING "%s: nrbufs_len == 0\n", __func__); + ret = 0; + goto err; + } + +// printk("BLA nrbufs_len: %d\n", nrbufs_len); + + /* Allocate as many destinations pages as needed. + * First and last pages are likely not to be filled, but the ones in-between will. + * If some allocations fail, finish the work on the allocated pages. + */ + page = &mspd_splice_pages[0]; + fsdata = &mspd_splice_fsdata[0]; + + pos = sd->pos; + offset = pos & ~PAGE_CACHE_MASK; + len = nrbufs_len; + + if (likely(len + offset > PAGE_CACHE_SIZE)) + len = PAGE_CACHE_SIZE - offset; + + ret = pagecache_write_begin(file, mapping, pos, len, + AOP_FLAG_UNINTERRUPTIBLE, page, fsdata); + if (unlikely(ret)) + goto err; // We failed early, so we still have an easy way out + +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + comcerto_dma_sg_add_output(sg, *page, offset, len, 1); //Don't check result since we should have at least one entry at this point +#endif + + pos += len; + remaining = nrbufs_len - len; + page++; + fsdata++; + + while (remaining > PAGE_CACHE_SIZE) { + ret = pagecache_write_begin(file, mapping, pos, PAGE_CACHE_SIZE, + AOP_FLAG_UNINTERRUPTIBLE, page, fsdata); + + if (unlikely(ret)) + goto write_begin_done; + +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + ret = comcerto_dma_sg_add_output(sg, *page, 0, PAGE_CACHE_SIZE, 1); + if (unlikely(ret)) { + pagecache_write_end(file, mapping, pos, PAGE_CACHE_SIZE, 0, *page, *fsdata); + goto write_begin_done; + } +#endif + pos += PAGE_CACHE_SIZE; + remaining -= PAGE_CACHE_SIZE; + page++; + fsdata++; + } + + if (remaining) { + ret = pagecache_write_begin(file, mapping, pos, remaining, + AOP_FLAG_UNINTERRUPTIBLE, page, fsdata); + + if (unlikely(ret)) + goto write_begin_done; + +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + ret = comcerto_dma_sg_add_output(sg, *page, 0, remaining, 1); + if (unlikely(ret)) { + pagecache_write_end(file, mapping, pos, remaining, 0, *page, *fsdata); + goto write_begin_done; + } +#endif + remaining = 0; + } + +write_begin_done: + // Couldn't allocate all pages or bdescs, so update the total length accordingly + if (unlikely(remaining)) + nrbufs_len = nrbufs_len - remaining; + + //Now do the copies +#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA) + + comcerto_dma_get(); + + comcerto_dma_sg_setup(sg, nrbufs_len); + + comcerto_dma_start(); + comcerto_dma_wait(); + comcerto_dma_put(); + + comcerto_dma_sg_cleanup(sg, nrbufs_len); +#else + remaining = nrbufs_len; + curbuf = pipe->curbuf; + buf = pipe->bufs + curbuf; + buf_len = buf->len; + buf_offset = buf->offset; + src = buf->ops->map(pipe, buf, 1); + pos = sd->pos; + offset = pos & ~PAGE_CACHE_MASK; + page = &mspd_splice_pages[0]; + dst = kmap_atomic(*page, KM_USER1); + + while (remaining) { + len = remaining; + if (len + offset > PAGE_CACHE_SIZE) + len = PAGE_CACHE_SIZE - offset; + if (len > buf_len) + len = buf_len; + + memcpy(dst + offset, src + buf_offset, len); + + buf_len -= len; + buf_offset += len; + remaining -= len; + pos += len; + offset = pos & ~PAGE_CACHE_MASK; + + if (!offset) { + /* FIXME if this was the last page we should still flush/unmap, even if it's not a full page */ + /* ... actually it looks ok, the unmap is done outside the loop */ + flush_dcache_page(*page); + kunmap_atomic(dst, KM_USER1); + if (remaining) { + page++; + dst = kmap_atomic(*page, KM_USER1); + } + } + + if (!buf_len) { + buf->ops->unmap(pipe, buf, src); + if (remaining) { + curbuf = (curbuf + 1) & (pipe->buffers - 1); + buf = pipe->bufs + curbuf; + buf_len = buf->len; + buf_offset = buf->offset; + src = buf->ops->map(pipe, buf, 1); + } + } + } + + if (offset) { + flush_dcache_page(*page); + kunmap_atomic(dst, KM_USER1); + } + + if (buf_len) + buf->ops->unmap(pipe, buf, src); +#endif + + + //loop on write_end, update sd fields + page = &mspd_splice_pages[0]; + fsdata = &mspd_splice_fsdata[0]; + offset = sd->pos & ~PAGE_CACHE_MASK; + pos = sd->pos; + remaining = nrbufs_len; + len = nrbufs_len; + done = 0; + + if (likely(len + offset > PAGE_CACHE_SIZE)) + len = PAGE_CACHE_SIZE - offset; + + ret = pagecache_write_end(file, mapping, pos, len, len, + *page, *fsdata); + + /* In case of error or short write we need to report error to the caller */ + /* If there was already a previous error, just continue doing the pagecache_write_end() cleanup */ + /* Otherwise keep track of how many bytes we have succefully written and that an error happened */ + if (unlikely(ret != len)) { + printk(KERN_ERR "Failed on write_end, continuing with other buffers\n"); + + /* Only report error to caller if nothing has been done */ + ret2 = ret; + nrbufs_len = (ret > 0) ? ret: 0; + } + + pos += len; + done += len; + remaining -= len; + + page++; + fsdata++; + + while (remaining > PAGE_CACHE_SIZE) { + ret = pagecache_write_end(file, mapping, pos, PAGE_CACHE_SIZE, PAGE_CACHE_SIZE, + *page, *fsdata); + + if (unlikely((ret != PAGE_CACHE_SIZE) && !ret2)) { + printk(KERN_ERR "Failed on write_end, continuing with other buffers\n"); + + nrbufs_len = done; + + if (ret >= 0) + nrbufs_len += ret; + + ret2 = nrbufs_len; + } + + pos += PAGE_CACHE_SIZE; + done += PAGE_CACHE_SIZE; + remaining -= PAGE_CACHE_SIZE; + + page++; + fsdata++; + } + + if (remaining) { + ret = pagecache_write_end(file, mapping, pos, remaining, remaining, + *page, *fsdata); + + if (unlikely((ret != remaining) && !ret2)) { + printk(KERN_ERR "Failed on write_end, continuing with other buffers\n"); + + nrbufs_len = done; + + if (ret >= 0) + nrbufs_len += ret; + + ret2 = nrbufs_len; + } + } + + sd->num_spliced += nrbufs_len; + sd->len -= nrbufs_len; + sd->pos += nrbufs_len; + sd->total_len -= nrbufs_len; + + //loop on pipe buffers to release them + remaining = nrbufs_len; + buf = pipe->bufs + pipe->curbuf; + + while (remaining && (remaining >= buf->len)) { + ops = buf->ops; + + remaining -= buf->len; + buf->len = 0; + buf->ops = NULL; + ops->release(pipe, buf); + pipe->nrbufs--; + pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); + buf = pipe->bufs + pipe->curbuf; + } + + // Last buffer, might not be empty + if (remaining) { + buf->len -= remaining; + buf->offset += remaining; + } + + if (pipe->inode) + sd->need_wakeup = true; + + if (!sd->total_len) { + kfree(mspd_splice_pages); + return 0; + } + + if (ret2) { + if (ret2 > 0) + ret = 0; + else + ret = ret2; + + goto err; + } + + if (pipe->nrbufs) + goto start; + + ret = 1; + +err: + kfree(mspd_splice_pages); + + return ret; +} +EXPORT_SYMBOL(comcerto_splice_from_pipe_feed); +#endif + /** * splice_from_pipe_next - wait for some data to splice from * @pipe: pipe to splice from @@ -1066,6 +1441,113 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, EXPORT_SYMBOL(generic_file_splice_write); +#if defined(CONFIG_COMCERTO_SPLICE_PROF) +unsigned int splicew_time_counter[256]; +unsigned int splicew_reqtime_counter[256]; +unsigned int splicew_data_counter[256]; +static struct timeval last_splicew; +unsigned int init_splicew_prof = 0; +#endif + +#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE) +ssize_t +comcerto_file_splice_write(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) +{ + struct address_space *mapping = out->f_mapping; + struct inode *inode = mapping->host; + struct splice_desc sd = { + .total_len = len, + .flags = flags, + .pos = *ppos, + .u.file = out, + }; + ssize_t ret; +#if defined(CONFIG_COMCERTO_SPLICE_PROF) + struct timeval now; + int diff_time_ms; +#endif + + pipe_lock(pipe); + +#if defined(CONFIG_COMCERTO_SPLICE_PROF) + if (enable_splice_prof) { + do_gettimeofday(&now); + if (init_splicew_prof) { + diff_time_ms = ((now.tv_sec - last_splicew.tv_sec) * 1000) + ((now.tv_usec - last_splicew.tv_usec) / 1000); + if (diff_time_ms < 1000) { + splicew_time_counter[diff_time_ms >> 3]++; + } + else { + splicew_time_counter[255]++; + } + } + last_splicew = now; + if (len < (1 <<21)) + splicew_data_counter[(len >> 13) & 0xFF]++; + else + splicew_data_counter[255]++; + } +#endif + + splice_from_pipe_begin(&sd); + do { + ret = splice_from_pipe_next(pipe, &sd); + if (ret <= 0) + break; + + mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); + ret = file_remove_suid(out); + if (!ret) { + file_update_time(out); + ret = comcerto_splice_from_pipe_feed(pipe, &sd); + } + mutex_unlock(&inode->i_mutex); + } while (ret > 0); + splice_from_pipe_end(pipe, &sd); + + pipe_unlock(pipe); + + if (sd.num_spliced) + ret = sd.num_spliced; + + if (ret > 0) { + unsigned long nr_pages; + int err; + + nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + + err = generic_write_sync(out, *ppos, ret); + if (err) + ret = err; + else + *ppos += ret; + balance_dirty_pages_ratelimited_nr(mapping, nr_pages); + } + +#if defined(CONFIG_COMCERTO_SPLICE_PROF) + if (enable_splice_prof) { + do_gettimeofday(&now); + + diff_time_ms = ((now.tv_sec - last_splicew.tv_sec) * 1000) + ((now.tv_usec - last_splicew.tv_usec) / 1000); + if (diff_time_ms < 1000) {//Don't record useless data + splicew_reqtime_counter[diff_time_ms >> 3]++; + } + else + splicew_reqtime_counter[255]++; + + if(!init_splicew_prof) + init_splicew_prof = 1; + + last_splicew = now; + } +#endif + return ret; +} + +EXPORT_SYMBOL(comcerto_file_splice_write); +#endif + static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct splice_desc *sd) { diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index b5e2e4c..6935a59 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -52,6 +52,27 @@ #define LOAD_OFFSET 0 #endif +#ifndef SYMTAB_KEEP_STR +#define SYMTAB_KEEP_STR *(__ksymtab_strings+*) +#define SYMTAB_DISCARD_STR +#else +#define SYMTAB_DISCARD_STR *(__ksymtab_strings+*) +#endif + +#ifndef SYMTAB_KEEP +#define SYMTAB_KEEP *(SORT(___ksymtab+*)) +#define SYMTAB_DISCARD +#else +#define SYMTAB_DISCARD *(SORT(___ksymtab+*)) +#endif + +#ifndef SYMTAB_KEEP_GPL +#define SYMTAB_KEEP_GPL *(SORT(___ksymtab_gpl+*)) +#define SYMTAB_DISCARD_GPL +#else +#define SYMTAB_DISCARD_GPL *(SORT(___ksymtab_gpl+*)) +#endif + #ifndef SYMBOL_PREFIX #define VMLINUX_SYMBOL(sym) sym #else @@ -275,14 +296,14 @@ /* Kernel symbol table: Normal symbols */ \ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab) = .; \ - *(SORT(___ksymtab+*)) \ + SYMTAB_KEEP \ VMLINUX_SYMBOL(__stop___ksymtab) = .; \ } \ \ /* Kernel symbol table: GPL-only symbols */ \ __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_gpl) = .; \ - *(SORT(___ksymtab_gpl+*)) \ + SYMTAB_KEEP_GPL \ VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .; \ } \ \ @@ -344,7 +365,7 @@ \ /* Kernel symbol table: strings */ \ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ - *(__ksymtab_strings) \ + SYMTAB_KEEP_STR \ } \ \ /* __*init sections */ \ @@ -676,6 +697,9 @@ EXIT_TEXT \ EXIT_DATA \ EXIT_CALL \ + SYMTAB_DISCARD \ + SYMTAB_DISCARD_GPL \ + SYMTAB_DISCARD_STR \ *(.discard) \ *(.discard.*) \ } diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index b1038bd..a165ab8 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -108,6 +108,10 @@ struct backing_dev_info { struct dentry *debug_dir; struct dentry *debug_stats; #endif + +#ifdef CONFIG_ARCH_M86XXX + unsigned int cpu0_bind; +#endif }; int bdi_init(struct backing_dev_info *bdi); diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index b840a49..a376f54 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -9,6 +9,9 @@ #define PHY_ID_BCM5464 0x002060b0 #define PHY_ID_BCM5461 0x002060c0 #define PHY_ID_BCM57780 0x03625d90 +#define PHY_ID_BCM54610 0x0143bd63 +#define PHY_ID_BCM54612 0x03625e6a + #define PHY_BCM_OUI_MASK 0xfffffc00 #define PHY_BCM_OUI_1 0x00206000 diff --git a/include/linux/export.h b/include/linux/export.h index 696c0f4..a2d8a34 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -45,12 +45,19 @@ extern struct module __this_module; #define __CRC_SYMBOL(sym, sec) #endif +#ifdef MODULE +#define __EXPORT_SUFFIX(sym) +#else +#define __EXPORT_SUFFIX(sym) "+" #sym +#endif + /* For every exported symbol, place a struct in the __ksymtab section */ #define __EXPORT_SYMBOL(sym, sec) \ extern typeof(sym) sym; \ __CRC_SYMBOL(sym, sec) \ static const char __kstrtab_##sym[] \ - __attribute__((section("__ksymtab_strings"), aligned(1))) \ + __attribute__((section("__ksymtab_strings" \ + __EXPORT_SUFFIX(sym)), aligned(1))) \ = MODULE_SYMBOL_PREFIX #sym; \ static const struct kernel_symbol __ksymtab_##sym \ __used \ diff --git a/include/linux/fs.h b/include/linux/fs.h index dd74385..0cb5eeb 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2442,6 +2442,10 @@ extern ssize_t default_file_splice_read(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); extern ssize_t generic_file_splice_write(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); +#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE) +extern ssize_t comcerto_file_splice_write(struct pipe_inode_info *, + struct file *, loff_t *, size_t, unsigned int); +#endif extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out, loff_t *, size_t len, unsigned int flags); extern long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 3a76faf..e371b05 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -141,7 +141,12 @@ struct vm_area_struct; /* Flag - indicates that the buffer will be suitable for DMA. Ignored on some platforms, used as appropriate on others */ +#if defined(CONFIG_COMCERTO_ZONE_DMA_NCNB) +#define GFP_DMA __GFP_DMA32 +#define GFP_DMA_NCNB __GFP_DMA +#else #define GFP_DMA __GFP_DMA +#endif /* 4GB DMA on some platforms */ #define GFP_DMA32 __GFP_DMA32 diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index 6d722f4..5e77aad 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -92,6 +92,8 @@ #define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */ #define ARPHRD_CAIF 822 /* CAIF media type */ +#define ARPHRD_IPV6_IPV6_TUNNEL ARPHRD_ETHER + #define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ #define ARPHRD_NONE 0xFFFE /* zero header length */ diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index e473003..e85de7c 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -34,6 +34,7 @@ #define ETH_DATA_LEN 1500 /* Max. octets in payload */ #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ #define ETH_FCS_LEN 4 /* Octets in the FCS */ +#define ETH_IPHLEN 2 /* EtherIP header length */ /* * These are the defined Ethernet Protocol ID's. diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index f379929..e06b393 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -29,6 +29,8 @@ struct sockaddr_ll { /* These ones are invisible by user level */ #define PACKET_LOOPBACK 5 /* MC/BRD frame looped back */ #define PACKET_FASTROUTE 6 /* Fastrouted frame */ +#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */ + /* Packet socket options */ @@ -50,6 +52,7 @@ struct sockaddr_ll { #define PACKET_TX_TIMESTAMP 16 #define PACKET_TIMESTAMP 17 #define PACKET_FANOUT 18 +#define PACKET_RECV_TYPE 19 #define PACKET_FANOUT_HASH 0 #define PACKET_FANOUT_LB 1 diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h index c9ad383..38f0d79 100644 --- a/include/linux/if_ppp.h +++ b/include/linux/if_ppp.h @@ -161,6 +161,9 @@ struct pppol2tp_ioc_stats { #define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */ #define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */ #define PPPIOCGL2TPSTATS _IOR('t', 54, struct pppol2tp_ioc_stats) +#if defined(CONFIG_COMCERTO_FP) +#define PPPIOCSFPPIDLE _IOW('t', 53, struct ppp_idle) /* Set the FPP stats */ +#endif #define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0) #define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */ diff --git a/include/linux/in.h b/include/linux/in.h index 01129c0..3422759 100644 --- a/include/linux/in.h +++ b/include/linux/in.h @@ -41,6 +41,7 @@ enum { IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ IPPROTO_AH = 51, /* Authentication Header protocol */ IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ + IPPROTO_ETHERIP = 97, /* IP option for EtherIP tunnel (rfc 3378) */ IPPROTO_PIM = 103, /* Protocol Independent Multicast */ IPPROTO_COMP = 108, /* Compression Header protocol */ diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h index bf22b03..5970daf9 100644 --- a/include/linux/ip6_tunnel.h +++ b/include/linux/ip6_tunnel.h @@ -31,4 +31,5 @@ struct ip6_tnl_parm { struct in6_addr raddr; /* remote tunnel end-point address */ }; + #endif diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 445f978..2df4d51 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -31,6 +31,8 @@ #define UEVENT_NUM_ENVP 32 /* number of env pointers */ #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ +struct sk_buff; + /* path to the userspace helper executed on an event */ extern char uevent_helper[]; @@ -215,6 +217,10 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); int kobject_action_type(const char *buf, size_t count, enum kobject_action *type); + +int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, + gfp_t allocation); + #else static inline int kobject_uevent(struct kobject *kobj, enum kobject_action action) @@ -231,6 +237,16 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) static inline int kobject_action_type(const char *buf, size_t count, enum kobject_action *type) { return -EINVAL; } + +void kfree_skb(struct sk_buff *); + +static inline int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, + gfp_t allocation) +{ + kfree_skb(skb); + return 0; +} + #endif #endif /* _KOBJECT_H_ */ diff --git a/include/linux/leds.h b/include/linux/leds.h index 5884def..ac673ba 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -33,12 +33,23 @@ struct led_classdev { int brightness; int max_brightness; int flags; +//#ifdef 1 + int color; + int blink; +//#endif /* Lower 16 bits reflect status */ #define LED_SUSPENDED (1 << 0) /* Upper 16 bits reflect control information */ #define LED_CORE_SUSPENDRESUME (1 << 16) +//#ifdef 1 + void (*color_set)(struct led_classdev *led_cdev, + enum led_brightness color); + /* Get LED brightness level */ + enum led_brightness (*color_get)(struct led_classdev *led_cdev); +//#endif + /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ void (*brightness_set)(struct led_classdev *led_cdev, @@ -58,6 +69,11 @@ struct led_classdev { unsigned long *delay_on, unsigned long *delay_off); +//#ifdef 1 + int (*blink_set_3g)(struct led_classdev *led_cdev, int value ); + int (*blink_get_3g)(struct led_classdev *led_cdev); +//#endif + struct device *dev; struct list_head node; /* LED Device list */ const char *default_trigger; /* Trigger to use */ diff --git a/include/linux/libata.h b/include/linux/libata.h index 42ac6ad..67609b1 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -362,7 +362,7 @@ enum { /* how hard are we gonna try to probe/recover devices */ ATA_PROBE_MAX_TRIES = 3, - ATA_EH_DEV_TRIES = 3, + ATA_EH_DEV_TRIES = 25, ATA_EH_PMP_TRIES = 5, ATA_EH_PMP_LINK_TRIES = 3, diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index c41d727..24b73c0 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -19,6 +19,7 @@ #define APOLLO_MOUSE_MINOR 7 #define PC110PAD_MINOR 9 /*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ +#define CRYPTODEV_MINOR 70 /* /dev/crypto */ #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ #define RTC_MINOR 135 diff --git a/include/linux/mm.h b/include/linux/mm.h index e5ee683..1e3e0e8 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -874,6 +874,7 @@ extern bool skip_free_areas_node(unsigned int flags, int nid); int shmem_lock(struct file *file, int lock, struct user_struct *user); struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); +void shmem_set_file(struct vm_area_struct *vma, struct file *file); int shmem_zero_setup(struct vm_area_struct *); extern int can_do_mlock(void); diff --git a/include/linux/module.h b/include/linux/module.h index 3cb7839..60652e7 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -86,8 +86,11 @@ void trim_init_extable(struct module *m); extern const struct gtype##_id __mod_##gtype##_table \ __attribute__ ((unused, alias(__stringify(name)))) +extern struct module __this_module; +#define THIS_MODULE (&__this_module) #else /* !MODULE */ #define MODULE_GENERIC_TABLE(gtype,name) +#define THIS_MODULE ((struct module *)0) #endif /* Generic info of form tag = "info" */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 9f5b312..629401a 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -58,6 +58,10 @@ struct erase_info { u_long priv; u_char state; struct erase_info *next; + + u8 *erase_buf; + u32 erase_buf_ofs; + bool partial_start; }; struct mtd_erase_region_info { @@ -114,6 +118,7 @@ struct nand_ecclayout { struct module; /* only needed for owner field in mtd_info */ +struct mtd_info; struct mtd_info { u_char type; uint32_t flags; @@ -266,6 +271,9 @@ struct mtd_info { struct device dev; int usecount; + int (*refresh_device)(struct mtd_info *mtd); + struct mtd_info *split; + /* If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. * The driver may register its callbacks. These callbacks are not @@ -321,6 +329,7 @@ extern int mtd_device_parse_register(struct mtd_info *mtd, int defnr_parts); #define mtd_device_register(master, parts, nr_parts) \ mtd_device_parse_register(master, NULL, NULL, parts, nr_parts) +extern int mtd_device_refresh(struct mtd_info *master); extern int mtd_device_unregister(struct mtd_info *master); extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); extern int __get_mtd_device(struct mtd_info *mtd); diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 2475228..8b2fecc 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -35,13 +35,16 @@ * Note: writeable partitions require their size and offset be * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). */ +struct mtd_info; +struct mtd_partition; struct mtd_partition { char *name; /* identifier string */ uint64_t size; /* partition size */ uint64_t offset; /* offset within the master MTD space */ uint32_t mask_flags; /* master MTD flags to mask out for this partition */ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */ + int (*refresh_partition)(struct mtd_info *); }; #define MTDPART_OFS_RETAIN (-3) @@ -50,7 +53,6 @@ struct mtd_partition { #define MTDPART_SIZ_FULL (0) -struct mtd_info; struct device_node; /** diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index 04e0181..41b00a5 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -17,6 +17,7 @@ #include #include +#include struct map_info; struct platform_device; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4b04097..52ed7d7f8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -142,7 +142,7 @@ static inline bool dev_xmit_complete(int rc) */ #if defined(CONFIG_WLAN) || defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -# if defined(CONFIG_MAC80211_MESH) +# if 1 || defined(CONFIG_MAC80211_MESH) # define LL_MAX_HEADER 128 # else # define LL_MAX_HEADER 96 diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index 0d3dd66..eaeea3f 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -83,6 +83,14 @@ enum ip_conntrack_status { /* Conntrack is a fake untracked entry */ IPS_UNTRACKED_BIT = 12, IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT), + + /* Connection cannot expire */ + IPS_PERMANENT_BIT = 13, + IPS_PERMANENT = (1 << IPS_PERMANENT_BIT), + + /* Connection is assured by DPI application */ + IPS_DPI_ALLOWED_BIT = 14, + IPS_DPI_ALLOWED = (1 << IPS_DPI_ALLOWED_BIT), }; /* Connection tracking event types */ diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 0ce91d5..feda699 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -2,12 +2,15 @@ #define __NF_CONNTRACK_SIP_H__ #ifdef __KERNEL__ +#include + #define SIP_PORT 5060 #define SIP_TIMEOUT 3600 struct nf_ct_sip_master { unsigned int register_cseq; unsigned int invite_cseq; + __be16 forced_dport; }; enum sip_expectation_classes { diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index debf1ae..fb0dd93 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -43,6 +43,8 @@ enum ctattr_type { CTA_ZONE, CTA_SECCTX, CTA_TIMESTAMP, + CTA_COMCERTO_FP_ORIG, + CTA_COMCERTO_FP_REPLY, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) @@ -190,4 +192,13 @@ enum ctattr_secctx { }; #define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1) +enum ctattr_comcerto_fp { + CTA_COMCERTO_FP_UNSPEC, + CTA_COMCERTO_FP_MARK, + CTA_COMCERTO_FP_IFINDEX, + CTA_COMCERTO_FP_IIF, + __CTA_COMCERTO_FP_MAX +}; +#define CTA_COMCERTO_FP_MAX (__CTA_COMCERTO_FP_MAX - 1) + #endif /* _IPCONNTRACK_NETLINK_H */ diff --git a/include/linux/netfilter/xt_connmark.h b/include/linux/netfilter/xt_connmark.h index efc17a8..2f2e48e 100644 --- a/include/linux/netfilter/xt_connmark.h +++ b/include/linux/netfilter/xt_connmark.h @@ -1,31 +1,6 @@ -#ifndef _XT_CONNMARK_H -#define _XT_CONNMARK_H +#ifndef _XT_CONNMARK_H_target +#define _XT_CONNMARK_H_target -#include +#include -/* Copyright (C) 2002,2004 MARA Systems AB - * by Henrik Nordstrom - * - * 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. - */ - -enum { - XT_CONNMARK_SET = 0, - XT_CONNMARK_SAVE, - XT_CONNMARK_RESTORE -}; - -struct xt_connmark_tginfo1 { - __u32 ctmark, ctmask, nfmask; - __u8 mode; -}; - -struct xt_connmark_mtinfo1 { - __u32 mark, mask; - __u8 invert; -}; - -#endif /*_XT_CONNMARK_H*/ +#endif /*_XT_CONNMARK_H_target*/ diff --git a/include/linux/netfilter/xt_dscp.h b/include/linux/netfilter/xt_dscp.h index 15f8932..648e0b3 100644 --- a/include/linux/netfilter/xt_dscp.h +++ b/include/linux/netfilter/xt_dscp.h @@ -1,31 +1,26 @@ -/* x_tables module for matching the IPv4/IPv6 DSCP field +/* x_tables module for setting the IPv4/IPv6 DSCP field * * (C) 2002 Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * This software is distributed under GNU GPL v2, 1991 * * See RFC2474 for a description of the DSCP field within the IP Header. * - * xt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp + * xt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp */ -#ifndef _XT_DSCP_H -#define _XT_DSCP_H - +#ifndef _XT_DSCP_TARGET_H +#define _XT_DSCP_TARGET_H +#include #include -#define XT_DSCP_MASK 0xfc /* 11111100 */ -#define XT_DSCP_SHIFT 2 -#define XT_DSCP_MAX 0x3f /* 00111111 */ - -/* match info */ -struct xt_dscp_info { +/* target info */ +struct xt_DSCP_info { __u8 dscp; - __u8 invert; }; -struct xt_tos_match_info { - __u8 tos_mask; +struct xt_tos_target_info { __u8 tos_value; - __u8 invert; + __u8 tos_mask; }; -#endif /* _XT_DSCP_H */ +#endif /* _XT_DSCP_TARGET_H */ diff --git a/include/linux/netfilter/xt_mark.h b/include/linux/netfilter/xt_mark.h index ecadc40..41c456d 100644 --- a/include/linux/netfilter/xt_mark.h +++ b/include/linux/netfilter/xt_mark.h @@ -1,15 +1,6 @@ -#ifndef _XT_MARK_H -#define _XT_MARK_H +#ifndef _XT_MARK_H_target +#define _XT_MARK_H_target -#include +#include -struct xt_mark_tginfo2 { - __u32 mark, mask; -}; - -struct xt_mark_mtinfo1 { - __u32 mark, mask; - __u8 invert; -}; - -#endif /*_XT_MARK_H*/ +#endif /*_XT_MARK_H_target */ diff --git a/include/linux/netfilter/xt_rateest.h b/include/linux/netfilter/xt_rateest.h index d40a619..6605e20 100644 --- a/include/linux/netfilter/xt_rateest.h +++ b/include/linux/netfilter/xt_rateest.h @@ -1,37 +1,15 @@ -#ifndef _XT_RATEEST_MATCH_H -#define _XT_RATEEST_MATCH_H +#ifndef _XT_RATEEST_TARGET_H +#define _XT_RATEEST_TARGET_H #include -enum xt_rateest_match_flags { - XT_RATEEST_MATCH_INVERT = 1<<0, - XT_RATEEST_MATCH_ABS = 1<<1, - XT_RATEEST_MATCH_REL = 1<<2, - XT_RATEEST_MATCH_DELTA = 1<<3, - XT_RATEEST_MATCH_BPS = 1<<4, - XT_RATEEST_MATCH_PPS = 1<<5, -}; - -enum xt_rateest_match_mode { - XT_RATEEST_MATCH_NONE, - XT_RATEEST_MATCH_EQ, - XT_RATEEST_MATCH_LT, - XT_RATEEST_MATCH_GT, -}; - -struct xt_rateest_match_info { - char name1[IFNAMSIZ]; - char name2[IFNAMSIZ]; - __u16 flags; - __u16 mode; - __u32 bps1; - __u32 pps1; - __u32 bps2; - __u32 pps2; +struct xt_rateest_target_info { + char name[IFNAMSIZ]; + __s8 interval; + __u8 ewma_log; /* Used internally by the kernel */ - struct xt_rateest *est1 __attribute__((aligned(8))); - struct xt_rateest *est2 __attribute__((aligned(8))); + struct xt_rateest *est __attribute__((aligned(8))); }; -#endif /* _XT_RATEEST_MATCH_H */ +#endif /* _XT_RATEEST_TARGET_H */ diff --git a/include/linux/netfilter/xt_tcpmss.h b/include/linux/netfilter/xt_tcpmss.h index fbac56b..9a6960a 100644 --- a/include/linux/netfilter/xt_tcpmss.h +++ b/include/linux/netfilter/xt_tcpmss.h @@ -1,11 +1,12 @@ -#ifndef _XT_TCPMSS_MATCH_H -#define _XT_TCPMSS_MATCH_H +#ifndef _XT_TCPMSS_H +#define _XT_TCPMSS_H #include -struct xt_tcpmss_match_info { - __u16 mss_min, mss_max; - __u8 invert; +struct xt_tcpmss_info { + __u16 mss; }; -#endif /*_XT_TCPMSS_MATCH_H*/ +#define XT_TCPMSS_CLAMP_PMTU 0xffff + +#endif /* _XT_TCPMSS_H */ diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h index db79231..061bb34 100644 --- a/include/linux/netfilter_ipv4/ip_tables.h +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -93,6 +93,7 @@ struct ipt_ip { #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ #define IPT_F_GOTO 0x02 /* Set if jump is a goto */ #define IPT_F_MASK 0x03 /* All possible flag bits mask. */ +#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */ /* Values for "inv" field in struct ipt_ip. */ #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ diff --git a/include/linux/netfilter_ipv4/ipt_ecn.h b/include/linux/netfilter_ipv4/ipt_ecn.h index eabf95f..bb88d53 100644 --- a/include/linux/netfilter_ipv4/ipt_ecn.h +++ b/include/linux/netfilter_ipv4/ipt_ecn.h @@ -1,35 +1,33 @@ -/* iptables module for matching the ECN header in IPv4 and TCP header +/* Header file for iptables ipt_ECN target * - * (C) 2002 Harald Welte + * (C) 2002 by Harald Welte * * This software is distributed under GNU GPL v2, 1991 * - * ipt_ecn.h,v 1.4 2002/08/05 19:39:00 laforge Exp + * ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp */ -#ifndef _IPT_ECN_H -#define _IPT_ECN_H +#ifndef _IPT_ECN_TARGET_H +#define _IPT_ECN_TARGET_H #include -#include +#include #define IPT_ECN_IP_MASK (~XT_DSCP_MASK) -#define IPT_ECN_OP_MATCH_IP 0x01 -#define IPT_ECN_OP_MATCH_ECE 0x10 -#define IPT_ECN_OP_MATCH_CWR 0x20 +#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */ +#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */ +#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */ -#define IPT_ECN_OP_MATCH_MASK 0xce +#define IPT_ECN_OP_MASK 0xce -/* match info */ -struct ipt_ecn_info { - __u8 operation; - __u8 invert; - __u8 ip_ect; +struct ipt_ECN_info { + __u8 operation; /* bitset of operations */ + __u8 ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */ union { struct { - __u8 ect; + __u8 ece:1, cwr:1; /* TCP ECT bits */ } tcp; } proto; }; -#endif /* _IPT_ECN_H */ +#endif /* _IPT_ECN_TARGET_H */ diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h index 37bee44..f6ac169 100644 --- a/include/linux/netfilter_ipv4/ipt_ttl.h +++ b/include/linux/netfilter_ipv4/ipt_ttl.h @@ -1,5 +1,5 @@ -/* IP tables module for matching the value of the TTL - * (C) 2000 by Harald Welte */ +/* TTL modification module for IP tables + * (C) 2000 by Harald Welte */ #ifndef _IPT_TTL_H #define _IPT_TTL_H @@ -7,14 +7,14 @@ #include enum { - IPT_TTL_EQ = 0, /* equals */ - IPT_TTL_NE, /* not equals */ - IPT_TTL_LT, /* less than */ - IPT_TTL_GT, /* greater than */ + IPT_TTL_SET = 0, + IPT_TTL_INC, + IPT_TTL_DEC }; +#define IPT_TTL_MAXMODE IPT_TTL_DEC -struct ipt_ttl_info { +struct ipt_TTL_info { __u8 mode; __u8 ttl; }; diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h index 6e76dbc..ebd8ead 100644 --- a/include/linux/netfilter_ipv6/ip6t_hl.h +++ b/include/linux/netfilter_ipv6/ip6t_hl.h @@ -1,6 +1,6 @@ -/* ip6tables module for matching the Hop Limit value +/* Hop Limit modification module for ip6tables * Maciej Soltysiak - * Based on HW's ttl module */ + * Based on HW's TTL module */ #ifndef _IP6T_HL_H #define _IP6T_HL_H @@ -8,14 +8,14 @@ #include enum { - IP6T_HL_EQ = 0, /* equals */ - IP6T_HL_NE, /* not equals */ - IP6T_HL_LT, /* less than */ - IP6T_HL_GT, /* greater than */ + IP6T_HL_SET = 0, + IP6T_HL_INC, + IP6T_HL_DEC }; +#define IP6T_HL_MAXMODE IP6T_HL_DEC -struct ip6t_hl_info { +struct ip6t_HL_info { __u8 mode; __u8 hop_limit; }; diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 8374d29..6f65e12 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -26,8 +26,23 @@ #define NETLINK_ECRYPTFS 19 #define NETLINK_RDMA 20 #define NETLINK_CRYPTO 21 /* Crypto layer */ - -#define MAX_LINKS 32 +#define NETLINK_FF 30 +#define NETLINK_VOP 31 +#define NETLINK_KEY 32 +#define NETLINK_L2FLOW 33 + +#define NETLINK_VOIP 34 +#define NETLINK_DTAM 35 +#define NETLINK_MCH 36 +#define NETLINK_CONFIG 37 +#define NETLINK_GENPLAY 38 +#define NETLINK_COMADEBUG 39 +#define NETLINK_EEPROM 40 +#define NETLINK_SS7 41 +#define NETLINK_DSR 42 +#define NETLINK_MMI 43 + +#define MAX_LINKS 44 struct sockaddr_nl { __kernel_sa_family_t nl_family; /* AF_NETLINK */ diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h index 0b80c80..0456669 100644 --- a/include/linux/pfkeyv2.h +++ b/include/linux/pfkeyv2.h @@ -268,6 +268,7 @@ struct sadb_x_kmaddress { #define SADB_SAFLAGS_NOPMTUDISC 0x20000000 #define SADB_SAFLAGS_DECAP_DSCP 0x40000000 #define SADB_SAFLAGS_NOECN 0x80000000 +#define SADB_SAFLAGS_ESN 0x01000000 /* Security Association states */ #define SADB_SASTATE_LARVAL 0 diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 7281d5a..8ee6a23 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -173,8 +173,37 @@ struct tc_sfq_xstats { * * The only reason for this is efficiency, it is possible * to change these parameters in compile time. + * + * If you need to play with these values, use esfq instead. */ +/* ESFQ section */ + +enum +{ + /* traditional */ + TCA_SFQ_HASH_CLASSIC, + TCA_SFQ_HASH_DST, + TCA_SFQ_HASH_SRC, + TCA_SFQ_HASH_FWMARK, + /* conntrack */ + TCA_SFQ_HASH_CTORIGDST, + TCA_SFQ_HASH_CTORIGSRC, + TCA_SFQ_HASH_CTREPLDST, + TCA_SFQ_HASH_CTREPLSRC, + TCA_SFQ_HASH_CTNATCHG, +}; + +struct tc_esfq_qopt +{ + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ + unsigned hash_kind; /* Hash function to use for flow identification */ +}; + /* RED section */ enum { diff --git a/include/linux/random.h b/include/linux/random.h index f5e1311..5757fe6 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -34,6 +34,30 @@ /* Clear the entropy pool and associated counters. (Superuser only.) */ #define RNDCLEARPOOL _IO( 'R', 0x06 ) +#ifdef CONFIG_FIPS_RNG + +/* Size of seed value - equal to AES blocksize */ +#define AES_BLOCK_SIZE_BYTES 16 +#define SEED_SIZE_BYTES AES_BLOCK_SIZE_BYTES +/* Size of AES key */ +#define KEY_SIZE_BYTES 16 + +/* ioctl() structure used by FIPS 140-2 Tests */ +struct rand_fips_test { + unsigned char key[KEY_SIZE_BYTES]; /* Input */ + unsigned char datetime[SEED_SIZE_BYTES]; /* Input */ + unsigned char seed[SEED_SIZE_BYTES]; /* Input */ + unsigned char result[SEED_SIZE_BYTES]; /* Output */ +}; + +/* FIPS 140-2 RNG Variable Seed Test. (Superuser only.) */ +#define RNDFIPSVST _IOWR('R', 0x10, struct rand_fips_test) + +/* FIPS 140-2 RNG Monte Carlo Test. (Superuser only.) */ +#define RNDFIPSMCT _IOWR('R', 0x11, struct rand_fips_test) + +#endif /* #ifdef CONFIG_FIPS_RNG */ + struct rand_pool_info { int entropy_count; int buf_size; @@ -55,6 +79,10 @@ extern void add_input_randomness(unsigned int type, unsigned int code, unsigned int value); extern void add_interrupt_randomness(int irq, int irq_flags); +extern void random_input_words(__u32 *buf, size_t wordcount, int ent_count); +extern int random_input_wait(void); +#define HAS_RANDOM_INPUT_WAIT 1 + extern void get_random_bytes(void *buf, int nbytes); extern void get_random_bytes_arch(void *buf, int nbytes); void generate_random_uuid(unsigned char uuid_out[16]); diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 577592e..60706ca 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -624,6 +624,9 @@ extern void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, struct nlmsghdr *nlh, gfp_t flags); extern void rtnl_set_sk_err(struct net *net, u32 group, int error); extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics); +#ifdef CONFIG_ARCH_COMCERTO +extern int rtnetlink_put_metrics_2(struct sk_buff *skb, u32 *metrics, struct dst_entry *dst); +#endif extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, u32 ts, u32 tsage, long expires, u32 error); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 1b4ea29..2269080 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -414,6 +414,9 @@ struct sk_buff { kmemcheck_bitfield_end(flags1); __be16 protocol; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + __u32 ipsec_offload; +#endif void (*destructor)(struct sk_buff *skb); #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack *nfct; @@ -550,6 +553,17 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size, return __alloc_skb(size, priority, 1, NUMA_NO_NODE); } +#if defined(CONFIG_ARCH_COMCERTO) +extern struct sk_buff *__alloc_skb_header(unsigned int size, void* data, gfp_t gfp_mask, + int fclone, int node); +static inline struct sk_buff *alloc_skb_header(unsigned int size, + void* data, + gfp_t priority) +{ + return __alloc_skb_header(size, data, priority, 0, NUMA_NO_NODE); +} +#endif + extern void skb_recycle(struct sk_buff *skb); extern bool skb_recycle_check(struct sk_buff *skb, int skb_size); diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index d00e0ba..334f381 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -148,7 +148,7 @@ static __always_inline void *kmalloc(size_t size, gfp_t flags) return NULL; found: #ifdef CONFIG_ZONE_DMA - if (flags & GFP_DMA) + if (flags & __GFP_DMA) cachep = malloc_sizes[i].cs_dmacachep; else #endif diff --git a/include/linux/stddef.h b/include/linux/stddef.h index 6a40c76..5dab46c 100644 --- a/include/linux/stddef.h +++ b/include/linux/stddef.h @@ -16,6 +16,7 @@ enum { false = 0, true = 1 }; +#endif /* __KERNEL__ */ #undef offsetof #ifdef __compiler_offsetof @@ -23,6 +24,5 @@ enum { #else #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif -#endif /* __KERNEL__ */ #endif diff --git a/include/linux/usb.h b/include/linux/usb.h index 93629fc..7a7357a 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -531,6 +531,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, extern int usb_reset_device(struct usb_device *dev); extern void usb_queue_reset_device(struct usb_interface *dev); +extern struct usb_device *usb_find_device_by_name(const char *name); /* USB autosuspend and autoresume */ #ifdef CONFIG_USB_SUSPEND diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 1a7e1d2..ec5aeaa 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -201,6 +201,7 @@ struct otp_info { * modes (see "struct mtd_write_req") */ #define MEMWRITE _IOWR('M', 24, struct mtd_write_req) +#define MTDREFRESH _IO('M', 50) /* * Obsolete legacy interface. Keep it in order not to break userspace diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 44b1110..feeaa5e 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -94,6 +94,12 @@ extern void addrconf_join_solict(struct net_device *dev, extern void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr); +extern int (*ipv6_dev_get_saddr_hook)(struct net *net, + struct net_device *dev, + const struct in6_addr *daddr, + unsigned int srcprefs, + struct in6_addr *saddr); + static inline unsigned long addrconf_timeout_fixup(u32 timeout, unsigned unit) { diff --git a/include/net/flow.h b/include/net/flow.h index 2a7eefdd..2b4adb6 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -212,9 +212,17 @@ typedef struct flow_cache_object *(*flow_resolve_t)( struct net *net, const struct flowi *key, u16 family, u8 dir, struct flow_cache_object *oldobj, void *ctx); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +extern struct flow_cache_object *flow_cache_lookup( + struct net *net, const struct flowi *key, u16 family, + u8 dir, u8 *new_flow, flow_resolve_t resolver, void *ctx); +extern void flow_cache_remove( + const struct flowi *fl, unsigned short family, unsigned short dir); +#else extern struct flow_cache_object *flow_cache_lookup( struct net *net, const struct flowi *key, u16 family, u8 dir, flow_resolve_t resolver, void *ctx); +#endif extern void flow_cache_flush(void); extern void flow_cache_flush_deferred(void); diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index fc73e66..500e449 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -19,6 +19,9 @@ struct ip6_tnl { struct flowi fl; /* flowi template for xmit */ struct dst_entry *dst_cache; /* cached dst */ u32 dst_cookie; +#if defined(CONFIG_INET6_IPSEC_OFFLOAD) + u32 genid; +#endif }; /* Tunnel encapsulation limit destination sub-option */ diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 8a2b0ae..2a39687 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -100,6 +100,14 @@ struct nf_conn_help { #include #include +#if defined(CONFIG_COMCERTO_FP) +struct comcerto_fp_info { + int ifindex; + int iif; + u32 mark; +}; +#endif + struct nf_conn { /* Usage count in here is 1 for hash table/destruct timer, 1 per skb, plus 1 for any connection(s) we are `master' for */ @@ -128,12 +136,32 @@ struct nf_conn { u_int32_t secmark; #endif +#if defined(CONFIG_COMCERTO_FP) + struct comcerto_fp_info fp_info[IP_CT_DIR_MAX]; +#endif + /* Extensions */ struct nf_ct_ext *ext; #ifdef CONFIG_NET_NS struct net *ct_net; #endif +#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || \ + defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) + struct { + /* + * e.g. "http". NULL before decision. "unknown" after decision + * if no match. + */ + char *app_proto; + /* + * application layer data so far. NULL after match decision. + */ + char *app_data; + unsigned int app_data_len; + } layer7; +#endif + /* Storage reserved for other modules, must be the last member */ union nf_conntrack_proto proto; }; @@ -315,6 +343,9 @@ static inline bool nf_is_loopback_packet(const struct sk_buff *skb) struct kernel_param; +extern int nf_conntrack_set_dpi_allow_report(struct sk_buff *skb); +extern int nf_conntrack_set_dpi_allow_and_mark(struct sk_buff *skb, int mark); + extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp); extern unsigned int nf_conntrack_htable_size; extern unsigned int nf_conntrack_max; diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index aced085..f8fb25f 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -76,4 +76,8 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, extern spinlock_t nf_conntrack_lock ; +#ifdef CONFIG_COMCERTO_FP +#define COMCERTO_PERMANENT_TIMEOUT 1000 +#endif + #endif /* _NF_CONNTRACK_CORE_H */ diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 748f91f..19295e5 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -27,6 +27,9 @@ struct netns_xfrm { struct hlist_head *state_bydst; struct hlist_head *state_bysrc; struct hlist_head *state_byspi; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + struct hlist_head *state_byh; +#endif unsigned int state_hmask; unsigned int state_num; struct work_struct state_hash_work; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 921f627..9e1cca4 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -137,7 +137,10 @@ struct xfrm_state { }; struct hlist_node bysrc; struct hlist_node byspi; - +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + struct hlist_node byh; + u16 handle; +#endif atomic_t refcnt; spinlock_t lock; @@ -229,6 +232,11 @@ struct xfrm_state { /* Private data of this transformer, format is opaque, * interpreted by xfrm_type methods. */ void *data; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + /* Intended direction of this state, used for offloading */ + int dir; + int offloaded; +#endif }; static inline struct net *xs_net(struct xfrm_state *x) @@ -247,6 +255,13 @@ enum { XFRM_STATE_EXPIRED, XFRM_STATE_DEAD }; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +enum { + XFRM_STATE_DIR_UNKNOWN, + XFRM_STATE_DIR_IN, + XFRM_STATE_DIR_OUT, +}; +#endif /* callback structure passed from either netlink or pfkey */ struct km_event { @@ -302,6 +317,9 @@ struct xfrm_policy_afinfo { extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +extern struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); +#endif extern void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c); extern void km_state_notify(struct xfrm_state *x, const struct km_event *c); @@ -961,6 +979,35 @@ struct sec_path { struct xfrm_state *xvec[XFRM_MAX_DEPTH]; }; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +struct xfrm_input_shared +{ + struct sk_buff *skb; + int xfrm_nr, first, xfrm_encap; + struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; + __u16 encap_type; + int decaps; + u32 seq, spi; + unsigned int nhoff; + int nexthdr; + int (*callback)(struct xfrm_input_shared *sh); + atomic_t refcnt; +}; + + +static inline void xfrm_shared_get(struct xfrm_input_shared *sh) +{ + atomic_inc(&sh->refcnt); +} + +static inline void xfrm_shared_put(struct xfrm_input_shared *sh) +{ + if (atomic_dec_and_test(&sh->refcnt)) { + kfree(sh); + } +} +#endif + static inline int secpath_exists(struct sk_buff *skb) { #ifdef CONFIG_XFRM diff --git a/init/Kconfig b/init/Kconfig index b8dc1de..19aafe1 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -892,6 +892,10 @@ config RELAY If unsure, say N. +config CRASHLOG + bool "Crash logging" + depends on !NO_BOOTMEM && !HAVE_MEMBLOCK + config BLK_DEV_INITRD bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support" depends on BROKEN || !FRV diff --git a/init/main.c b/init/main.c index e937d9b..1ebba90 100644 --- a/init/main.c +++ b/init/main.c @@ -786,10 +786,7 @@ static noinline int init_post(void) printk(KERN_WARNING "Failed to execute %s. Attempting " "defaults...\n", execute_command); } - run_init_process("/sbin/init"); - run_init_process("/etc/init"); - run_init_process("/bin/init"); - run_init_process("/bin/sh"); + run_init_process("/etc/preinit"); panic("No init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance."); @@ -828,7 +825,7 @@ static int __init kernel_init(void * unused) /* Open the /dev/console on the rootfs, this should never fail */ if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - printk(KERN_WARNING "Warning: unable to open an initial console.\n"); + printk(KERN_WARNING "Please be patient, while OpenWrt loads ...\n"); (void) sys_dup(0); (void) sys_dup(0); diff --git a/kernel/Makefile b/kernel/Makefile index e898c5b..e1ac7e7 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -109,6 +109,7 @@ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o +obj-$(CONFIG_CRASHLOG) += crashlog.o ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) # According to Alan Modra , the -fno-omit-frame-pointer is diff --git a/kernel/events/core.c b/kernel/events/core.c index 2a4bf43..a2b2f1c 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5340,7 +5340,7 @@ static int perf_swevent_init(struct perf_event *event) break; } - if (event_id >= PERF_COUNT_SW_MAX) + if (event_id < 0 || event_id >= PERF_COUNT_SW_MAX) return -ENOENT; if (!event->parent) { diff --git a/kernel/exit.c b/kernel/exit.c index fde15f9..fd79f65 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -499,6 +499,7 @@ struct files_struct *get_files_struct(struct task_struct *task) return files; } +EXPORT_SYMBOL_GPL(get_files_struct); void put_files_struct(struct files_struct *files) { @@ -520,6 +521,7 @@ void put_files_struct(struct files_struct *files) rcu_read_unlock(); } } +EXPORT_SYMBOL_GPL(put_files_struct); void reset_files_struct(struct files_struct *files) { diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 4e316e1..f49d132 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -141,6 +141,446 @@ static ssize_t fscaps_show(struct kobject *kobj, } KERNEL_ATTR_RO(fscaps); +#if defined(CONFIG_COMCERTO_MDMA_PROF) +extern unsigned int mdma_time_counter[256]; // 16 -> 4000 us +extern unsigned int mdma_reqtime_counter[256]; // 16 -> 4000 us +extern unsigned int mdma_data_counter[256]; +extern unsigned int init_mdma_prof; +extern unsigned int enable_mdma_prof; + +static ssize_t comcerto_mdma_prof_enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int n; + buf[0] = '\0'; + n = 0; + if (enable_mdma_prof) + n += sprintf(buf, "MDMA profiling is enabled\n"); + else + n += sprintf(buf, "MDMA profiling is disabled\n"); + + return (n + 1); +} + +static ssize_t comcerto_mdma_prof_enable_store(struct kobject *kobj, + struct kobj_attribute *attr, char *buf, size_t count) +{ + unsigned int enable; + + if (kstrtouint(buf, 0, &enable)) + return -EINVAL; + + if (enable > 0) + enable_mdma_prof = 1; + else + enable_mdma_prof = 0; + + return count; +} +KERNEL_ATTR_RW(comcerto_mdma_prof_enable); + +static ssize_t comcerto_mdma_reqtiming_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of mdma request time\n"); + + for (i = 0; i < 255; i++) + { + if (mdma_reqtime_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] us\n", mdma_reqtime_counter[i], i << 4, (i + 1) << 4); + mdma_reqtime_counter[i] = 0; + } + } + if (mdma_reqtime_counter[255]) { + n += sprintf(buf + n, "%d >= %d us\n", mdma_reqtime_counter[255], 255 << 4); + mdma_reqtime_counter[255] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_mdma_reqtiming); + +static ssize_t comcerto_mdma_timing_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + init_mdma_prof = 0; + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of inter mdma request time\n"); + + for (i = 0; i < 255; i++) + { + if (mdma_time_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] us\n", mdma_time_counter[i], i << 4, (i + 1) << 4); + mdma_time_counter[i] = 0; + } + } + if (mdma_time_counter[255]) { + n += sprintf(buf + n, "%d >= %d us\n", mdma_time_counter[255], 255 << 4); + mdma_time_counter[255] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_mdma_timing); + +static ssize_t comcerto_mdma_data_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of mdma data length (up to 1M)\n"); + for (i = 0; i < 256; i++) + { + if (mdma_data_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] KB\n", mdma_data_counter[i], i << (13 - 10), (i + 1) << (13 - 10)); + mdma_data_counter[i] = 0; + } + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_mdma_data); +#endif + +#if defined(CONFIG_COMCERTO_SPLICE_PROF) +extern unsigned int splicew_time_counter[256]; // 4 ms -> 1S +extern unsigned int splicew_reqtime_counter[256]; // 4 ms -> 1S +extern unsigned int splicew_data_counter[256]; +extern unsigned int init_splicew_prof; +extern unsigned int enable_splice_prof; +static ssize_t comcerto_splice_prof_enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int n; + buf[0] = '\0'; + n = 0; + if (enable_splice_prof) + n += sprintf(buf, "Splice profiling is enabled\n"); + else + n += sprintf(buf, "Splice profiling is disabled\n"); + + return (n + 1); +} +static ssize_t comcerto_splice_prof_enable_store(struct kobject *kobj, + struct kobj_attribute *attr, char *buf, size_t count) +{ + unsigned int enable; + + if (kstrtouint(buf, 0, &enable)) + return -EINVAL; + + if (enable > 0) + enable_splice_prof = 1; + else + enable_splice_prof = 0; + + return count; +} +KERNEL_ATTR_RW(comcerto_splice_prof_enable); +static ssize_t comcerto_splicew_reqtiming_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of splice write time (up to 1 sec otherwise date is discarded)\n"); + + for (i = 0; i < 255; i++) + { + if (splicew_reqtime_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicew_reqtime_counter[i], (i * 8), (i * 8) + 8); + splicew_reqtime_counter[i] = 0; + } + } + if (splicew_reqtime_counter[255]) { + n += sprintf(buf + n, "%d > 1 second\n", splicew_reqtime_counter[255]); + splicew_reqtime_counter[255] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_splicew_reqtiming); +static ssize_t comcerto_splicew_timing_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + init_splicew_prof = 0; + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of inter splice write time (up to 1 sec otherwise date is discarded)\n"); + + for (i = 0; i < 255; i++) + { + if (splicew_time_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicew_time_counter[i], (i * 8), (i * 8) + 8); + splicew_time_counter[i] = 0; + } + } + if (splicew_time_counter[255]) { + n += sprintf(buf + n, "%d > 1 second\n", splicew_time_counter[255]); + splicew_time_counter[255] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_splicew_timing); +static ssize_t comcerto_splicew_data_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of splice write data length (up to 1M)\n"); + for (i = 0; i < 256; i++) + { + if (splicew_data_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] KB\n", splicew_data_counter[i], (i * 8), (i * 8) + 8); + splicew_data_counter[i] = 0; + } + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_splicew_data); + + +extern unsigned int splicer_time_counter[256]; // 4 ms -> 1S +extern unsigned int splicer_reqtime_counter[256]; // 4 ms -> 1S +extern unsigned int splicer_data_counter[256]; +extern unsigned int splicer_tcp_rsock_counter[64]; +extern unsigned int init_splicer_prof; +static ssize_t comcerto_splicer_reqtiming_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of splice read time\n"); + + for (i = 0; i < 255; i++) + { + if (splicer_reqtime_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicer_reqtime_counter[i], (i * 8), (i * 8) + 8); + splicer_reqtime_counter[i] = 0; + } + } + if (splicer_reqtime_counter[255]) { + n += sprintf(buf + n, "%d > 1 second\n", splicer_reqtime_counter[255]); + splicer_reqtime_counter[255] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_splicer_reqtiming); +static ssize_t comcerto_splicer_timing_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + init_splicer_prof = 0; + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of inter splice read time\n"); + + for (i = 0; i < 255; i++) + { + if (splicer_time_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicer_time_counter[i], (i * 8), (i * 8) + 8); + splicer_time_counter[i] = 0; + } + } + if (splicer_time_counter[255]) { + n += sprintf(buf + n, "%d > 1 second\n", splicer_time_counter[255]); + splicer_time_counter[255] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_splicer_timing); +static ssize_t comcerto_splicer_data_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of splice read data length (up to 1M)\n"); + for (i = 0; i < 256; i++) + { + if (splicer_data_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] KB\n", splicer_data_counter[i], (i * 8), (i * 8) + 8); + splicer_data_counter[i] = 0; + } + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_splicer_data); +static ssize_t comcerto_splicer_tcp_rsock_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of TCP receive queue size when splice read is performed\n"); + for (i = 0; i < 63; i++) + { + if (splicer_tcp_rsock_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] KB\n", splicer_tcp_rsock_counter[i], (i * 64), (i * 64) + 64); + splicer_tcp_rsock_counter[i] = 0; + } + } + if (splicer_tcp_rsock_counter[i]) { + n += sprintf(buf + n, "%d >= %d KB\n", splicer_tcp_rsock_counter[i], (i * 64)); + splicer_tcp_rsock_counter[i] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_splicer_tcp_rsock); +#endif + +#if defined(CONFIG_COMCERTO_AHCI_PROF) +extern unsigned int ahci_time_counter[256]; // 4 ms -> 1S +extern unsigned int ahci_data_counter[256]; +extern unsigned int init_ahci_prof; +extern unsigned int enable_ahci_prof; +static ssize_t comcerto_ahci_prof_enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int n; + buf[0] = '\0'; + n = 0; + if (enable_ahci_prof) + n += sprintf(buf, "AHCI profiling is enabled\n"); + else + n += sprintf(buf, "AHCI profiling is disabled\n"); + + return (n + 1); +} +static ssize_t comcerto_ahci_prof_enable_store(struct kobject *kobj, + struct kobj_attribute *attr, char *buf, size_t count) +{ + unsigned int enable; + + if (kstrtouint(buf, 0, &enable)) + return -EINVAL; + + if (enable > 0) + enable_ahci_prof = 1; + else + enable_ahci_prof = 0; + + return count; +} +KERNEL_ATTR_RW(comcerto_ahci_prof_enable); +static ssize_t comcerto_ahci_timing_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of inter ahci write time (up to 1 sec otherwise date is discarded)\n"); + init_ahci_prof = 0; + for (i = 0; i < 255; i++) + { + if (ahci_time_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] ms\n", ahci_time_counter[i], (i * 8), (i * 8) + 8); + ahci_time_counter[i] = 0; + } + } + if (ahci_time_counter[255]) { + n += sprintf(buf + n, "%d > 1 second\n", ahci_time_counter[255]); + ahci_time_counter[255] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_ahci_timing); +static ssize_t comcerto_ahci_data_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + n += sprintf(buf, "Histogram of ahci write data length (up to 1M)\n"); + + for (i = 0; i < 256; i++) + { + if (ahci_data_counter[i]) { + n += sprintf(buf + n, "%d in [%d-%d] KB\n", ahci_data_counter[i], (i * 8), (i * 8) + 8); + ahci_data_counter[i] = 0; + } + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_ahci_data); + + +extern unsigned int ahci_qc_comp_counter[33]; +static ssize_t comcerto_ahci_qc_comp_timing_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + int n; + + buf[0] = '\0'; + n = 0; + sprintf(buf, "Histogram of AHCI qc_complete time (in ms):\n"); + n = strlen(buf); + for (i = 0; i < 32; i++) + { + if (ahci_qc_comp_counter[i]) { + sprintf(buf + n, "%d, in [%d-%d]ms\n",ahci_qc_comp_counter[i], (i * 16), (i * 16) + 16); + n = strlen(buf); + ahci_qc_comp_counter[i] = 0; + } + } + if (ahci_qc_comp_counter[i]) { + sprintf(buf + n, "%d, in [> 512]ms\n",ahci_qc_comp_counter[i]); + n = strlen(buf); + ahci_qc_comp_counter[i] = 0; + } + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_ahci_qc_comp_timing); + + +extern unsigned int ahci_qc_no_free_slot; +static ssize_t comcerto_ahci_qc_no_free_slot_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int n; + + buf[0] = '\0'; + n = 0; + sprintf(buf, "AHCI qc_no_free_slot count: %d\n", ahci_qc_no_free_slot); + ahci_qc_no_free_slot = 0; + + n = strlen(buf); + + return (n + 1); +} +KERNEL_ATTR_RO(comcerto_ahci_qc_no_free_slot); +#endif + /* * Make /sys/kernel/notes give the raw contents of our kernel .notes section. */ @@ -182,6 +622,29 @@ static struct attribute * kernel_attrs[] = { &kexec_crash_size_attr.attr, &vmcoreinfo_attr.attr, #endif +#if defined(CONFIG_COMCERTO_MDMA_PROF) + &comcerto_mdma_prof_enable_attr.attr, + &comcerto_mdma_timing_attr.attr, + &comcerto_mdma_reqtiming_attr.attr, + &comcerto_mdma_data_attr.attr, +#endif +#if defined(CONFIG_COMCERTO_SPLICE_PROF) + &comcerto_splice_prof_enable_attr.attr, + &comcerto_splicew_timing_attr.attr, + &comcerto_splicew_reqtiming_attr.attr, + &comcerto_splicew_data_attr.attr, + &comcerto_splicer_timing_attr.attr, + &comcerto_splicer_reqtiming_attr.attr, + &comcerto_splicer_data_attr.attr, + &comcerto_splicer_tcp_rsock_attr.attr, +#endif +#if defined(CONFIG_COMCERTO_AHCI_PROF) + &comcerto_ahci_prof_enable_attr.attr, + &comcerto_ahci_timing_attr.attr, + &comcerto_ahci_data_attr.attr, + &comcerto_ahci_qc_comp_timing_attr.attr, + &comcerto_ahci_qc_no_free_slot_attr.attr, +#endif NULL }; diff --git a/kernel/module.c b/kernel/module.c index 95ecd9f..e065410 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -107,6 +107,9 @@ static LIST_HEAD(modules); #ifdef CONFIG_KGDB_KDB struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ #endif /* CONFIG_KGDB_KDB */ +#ifdef CONFIG_CRASHLOG +struct list_head *crashlog_modules = &modules; +#endif /* Block module loading/unloading? */ diff --git a/kernel/pid.c b/kernel/pid.c index fa5f722..2bf49fd 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -428,6 +428,7 @@ struct task_struct *find_task_by_vpid(pid_t vnr) { return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns); } +EXPORT_SYMBOL(find_task_by_vpid); struct pid *get_task_pid(struct task_struct *task, enum pid_type type) { diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index deb5461..d4c5692 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -148,6 +148,15 @@ config PM_TEST_SUSPEND You probably want to have your system's RTC driver statically linked, ensuring that it's available when this test runs. +config PM_SYSFS_MANUAL + bool "Driver model /sys/devices/.../power/state files" + depends on PM + default n + ---help--- + The driver model started out with a sysfs file intended to provide + a userspace hook for device power management. By this hook Non CPU + devices can be put in power off state. + config CAN_PM_TRACE def_bool y depends on PM_DEBUG && PM_SLEEP diff --git a/kernel/sched.c b/kernel/sched.c index ea85b0d..cadc958 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5300,6 +5300,7 @@ int can_nice(const struct task_struct *p, const int nice) return (nice_rlim <= task_rlimit(p, RLIMIT_NICE) || capable(CAP_SYS_NICE)); } +EXPORT_SYMBOL_GPL(can_nice); #ifdef __ARCH_WANT_SYS_NICE diff --git a/kernel/softirq.c b/kernel/softirq.c index 2c71d91..e34cc37 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -202,7 +202,11 @@ EXPORT_SYMBOL(local_bh_enable_ip); * we want to handle softirqs as soon as possible, but they * should not be able to lock up the box. */ +#if defined(CONFIG_ARCH_COMCERTO) +#define MAX_SOFTIRQ_RESTART 2 +#else #define MAX_SOFTIRQ_RESTART 10 +#endif asmlinkage void __do_softirq(void) { diff --git a/lib/Kconfig b/lib/Kconfig index 32f3e5a..0a5d625 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -197,16 +197,16 @@ config BCH_CONST_T # Textsearch support is select'ed if needed # config TEXTSEARCH - boolean + boolean "Textsearch support" config TEXTSEARCH_KMP - tristate + tristate "Textsearch KMP" config TEXTSEARCH_BM - tristate + tristate "Textsearch BM" config TEXTSEARCH_FSM - tristate + tristate "Textsearch FSM" config BTREE boolean diff --git a/lib/decompress.c b/lib/decompress.c index 3d766b7..af0c866 100644 --- a/lib/decompress.c +++ b/lib/decompress.c @@ -40,6 +40,7 @@ static const struct compress_format { { {037, 0236}, "gzip", gunzip }, { {0x42, 0x5a}, "bzip2", bunzip2 }, { {0x5d, 0x00}, "lzma", unlzma }, + { {0x6d, 0x00}, "lzma-openwrt", unlzma }, { {0xfd, 0x37}, "xz", unxz }, { {0x89, 0x4c}, "lzo", unlzo }, { {0, 0}, NULL, NULL } diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c index 26f89ad..41cb1ae 100644 --- a/lib/decompress_unlzo.c +++ b/lib/decompress_unlzo.c @@ -38,6 +38,7 @@ #include #include +#include #include #include diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 6d40244..df64823 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -50,6 +50,18 @@ static const char *kobject_actions[] = { [KOBJ_OFFLINE] = "offline", }; +u64 uevent_next_seqnum(void) +{ + u64 seq; + + mutex_lock(&uevent_sock_mutex); + seq = ++uevent_seqnum; + mutex_unlock(&uevent_sock_mutex); + + return seq; +} +EXPORT_SYMBOL_GPL(uevent_next_seqnum); + /** * kobject_action_type - translate action string to numeric type * @@ -366,6 +378,43 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) EXPORT_SYMBOL_GPL(add_uevent_var); #if defined(CONFIG_NET) +int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, + gfp_t allocation) +{ + struct uevent_sock *ue_sk; + int err = 0; + + /* send netlink message */ + mutex_lock(&uevent_sock_mutex); + list_for_each_entry(ue_sk, &uevent_sock_list, list) { + struct sock *uevent_sock = ue_sk->sk; + struct sk_buff *skb2; + + skb2 = skb_clone(skb, allocation); + if (!skb2) + break; + + err = netlink_broadcast(uevent_sock, skb2, pid, group, + allocation); + if (err) + break; + } + mutex_unlock(&uevent_sock_mutex); + + kfree_skb(skb); + return err; +} +#else +int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, + gfp_t allocation) +{ + kfree_skb(skb); + return 0; +} +#endif +EXPORT_SYMBOL_GPL(broadcast_uevent); + +#if defined(CONFIG_NET) static int uevent_net_init(struct net *net) { struct uevent_sock *ue_sk; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 2b49dd2..0462891 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -225,12 +225,40 @@ static ssize_t max_ratio_store(struct device *dev, } BDI_SHOW(max_ratio, bdi->max_ratio) +#ifdef CONFIG_ARCH_M86XXX +static ssize_t cpu0_bind_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct backing_dev_info *bdi = dev_get_drvdata(dev); + unsigned int flag; + + flag = simple_strtoul(buf, NULL, 10); + if (flag) + bdi->cpu0_bind = 1; + else + bdi->cpu0_bind = 0; + + return count; +} + +static ssize_t cpu0_bind_show(struct device *dev, + struct device_attribute *attr, char *page) +{ + struct backing_dev_info *bdi = dev_get_drvdata(dev); + + return snprintf(page, PAGE_SIZE-1, "%d\n", bdi->cpu0_bind); +} +#endif /* CONFIG_ARCH_M86XXX */ + #define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store) static struct device_attribute bdi_dev_attrs[] = { __ATTR_RW(read_ahead_kb), __ATTR_RW(min_ratio), __ATTR_RW(max_ratio), +#ifdef CONFIG_ARCH_M86XXX + __ATTR_RW(cpu0_bind), +#endif __ATTR_NULL, }; @@ -478,6 +506,10 @@ static int bdi_forker_thread(void *ptr) writeback_inodes_wb(&bdi->wb, 1024, WB_REASON_FORKER_THREAD); } else { +#ifdef CONFIG_ARCH_M86XXX + if (bdi->cpu0_bind) + kthread_bind(task, 0); +#endif /* * The spinlock makes sure we do not lose * wake-ups when racing with 'bdi_queue_work()'. diff --git a/mm/bootmem.c b/mm/bootmem.c index b863822..d514f27 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -178,6 +179,7 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) if (!bdata->node_bootmem_map) return 0; + crashlog_init_mem(bdata); start = bdata->node_min_pfn; end = bdata->node_low_pfn; diff --git a/mm/memory.c b/mm/memory.c index 0a7bb38..49c4646 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1403,6 +1403,7 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address, tlb_finish_mmu(&tlb, address, end); return end; } +EXPORT_SYMBOL_GPL(zap_page_range); /** * zap_vma_ptes - remove ptes mapping the vma @@ -3137,6 +3138,7 @@ static inline int check_stack_guard_page(struct vm_area_struct *vma, unsigned lo } return 0; } +EXPORT_SYMBOL_GPL(vmtruncate_range); /* * We enter with non-exclusive mmap_sem (to exclude vma changes, diff --git a/mm/mmap.c b/mm/mmap.c index 13b5685..db4abd4 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1895,7 +1895,7 @@ static void unmap_region(struct mm_struct *mm, unmap_vmas(&tlb, vma, start, end, &nr_accounted, NULL); vm_unacct_memory(nr_accounted); free_pgtables(&tlb, vma, prev ? prev->vm_end : FIRST_USER_ADDRESS, - next ? next->vm_start : 0); + next ? next->vm_start : mm->task_size); tlb_finish_mmu(&tlb, start, end); } @@ -2270,7 +2270,7 @@ void exit_mmap(struct mm_struct *mm) end = unmap_vmas(&tlb, vma, 0, -1, &nr_accounted, NULL); vm_unacct_memory(nr_accounted); - free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, 0); + free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, mm->task_size); tlb_finish_mmu(&tlb, 0, end); /* diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 62a7fa23..7dd1c75 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1668,6 +1668,11 @@ zonelist_scan: !cpuset_zone_allowed_softwall(zone, gfp_mask)) continue; +#if defined CONFIG_COMCERTO_ZONE_DMA_NCNB + if (!(gfp_mask & __GFP_DMA) && (zone_idx(zone) == ZONE_DMA)) + continue; +#endif + BUILD_BUG_ON(ALLOC_NO_WATERMARKS < NR_WMARK); if (!(alloc_flags & ALLOC_NO_WATERMARKS)) { unsigned long mark; diff --git a/mm/shmem.c b/mm/shmem.c index 83efac6..0fe046f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2629,6 +2629,16 @@ int vmtruncate_range(struct inode *inode, loff_t lstart, loff_t lend) /* common code */ +void shmem_set_file(struct vm_area_struct *vma, struct file *file) +{ + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = file; + vma->vm_ops = &shmem_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; +} +EXPORT_SYMBOL_GPL(shmem_set_file); + /** * shmem_file_setup - get an unlinked file living in tmpfs * @name: name for dentry (to be seen in /proc//maps @@ -2706,11 +2716,8 @@ int shmem_zero_setup(struct vm_area_struct *vma) if (IS_ERR(file)) return PTR_ERR(file); - if (vma->vm_file) - fput(vma->vm_file); - vma->vm_file = file; - vma->vm_ops = &shmem_vm_ops; - vma->vm_flags |= VM_CAN_NONLINEAR; + shmem_set_file(vma, file); + return 0; } diff --git a/mm/slab.c b/mm/slab.c index aea5e42..e2cada3 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -749,7 +749,7 @@ static inline struct kmem_cache *__find_general_cachep(size_t size, * for large kmalloc calls required. */ #ifdef CONFIG_ZONE_DMA - if (unlikely(gfpflags & GFP_DMA)) + if (unlikely(gfpflags & __GFP_DMA)) return csizep->cs_dmacachep; #endif return csizep->cs_cachep; @@ -2445,7 +2445,8 @@ kmem_cache_create (const char *name, size_t size, size_t align, cachep->flags = flags; cachep->gfpflags = 0; if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA)) - cachep->gfpflags |= GFP_DMA; + cachep->gfpflags |= __GFP_DMA; + cachep->buffer_size = size; cachep->reciprocal_buffer_size = reciprocal_value(size); @@ -2791,10 +2792,10 @@ static void cache_init_objs(struct kmem_cache *cachep, static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags) { if (CONFIG_ZONE_DMA_FLAG) { - if (flags & GFP_DMA) - BUG_ON(!(cachep->gfpflags & GFP_DMA)); + if (flags & __GFP_DMA) + BUG_ON(!(cachep->gfpflags & __GFP_DMA)); else - BUG_ON(cachep->gfpflags & GFP_DMA); + BUG_ON(cachep->gfpflags & __GFP_DMA); } } diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 1431458..b794238 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1239,6 +1239,7 @@ void unmap_kernel_range(unsigned long addr, unsigned long size) vunmap_page_range(addr, end); flush_tlb_kernel_range(addr, end); } +EXPORT_SYMBOL_GPL(unmap_kernel_range); int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages) { @@ -1376,6 +1377,7 @@ struct vm_struct *get_vm_area(unsigned long size, unsigned long flags) return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END, -1, GFP_KERNEL, __builtin_return_address(0)); } +EXPORT_SYMBOL_GPL(get_vm_area); struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags, void *caller) diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index 6dee7bf..9190ae4 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -6,7 +6,6 @@ config BRIDGE tristate "802.1d Ethernet Bridging" select LLC select STP - depends on IPV6 || IPV6=n ---help--- If you say Y here, then your Linux box will be able to act as an Ethernet bridge, which means that the different Ethernet segments it diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index e221f88..eb602b9 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -43,7 +43,11 @@ int br_dev_queue_push_xmit(struct sk_buff *skb) { /* ip_fragment doesn't copy the MAC header */ if (nf_bridge_maybe_copy_header(skb) || +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb) && (!skb->ipsec_offload))) { +#else (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))) { +#endif kfree_skb(skb); } else { skb_push(skb, ETH_HLEN); @@ -110,7 +114,7 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) /* called with rcu_read_lock */ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0) { - if (should_deliver(to, skb)) { + if (should_deliver(to, skb) && !(to->flags & BR_ISOLATE_MODE)) { if (skb0) deliver_clone(to, skb, __br_forward); else @@ -165,7 +169,8 @@ out: static void br_flood(struct net_bridge *br, struct sk_buff *skb, struct sk_buff *skb0, void (*__packet_hook)(const struct net_bridge_port *p, - struct sk_buff *skb)) + struct sk_buff *skb), + bool forward) { struct net_bridge_port *p; struct net_bridge_port *prev; @@ -173,6 +178,9 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, prev = NULL; list_for_each_entry_rcu(p, &br->port_list, list) { + if (forward && (p->flags & BR_ISOLATE_MODE)) + continue; + prev = maybe_deliver(prev, p, skb, __packet_hook); if (IS_ERR(prev)) goto out; @@ -196,14 +204,14 @@ out: /* called with rcu_read_lock */ void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb) { - br_flood(br, skb, NULL, __br_deliver); + br_flood(br, skb, NULL, __br_deliver, false); } /* called under bridge lock */ void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, struct sk_buff *skb2) { - br_flood(br, skb, skb2, __br_forward); + br_flood(br, skb, skb2, __br_forward, true); } #ifdef CONFIG_BRIDGE_IGMP_SNOOPING diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 5a31731..988c300 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -65,7 +65,7 @@ int br_handle_frame_finish(struct sk_buff *skb) br_multicast_rcv(br, p, skb)) goto drop; - if (p->state == BR_STATE_LEARNING) + if ((p->state == BR_STATE_LEARNING) && skb->protocol != htons(ETH_P_PAE)) goto drop; BR_INPUT_SKB_CB(skb)->brdev = br->dev; @@ -78,7 +78,11 @@ int br_handle_frame_finish(struct sk_buff *skb) dst = NULL; - if (is_broadcast_ether_addr(dest)) + if (skb->protocol == htons(ETH_P_PAE)) { + skb2 = skb; + /* Do not forward 802.1x/EAP frames */ + skb = NULL; + } else if (is_broadcast_ether_addr(dest)) skb2 = skb; else if (is_multicast_ether_addr(dest)) { mdst = br_mdb_get(br, skb); @@ -94,7 +98,8 @@ int br_handle_frame_finish(struct sk_buff *skb) skb2 = skb; br->dev->stats.multicast++; - } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { + } else if ((p->flags & BR_ISOLATE_MODE) || + ((dst = __br_fdb_get(br, dest)) && dst->is_local)) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index b9bba8f..4d6fb62 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -135,6 +135,7 @@ struct net_bridge_port unsigned long flags; #define BR_HAIRPIN_MODE 0x00000001 +#define BR_ISOLATE_MODE 0x00000002 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING u32 multicast_startup_queries_sent; diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 6229b62..cde52fe 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -149,6 +149,22 @@ static int store_hairpin_mode(struct net_bridge_port *p, unsigned long v) static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR, show_hairpin_mode, store_hairpin_mode); +static ssize_t show_isolate_mode(struct net_bridge_port *p, char *buf) +{ + int isolate_mode = (p->flags & BR_ISOLATE_MODE) ? 1 : 0; + return sprintf(buf, "%d\n", isolate_mode); +} +static ssize_t store_isolate_mode(struct net_bridge_port *p, unsigned long v) +{ + if (v) + p->flags |= BR_ISOLATE_MODE; + else + p->flags &= ~BR_ISOLATE_MODE; + return 0; +} +static BRPORT_ATTR(isolate_mode, S_IRUGO | S_IWUSR, + show_isolate_mode, store_isolate_mode); + #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) { @@ -181,6 +197,7 @@ static struct brport_attribute *brport_attrs[] = { &brport_attr_hold_timer, &brport_attr_flush, &brport_attr_hairpin_mode, + &brport_attr_isolate_mode, #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &brport_attr_multicast_router, #endif diff --git a/net/core/flow.c b/net/core/flow.c index e318c7e..f0eb4f8 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -22,6 +22,9 @@ #include #include #include +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +#include +#endif #include #include @@ -204,9 +207,15 @@ static int flow_key_compare(const struct flowi *key1, const struct flowi *key2, return 0; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +struct flow_cache_object * +flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, + u8 *new_flow, flow_resolve_t resolver, void *ctx) +#else struct flow_cache_object * flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, flow_resolve_t resolver, void *ctx) +#endif { struct flow_cache *fc = &flow_cache_global; struct flow_cache_percpu *fcp; @@ -216,6 +225,11 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, size_t keysize; unsigned int hash; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (new_flow) + *new_flow = 0; +#endif + local_bh_disable(); fcp = this_cpu_ptr(fc->percpu); @@ -281,8 +295,13 @@ nocache: flo = resolver(net, key, family, dir, flo, ctx); if (fle) { fle->genid = atomic_read(&flow_cache_genid); - if (!IS_ERR(flo)) + if (!IS_ERR(flo)) { fle->object = flo; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (new_flow) + *new_flow = 1; +#endif + } else fle->genid--; } else { @@ -358,6 +377,39 @@ void flow_cache_flush(void) put_online_cpus(); } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +void flow_cache_remove(const struct flowi *key, + unsigned short family, unsigned short dir) +{ + struct flow_cache *fc = &flow_cache_global; + struct flow_cache_percpu *fcp; + struct flow_cache_entry *fle; + struct hlist_node *entry; + size_t keysize; + unsigned int hash; + + local_bh_disable(); + fcp = this_cpu_ptr(fc->percpu); + + keysize = flow_key_size(family); + if (!keysize) + goto nocache; + + hash = flow_hash_code(fc, fcp, key, keysize); + + hlist_for_each_entry(fle, entry, &fcp->hash_table[hash], u.hlist) { + if((fle->family == family) && (fle->dir == dir) && (flow_key_compare(&fle->key, key, keysize) == 0)) { + hlist_del(&fle->u.hlist); + flow_entry_kill(fle); + break; + } + } + +nocache: + local_bh_enable(); +} +#endif + static void flow_cache_flush_task(struct work_struct *work) { flow_cache_flush(); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 0900a17..5af8260 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -621,7 +621,40 @@ nla_put_failure: return -EMSGSIZE; } EXPORT_SYMBOL(rtnetlink_put_metrics); +#ifdef CONFIG_ARCH_COMCERTO +int rtnetlink_put_metrics_2(struct sk_buff *skb, u32 *metrics, struct dst_entry *dst) +{ + struct nlattr *mx; + int i, valid = 0; + + mx = nla_nest_start(skb, RTA_METRICS); + if (mx == NULL) + return -ENOBUFS; + + for (i = 0; i < RTAX_MAX; i++) { + if (metrics[i]) { + valid++; + NLA_PUT_U32(skb, i+1, metrics[i]); + } + else if ((i + 1) == RTAX_MTU){ + valid++; + NLA_PUT_U32(skb, i+1, dst_mtu(dst)); + } + } + + if (!valid) { + nla_nest_cancel(skb, mx); + return 0; + } + + return nla_nest_end(skb, mx); +nla_put_failure: + nla_nest_cancel(skb, mx); + return -EMSGSIZE; +} +EXPORT_SYMBOL(rtnetlink_put_metrics_2); +#endif int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, u32 ts, u32 tsage, long expires, u32 error) { @@ -2001,6 +2034,10 @@ errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_LINK, err); } +#if defined(CONFIG_COMCERTO_FP) +EXPORT_SYMBOL(rtmsg_ifinfo); +#endif + /* Protected by RTNL sempahore. */ static struct rtattr **rta_buf; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7121d9b..a2e4aa0 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -216,6 +216,88 @@ nodata: } EXPORT_SYMBOL(__alloc_skb); +#if defined(CONFIG_ARCH_COMCERTO) +/** + * __alloc_skb_header - allocate a network buffer + * @size: size to allocate + * @gfp_mask: allocation mask + * @fclone: allocate from fclone cache instead of head cache + * and allocate a cloned (child) skb + * + * Allocate a new &sk_buff. The returned buffer has no headroom and a + * tail room of size bytes. The object has a reference count of one. + * The return is the buffer. On a failure the return is %NULL. + * + * Buffers may only be allocated from interrupts using a @gfp_mask of + * %GFP_ATOMIC. + */ +struct sk_buff *__alloc_skb_header(unsigned int size, void *data, gfp_t gfp_mask, + int fclone, int node) +{ + struct kmem_cache *cache; + struct skb_shared_info *shinfo; + struct sk_buff *skb; + + cache = fclone ? skbuff_fclone_cache : skbuff_head_cache; + + if (size <= SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) { + skb = NULL; + goto out; + } + + /* Get the HEAD */ + skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); + if (!skb) + goto out; + prefetchw(skb); + + /* kmalloc might give us more room than requested. + * Put skb_shared_info exactly at the end of allocated zone, + * to allow max possible filling before reallocation. + */ + size = SKB_WITH_OVERHEAD(ksize(data)); + prefetchw(data + size); + + /* + * Only clear those fields we need to clear, not those that we will + * actually initialise below. Hence, don't put any more fields after + * the tail pointer in struct sk_buff! + */ + memset(skb, 0, offsetof(struct sk_buff, tail)); + /* Account for allocated memory : skb + skb->head */ + skb->truesize = SKB_TRUESIZE(size); + atomic_set(&skb->users, 1); + skb->head = data; + skb->data = data; + skb_reset_tail_pointer(skb); + skb->end = skb->tail + size; +#ifdef NET_SKBUFF_DATA_USES_OFFSET + skb->mac_header = ~0U; +#endif + + /* make sure we initialize shinfo sequentially */ + shinfo = skb_shinfo(skb); + memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); + atomic_set(&shinfo->dataref, 1); + kmemcheck_annotate_variable(shinfo->destructor_arg); + + if (fclone) { + struct sk_buff *child = skb + 1; + atomic_t *fclone_ref = (atomic_t *) (child + 1); + + kmemcheck_annotate_bitfield(child, flags1); + kmemcheck_annotate_bitfield(child, flags2); + skb->fclone = SKB_FCLONE_ORIG; + atomic_set(fclone_ref, 1); + + child->fclone = SKB_FCLONE_UNAVAILABLE; + } +out: + return skb; +} +EXPORT_SYMBOL(__alloc_skb_header); +#endif + /** * __netdev_alloc_skb - allocate an skbuff for rx on a specific device * @dev: network device to receive on @@ -518,6 +600,9 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) #ifdef CONFIG_XFRM new->sp = secpath_get(old->sp); #endif +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + new->ipsec_offload = old->ipsec_offload; +#endif memcpy(new->cb, old->cb, sizeof(old->cb)); new->csum = old->csum; new->local_df = old->local_df; diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index cbb505b..b787148 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -339,6 +339,12 @@ config INET_ESP If unsure, say Y. +config INET_IPSEC_OFFLOAD + bool "IPsec Fast Path Processing offload" + depends on (INET_ESP || INET_AH) && COMCERTO_FP + ---help--- + Support for IPsec Fast Path offload. + config INET_IPCOMP tristate "IP: IPComp transformation" select INET_XFRM_TUNNEL diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index f2dc69c..3ad63e6 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -22,7 +22,8 @@ obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o obj-$(CONFIG_NET_IPGRE) += ip_gre.o obj-$(CONFIG_SYN_COOKIES) += syncookies.o obj-$(CONFIG_INET_AH) += ah4.o -obj-$(CONFIG_INET_ESP) += esp4.o +#obj-$(CONFIG_INET_ESP) += esp4.o +obj-$(CONFIG_INET_IPSEC_OFFLOAD) += esp4.o obj-$(CONFIG_INET_IPCOMP) += ipcomp.o obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o obj-$(CONFIG_INET_XFRM_MODE_BEET) += xfrm4_mode_beet.o diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 4f47e06..0ab609d 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -81,6 +81,16 @@ int ip_xfrm_me_harder(struct sk_buff *skb) if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) return 0; + +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + /* Mindspeed added WA: required to support 4o6 ipsec offload */ + if(skb->ipsec_offload) + { + if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) + return 0; + } +#endif + if (xfrm_decode_session(skb, &fl, AF_INET) < 0) return -1; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index f98a1cf..05fe243 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -81,9 +81,14 @@ ip_packet_match(const struct iphdr *ip, #define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg))) - if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr, + if (ipinfo->flags & IPT_F_NO_DEF_MATCH) + return true; + + if (FWINV(ipinfo->smsk.s_addr && + (ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr, IPT_INV_SRCIP) || - FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr, + FWINV(ipinfo->dmsk.s_addr && + (ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr, IPT_INV_DSTIP)) { dprintf("Source or dest mismatch.\n"); @@ -134,6 +139,29 @@ ip_packet_match(const struct iphdr *ip, return true; } +static void +ip_checkdefault(struct ipt_ip *ip) +{ + static const char iface_mask[IFNAMSIZ] = {}; + + if (ip->invflags || ip->flags & IPT_F_FRAG) + return; + + if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0) + return; + + if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0) + return; + + if (ip->smsk.s_addr || ip->dmsk.s_addr) + return; + + if (ip->proto) + return; + + ip->flags |= IPT_F_NO_DEF_MATCH; +} + static bool ip_checkentry(const struct ipt_ip *ip) { @@ -284,6 +312,33 @@ struct ipt_entry *ipt_next_entry(const struct ipt_entry *entry) return (void *)entry + entry->next_offset; } +static bool +ipt_handle_default_rule(struct ipt_entry *e, unsigned int *verdict) +{ + struct xt_entry_target *t; + struct xt_standard_target *st; + + if (e->target_offset != sizeof(struct ipt_entry)) + return false; + + if (!(e->ip.flags & IPT_F_NO_DEF_MATCH)) + return false; + + t = ipt_get_target(e); + if (t->u.kernel.target->target) + return false; + + st = (struct xt_standard_target *) t; + if (st->verdict == XT_RETURN) + return false; + + if (st->verdict >= 0) + return false; + + *verdict = (unsigned)(-st->verdict) - 1; + return true; +} + /* Returns one of the generic firewall policies, like NF_ACCEPT. */ unsigned int ipt_do_table(struct sk_buff *skb, @@ -308,6 +363,25 @@ ipt_do_table(struct sk_buff *skb, ip = ip_hdr(skb); indev = in ? in->name : nulldevname; outdev = out ? out->name : nulldevname; + + IP_NF_ASSERT(table->valid_hooks & (1 << hook)); + local_bh_disable(); + addend = xt_write_recseq_begin(); + private = table->private; + cpu = smp_processor_id(); + table_base = private->entries[cpu]; + jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; + stackptr = per_cpu_ptr(private->stackptr, cpu); + origptr = *stackptr; + + e = get_entry(table_base, private->hook_entry[hook]); + if (ipt_handle_default_rule(e, &verdict)) { + ADD_COUNTER(e->counters, skb->len, 1); + xt_write_recseq_end(addend); + local_bh_enable(); + return verdict; + } + /* We handle fragments by dealing with the first fragment as * if it was a normal packet. All other fragments are treated * normally, except that they will NEVER match rules that ask @@ -322,18 +396,6 @@ ipt_do_table(struct sk_buff *skb, acpar.family = NFPROTO_IPV4; acpar.hooknum = hook; - IP_NF_ASSERT(table->valid_hooks & (1 << hook)); - local_bh_disable(); - addend = xt_write_recseq_begin(); - private = table->private; - cpu = smp_processor_id(); - table_base = private->entries[cpu]; - jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; - stackptr = per_cpu_ptr(private->stackptr, cpu); - origptr = *stackptr; - - e = get_entry(table_base, private->hook_entry[hook]); - pr_debug("Entering %s(hook %u); sp at %u (UF %p)\n", table->name, hook, origptr, get_entry(table_base, private->underflow[hook])); @@ -561,7 +623,7 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net) } static int -check_entry(const struct ipt_entry *e, const char *name) +check_entry(struct ipt_entry *e, const char *name) { const struct xt_entry_target *t; @@ -570,6 +632,8 @@ check_entry(const struct ipt_entry *e, const char *name) return -EINVAL; } + ip_checkdefault(&e->ip); + if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset) return -EINVAL; @@ -931,6 +995,7 @@ copy_entries_to_user(unsigned int total_size, const struct xt_table_info *private = table->private; int ret = 0; const void *loc_cpu_entry; + u8 flags; counters = alloc_counters(table); if (IS_ERR(counters)) @@ -962,6 +1027,14 @@ copy_entries_to_user(unsigned int total_size, goto free_counters; } + flags = e->ip.flags & IPT_F_MASK; + if (copy_to_user(userptr + off + + offsetof(struct ipt_entry, ip.flags), + &flags, sizeof(flags)) != 0) { + ret = -EFAULT; + goto free_counters; + } + for (i = sizeof(struct ipt_entry); i < e->target_offset; i += m->u.match_size) { diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index 2b57e52..4bf3dc4 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -1,127 +1,138 @@ -/* IP tables module for matching the value of the IPv4 and TCP ECN bits +/* iptables module for the IPv4 and TCP ECN bits, Version 1.5 * - * (C) 2002 by Harald Welte + * (C) 2002 by Harald Welte * * 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 -#include -#include #include #include +#include +#include #include +#include #include #include -#include +#include -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match for IPv4"); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag modification"); -static inline bool match_ip(const struct sk_buff *skb, - const struct ipt_ecn_info *einfo) +/* set ECT codepoint from IP header. + * return false if there was an error. */ +static inline bool +set_ect_ip(struct sk_buff *skb, const struct ipt_ECN_info *einfo) { - return ((ip_hdr(skb)->tos & IPT_ECN_IP_MASK) == einfo->ip_ect) ^ - !!(einfo->invert & IPT_ECN_OP_MATCH_IP); + struct iphdr *iph = ip_hdr(skb); + + if ((iph->tos & IPT_ECN_IP_MASK) != (einfo->ip_ect & IPT_ECN_IP_MASK)) { + __u8 oldtos; + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return false; + iph = ip_hdr(skb); + oldtos = iph->tos; + iph->tos &= ~IPT_ECN_IP_MASK; + iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK); + csum_replace2(&iph->check, htons(oldtos), htons(iph->tos)); + } + return true; } -static inline bool match_tcp(const struct sk_buff *skb, - const struct ipt_ecn_info *einfo, - bool *hotdrop) +/* Return false if there was an error. */ +static inline bool +set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo) { - struct tcphdr _tcph; - const struct tcphdr *th; - - /* In practice, TCP match does this, so can't fail. But let's - * be good citizens. - */ - th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); - if (th == NULL) { - *hotdrop = false; + struct tcphdr _tcph, *tcph; + __be16 oldval; + + /* Not enough header? */ + tcph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); + if (!tcph) return false; - } - if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { - if (einfo->invert & IPT_ECN_OP_MATCH_ECE) { - if (th->ece == 1) - return false; - } else { - if (th->ece == 0) - return false; - } - } + if ((!(einfo->operation & IPT_ECN_OP_SET_ECE) || + tcph->ece == einfo->proto.tcp.ece) && + (!(einfo->operation & IPT_ECN_OP_SET_CWR) || + tcph->cwr == einfo->proto.tcp.cwr)) + return true; - if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { - if (einfo->invert & IPT_ECN_OP_MATCH_CWR) { - if (th->cwr == 1) - return false; - } else { - if (th->cwr == 0) - return false; - } - } + if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph))) + return false; + tcph = (void *)ip_hdr(skb) + ip_hdrlen(skb); + + oldval = ((__be16 *)tcph)[6]; + if (einfo->operation & IPT_ECN_OP_SET_ECE) + tcph->ece = einfo->proto.tcp.ece; + if (einfo->operation & IPT_ECN_OP_SET_CWR) + tcph->cwr = einfo->proto.tcp.cwr; + inet_proto_csum_replace2(&tcph->check, skb, + oldval, ((__be16 *)tcph)[6], 0); return true; } -static bool ecn_mt(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +ecn_tg(struct sk_buff *skb, const struct xt_action_param *par) { - const struct ipt_ecn_info *info = par->matchinfo; + const struct ipt_ECN_info *einfo = par->targinfo; - if (info->operation & IPT_ECN_OP_MATCH_IP) - if (!match_ip(skb, info)) - return false; + if (einfo->operation & IPT_ECN_OP_SET_IP) + if (!set_ect_ip(skb, einfo)) + return NF_DROP; - if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { - if (!match_tcp(skb, info, &par->hotdrop)) - return false; - } + if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR) && + ip_hdr(skb)->protocol == IPPROTO_TCP) + if (!set_ect_tcp(skb, einfo)) + return NF_DROP; - return true; + return XT_CONTINUE; } -static int ecn_mt_check(const struct xt_mtchk_param *par) +static int ecn_tg_check(const struct xt_tgchk_param *par) { - const struct ipt_ecn_info *info = par->matchinfo; - const struct ipt_ip *ip = par->entryinfo; + const struct ipt_ECN_info *einfo = par->targinfo; + const struct ipt_entry *e = par->entryinfo; - if (info->operation & IPT_ECN_OP_MATCH_MASK) + if (einfo->operation & IPT_ECN_OP_MASK) { + pr_info("unsupported ECN operation %x\n", einfo->operation); return -EINVAL; - - if (info->invert & IPT_ECN_OP_MATCH_MASK) + } + if (einfo->ip_ect & ~IPT_ECN_IP_MASK) { + pr_info("new ECT codepoint %x out of mask\n", einfo->ip_ect); return -EINVAL; - - if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR) && - (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) { - pr_info("cannot match TCP bits in rule for non-tcp packets\n"); + } + if ((einfo->operation & (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)) && + (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & XT_INV_PROTO))) { + pr_info("cannot use TCP operations on a non-tcp rule\n"); return -EINVAL; } - return 0; } -static struct xt_match ecn_mt_reg __read_mostly = { - .name = "ecn", +static struct xt_target ecn_tg_reg __read_mostly = { + .name = "ECN", .family = NFPROTO_IPV4, - .match = ecn_mt, - .matchsize = sizeof(struct ipt_ecn_info), - .checkentry = ecn_mt_check, + .target = ecn_tg, + .targetsize = sizeof(struct ipt_ECN_info), + .table = "mangle", + .checkentry = ecn_tg_check, .me = THIS_MODULE, }; -static int __init ecn_mt_init(void) +static int __init ecn_tg_init(void) { - return xt_register_match(&ecn_mt_reg); + return xt_register_target(&ecn_tg_reg); } -static void __exit ecn_mt_exit(void) +static void __exit ecn_tg_exit(void) { - xt_unregister_match(&ecn_mt_reg); + xt_unregister_target(&ecn_tg_reg); } -module_init(ecn_mt_init); -module_exit(ecn_mt_exit); +module_init(ecn_tg_init); +module_exit(ecn_tg_exit); diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index 6609a84..123ffde 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -73,6 +73,7 @@ static int map_addr(struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; unsigned int buflen; __be32 newaddr; @@ -85,7 +86,8 @@ static int map_addr(struct sk_buff *skb, unsigned int dataoff, } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip && ct->tuplehash[dir].tuple.dst.u.udp.port == port) { newaddr = ct->tuplehash[!dir].tuple.src.u3.ip; - newport = ct->tuplehash[!dir].tuple.src.u.udp.port; + newport = help->help.ct_sip_info.forced_dport ? : + ct->tuplehash[!dir].tuple.src.u.udp.port; } else return 1; @@ -121,6 +123,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); unsigned int coff, matchoff, matchlen; enum sip_header_types hdr; union nf_inet_addr addr; @@ -230,6 +233,20 @@ next: !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO)) return NF_DROP; + /* Mangle destination port for Cisco phones, then fix up checksums */ + if (dir == IP_CT_DIR_REPLY && help->help.ct_sip_info.forced_dport) { + struct udphdr *uh; + + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + uh = (struct udphdr *)(skb->data + ip_hdrlen(skb)); + uh->dest = help->help.ct_sip_info.forced_dport; + + if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, 0, 0, NULL, 0)) + return NF_DROP; + } + return NF_ACCEPT; } @@ -281,8 +298,10 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); __be32 newip; u_int16_t port; + __be16 srcport; char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; unsigned buflen; @@ -295,8 +314,9 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, /* If the signalling port matches the connection's source port in the * original direction, try to use the destination port in the opposite * direction. */ - if (exp->tuple.dst.u.udp.port == - ct->tuplehash[dir].tuple.src.u.udp.port) + srcport = help->help.ct_sip_info.forced_dport ? : + ct->tuplehash[dir].tuple.src.u.udp.port; + if (exp->tuple.dst.u.udp.port == srcport) port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); else port = ntohs(exp->tuple.dst.u.udp.port); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 8e79a9e..1a9a45e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3047,7 +3047,11 @@ static int rt_fill_info(struct net *net, if (rt->rt_dst != rt->rt_gateway) NLA_PUT_BE32(skb, RTA_GATEWAY, rt->rt_gateway); +#ifdef CONFIG_ARCH_COMCERTO + if (rtnetlink_put_metrics_2(skb, dst_metrics_ptr(&rt->dst), &rt->dst) < 0) +#else if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) +#endif goto nla_put_failure; if (rt->rt_mark) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 9a7c01e..dda279b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -592,7 +592,15 @@ static int __tcp_splice_read(struct sock *sk, struct tcp_splice_state *tss) return tcp_read_sock(sk, &rd_desc, tcp_splice_data_recv); } - +#if defined(CONFIG_COMCERTO_SPLICE_PROF) +unsigned int splicer_time_counter[256]; +unsigned int splicer_reqtime_counter[256]; +unsigned int splicer_data_counter[256]; +unsigned int splicer_tcp_rsock_counter[64]; +static struct timeval last_splicer; +unsigned int init_splicer_prof = 0; +extern unsigned int enable_splice_prof; +#endif /** * tcp_splice_read - splice data from TCP socket to a pipe * @sock: socket to splice from @@ -618,7 +626,28 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, long timeo; ssize_t spliced; int ret; - +#if defined(CONFIG_COMCERTO_SPLICE_PROF) + struct timeval now; + int diff_time_ms; + + if (enable_splice_prof) { + do_gettimeofday(&now); + if (init_splicer_prof) { + diff_time_ms = ((now.tv_sec - last_splicer.tv_sec) * 1000) + ((now.tv_usec - last_splicer.tv_usec) / 1000); + if (diff_time_ms < 1000) { + splicer_time_counter[diff_time_ms >> 3]++; + } + else { + splicer_time_counter[255]++; + } + } + if (len < (1 <<21)) + splicer_data_counter[(len >> 13) & 0xFF]++; + else + splicer_data_counter[255]++; + last_splicer = now; + } +#endif sock_rps_record_flow(sk); /* * We can't seek on a socket input @@ -629,7 +658,18 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, ret = spliced = 0; lock_sock(sk); - +#if defined(CONFIG_COMCERTO_SPLICE_PROF) + /* Need locked socket*/ + if (enable_splice_prof) { + const struct tcp_sock *tp = tcp_sk(sk); + int rsock_qsize = tp->rcv_nxt - tp->copied_seq; + + if (rsock_qsize < (4 * 1024 * 1024)) + splicer_tcp_rsock_counter[(rsock_qsize >> 16) & 0x3F]++; + else + splicer_tcp_rsock_counter[63]++; + } +#endif timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK); while (tss.len) { ret = __tcp_splice_read(sk, &tss); @@ -682,6 +722,23 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, release_sock(sk); +#if defined(CONFIG_COMCERTO_SPLICE_PROF) + if (enable_splice_prof) { + do_gettimeofday(&now); + + diff_time_ms = ((now.tv_sec - last_splicer.tv_sec) * 1000) + ((now.tv_usec - last_splicer.tv_usec) / 1000); + if (diff_time_ms < 1000) {//Don't record useless data + splicer_reqtime_counter[diff_time_ms >> 3]++; + } + else + splicer_reqtime_counter[255]++; + + if(!init_splicer_prof) + init_splicer_prof = 1; + + last_splicer = now; + } +#endif if (spliced) return spliced; diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c index 8280645..2b89f0d 100644 --- a/net/ipv4/xfrm4_tunnel.c +++ b/net/ipv4/xfrm4_tunnel.c @@ -3,6 +3,7 @@ * Copyright (C) 2003 David S. Miller (davem@redhat.com) */ + #include #include #include diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 36d7437..a348cf9 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -94,6 +94,12 @@ config INET6_ESP If unsure, say Y. +config INET6_IPSEC_OFFLOAD + bool "IPsec IPv6 Fast Path Processing offload" + depends on (INET6_ESP && COMCERTO_FP) + ---help--- + Support for IPsec IPv6 Fast Path offload. + config INET6_IPCOMP tristate "IPv6: IPComp transformation" select INET6_XFRM_TUNNEL @@ -222,6 +228,15 @@ config IPV6_SUBTREES If unsure, say N. +config IPV6_ETHERIP + tristate "EtherIP over IPv6: EtherIP-in-IPv6 tunnel" + depends on IPV6 + select INET6_TUNNEL + ---help--- + Support for EtherIP-in-IPv6 tunnels described in RFC 3378. + + If unsure, say N. + config IPV6_MROUTE bool "IPv6: multicast routing (EXPERIMENTAL)" depends on IPV6 && EXPERIMENTAL diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 4b20d56..d5e4842 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -23,7 +23,8 @@ ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o ipv6-objs += $(ipv6-y) obj-$(CONFIG_INET6_AH) += ah6.o -obj-$(CONFIG_INET6_ESP) += esp6.o +#obj-$(CONFIG_INET6_ESP) += esp6.o +obj-$(CONFIG_INET6_IPSEC_OFFLOAD) += esp6.o obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o @@ -36,7 +37,9 @@ obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_IPV6_SIT) += sit.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o +obj-$(CONFIG_IPV6_ETHERIP) += ethipip6.o obj-y += addrconf_core.o exthdrs_core.o output_core.o obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o +obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_stubs.o diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3afdd78..5b6e6a9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1110,7 +1110,7 @@ out: return ret; } -int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev, +static int __ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev, const struct in6_addr *daddr, unsigned int prefs, struct in6_addr *saddr) { @@ -1235,7 +1235,6 @@ try_nextdev: in6_ifa_put(hiscore->ifa); return 0; } -EXPORT_SYMBOL(ipv6_dev_get_saddr); int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, unsigned char banned_flags) @@ -4861,6 +4860,9 @@ int __init addrconf_init(void) ipv6_addr_label_rtnl_register(); + BUG_ON(ipv6_dev_get_saddr_hook != NULL); + rcu_assign_pointer(ipv6_dev_get_saddr_hook, __ipv6_dev_get_saddr); + return 0; errout: rtnl_af_unregister(&inet6_ops); @@ -4879,6 +4881,9 @@ void addrconf_cleanup(void) struct net_device *dev; int i; + rcu_assign_pointer(ipv6_dev_get_saddr_hook, NULL); + synchronize_rcu(); + unregister_netdevice_notifier(&ipv6_dev_notf); unregister_pernet_subsys(&addrconf_ops); ipv6_addr_label_cleanup(); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 2215d6b..18f8bad 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -152,8 +152,15 @@ static int ip6_finish_output2(struct sk_buff *skb) static int ip6_finish_output(struct sk_buff *skb) { + +#if defined(CONFIG_INET6_IPSEC_OFFLOAD) + if ((skb->ipsec_offload == 0) && + (skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || + dst_allfrag(skb_dst(skb))) +#else if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || - dst_allfrag(skb_dst(skb))) + dst_allfrag(skb_dst(skb))) +#endif return ip6_fragment(skb, ip6_finish_output2); else return ip6_finish_output2(skb); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 782f67a..2b5cede 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2446,8 +2446,11 @@ static int rt6_fill_node(struct net *net, ipv6_addr_copy(&saddr_buf, &rt->rt6i_prefsrc.addr); NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); } - +#ifdef CONFIG_ARCH_COMCERTO + if (rtnetlink_put_metrics_2(skb, dst_metrics_ptr(&rt->dst), &rt->dst) < 0) +#else if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) +#endif goto nla_put_failure; rcu_read_lock(); @@ -2509,6 +2512,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void struct rtmsg *rtm; struct flowi6 fl6; int err, iif = 0; + int flags = 0; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); if (err < 0) @@ -2537,6 +2541,9 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void if (tb[RTA_OIF]) fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]); + if (tb[RTA_MARK]) + fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); + if (iif) { struct net_device *dev; dev = __dev_get_by_index(net, iif); @@ -2544,6 +2551,14 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void err = -ENODEV; goto errout; } + + fl6.flowi6_iif = iif; + + if (!ipv6_addr_any(&fl6.saddr)) + flags |= RT6_LOOKUP_F_HAS_SADDR; + + if (rt6_need_strict(&fl6.daddr) && dev->type != ARPHRD_PIMREG) + flags |= RT6_LOOKUP_F_IFACE; } skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); @@ -2558,7 +2573,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void skb_reset_mac_header(skb); skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); - rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6); + if (iif) + rt = (struct rt6_info*) fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input); + else + rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6); + skb_dst_set(skb, &rt->dst); err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, diff --git a/net/key/af_key.c b/net/key/af_key.c index dc8d7ef..760ac5e 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -30,8 +30,184 @@ #include #include #include +#include #include +#include + +#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD) +#define NLKEY_SUPPORT 1 +#else +#undef NLKEY_SUPPORT +#endif + +#ifdef NLKEY_SUPPORT +#include +#include +#include + + +extern struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, + xfrm_address_t *prev_saddr, + xfrm_address_t *prev_daddr, + int family); +extern int xfrm_get_tos(struct flowi *fl, int family); +extern int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, + const struct flowi *fl, u16 family, u16 dir); + + +#define NLKEY_SA_CREATE 0x0A01 +#define NLKEY_SA_DELETE 0x0A02 +#define NLKEY_SA_FLUSH 0x0A03 +#define NLKEY_SA_SET_KEYS 0x0A04 +#define NLKEY_SA_SET_TUNNEL 0x0A05 +#define NLKEY_SA_SET_NATT 0x0A06 +#define NLKEY_SA_SET_STATE 0x0A07 +#define NLKEY_SA_SET_LIFETIME 0x0A08 +#define NLKEY_SA_NOTIFY 0x0A09 +#define NLKEY_SA_INFO_UPDATE 0x0A0C +#define NLKEY_FLOW_ADD 0x0A11 +#define NLKEY_FLOW_REMOVE 0x0A12 +#define NLKEY_FLOW_NOTIFY 0x0A13 +#define NLKEY_NULL_MSG 0x0000 + +#define NLKEY_HDR_LEN 4 +#define NLKEY_MSG_LEN 256 + +#define NLKEY_MAX_NUM_KEYS 2 +#define NLKEY_MAX_KEY_LEN (256 / 8) + +struct nlkey_msg { + /* message data */ + unsigned short fcode; + unsigned short length; + unsigned short payload[(NLKEY_MSG_LEN /sizeof(unsigned short))]; +}; +/* sizeof(nlkey_msg) = 4 + 256 */ + +struct nlkey_sa_id { + unsigned int spi; + unsigned char sa_type; + unsigned char proto_family; + unsigned char replay_window; +#define NLKEY_SAFLAGS_ESN 0x1 + unsigned char flags; + unsigned int dst_ip[4]; + unsigned int src_ip[4]; + unsigned short mtu; + unsigned short dev_mtu; + +}; +/* sizeof(nlkey_sa_id) = 24 */ + +struct nlkey_sa_create { + unsigned short sagd; + unsigned short rsvd; + struct nlkey_sa_id said; +}; +/* sizeof(nlkey_sa_delete) = 28 */ + +struct nlkey_sa_delete { + unsigned short sagd; + unsigned short rsvd; +}; +/* sizeof(nlkey_sa_delete) = 4 */ + +struct nlkey_sa_set_tunnel { + unsigned short sagd; + unsigned char rsvd; + unsigned char proto_family; + union { + struct iphdr ipv4h; + struct ipv6hdr ipv6h; + } h; +}; +/* sizeof(nlkey_sa_set_tunnel) = 36 */ + +struct nlkey_sa_set_natt { + unsigned short sagd; + unsigned short sport; + unsigned short dport; + unsigned short rsvd; +}; +/* sizeof(nlkey_sa_set_natt) = 4 */ + +struct nlkey_sa_set_state { + unsigned short sagd; + unsigned short rsvd; + unsigned short state; + unsigned short rsvd2; +}; +/* sizeof(nlkey_sa_set_natt) = 8 */ + +struct nlkey_key_desc { + unsigned short key_bits; + unsigned char key_alg; + unsigned char key_type; + unsigned char key[NLKEY_MAX_KEY_LEN]; +}; +/* sizeof(nlkey_key_desc) = 36 */ + +struct nlkey_sa_set_keys { + unsigned short sagd; + unsigned short rsvd; + unsigned short num_keys; + unsigned short rsvd2; + struct nlkey_key_desc keys[NLKEY_MAX_NUM_KEYS]; +}; +/* sizeof(nlkey_sa_set_keys) = 80 */ + +struct nlkey_lifetime_desc { + unsigned int allocations; + unsigned int bytes[2]; +}; +/* sizeof(nlkey_sa_set_lifetime) = 12 */ + +struct nlkey_sa_set_lifetime { + unsigned short sagd; + unsigned short rsvd; + struct nlkey_lifetime_desc hard_time; + struct nlkey_lifetime_desc soft_time; + struct nlkey_lifetime_desc current_time; +}; +/* sizeof(nlkey_sa_set_lifetime) = 40 */ + +/* SA notifications */ +#define IPSEC_SOFT_EXPIRE 0 +#define IPSEC_HARD_EXPIRE 1 + +struct nlkey_sa_notify { + unsigned short sagd; + unsigned short rsvd; + unsigned int action; +}; +/* sizeof(nlkey_sa_notify) = 8 */ + +/* SA Info update */ + +struct nlkey_sa_info { + unsigned short sagd; + unsigned short rsvd; + unsigned long long bytes; + unsigned long long packets; +}; +/* sizeof(nlkey_sa_info) = */ + + +static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c); +static void ipsec_nlkey_rcv(struct sk_buff *skb); +static void ipsec_nlkey_init(void); +static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code); +static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, + const struct km_event *c, unsigned short *msg_id); +static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, const struct km_event *c, struct nlkey_sa_id *said); + +/* netlink NETLINK_KEY socket */ +struct sock *nlkey_socket = NULL; + +#endif +/************************************************************************************/ + #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x)) #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x)) @@ -837,6 +1013,8 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP; if (x->props.flags & XFRM_STATE_NOPMTUDISC) sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC; + if (x->props.flags & XFRM_STATE_ESN) + sa->sadb_sa_flags |= SADB_SAFLAGS_ESN; /* hard time */ if (hsc & 2) { @@ -1104,6 +1282,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, x->props.flags |= XFRM_STATE_DECAP_DSCP; if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC) x->props.flags |= XFRM_STATE_NOPMTUDISC; + if (sa->sadb_sa_flags & SADB_SAFLAGS_ESN) + x->props.flags |= XFRM_STATE_ESN; lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1]; if (lifetime != NULL) { @@ -1430,7 +1610,11 @@ static inline int event2keytype(int event) } /* ADD/UPD/DEL */ +#if NLKEY_SUPPORT +static int key_notify_sa(struct net *net, struct xfrm_state *x, const struct km_event *c) +#else static int key_notify_sa(struct xfrm_state *x, const struct km_event *c) +#endif { struct sk_buff *skb; struct sadb_msg *hdr; @@ -1451,6 +1635,10 @@ static int key_notify_sa(struct xfrm_state *x, const struct km_event *c) pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x)); +#ifdef NLKEY_SUPPORT + /* now sent message also to the user space through NETLINK_KEY socket*/ + ipsec_nlkey_send(net, x, c); +#endif return 0; } @@ -1689,7 +1877,11 @@ static int unicast_flush_resp(struct sock *sk, const struct sadb_msg *ihdr) return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk)); } +#if defined(NLKEY_SUPPORT) +static int key_notify_sa_flush(struct net *net, const struct km_event *c) +#else static int key_notify_sa_flush(const struct km_event *c) +#endif { struct sk_buff *skb; struct sadb_msg *hdr; @@ -1709,6 +1901,11 @@ static int key_notify_sa_flush(const struct km_event *c) pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net); +#ifdef NLKEY_SUPPORT + /* now sent message also to the user space through NETLINK_KEY socket*/ + ipsec_nlkey_send(net, NULL, c); +#endif + return 0; } @@ -2936,7 +3133,11 @@ static int key_notify_policy_expire(struct xfrm_policy *xp, const struct km_even return 0; } +#if defined(NLKEY_SUPPORT) +static int key_notify_sa_expire(struct net *net, struct xfrm_state *x, const struct km_event *c) +#else static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c) +#endif { struct sk_buff *out_skb; struct sadb_msg *out_hdr; @@ -2963,6 +3164,11 @@ static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c) out_hdr->sadb_msg_pid = 0; pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x)); + +#ifdef NLKEY_SUPPORT + /* now sent message also to the user space through NETLINK_KEY socket*/ + ipsec_nlkey_send(net, x, c); +#endif return 0; } @@ -2976,13 +3182,25 @@ static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c) switch (c->event) { case XFRM_MSG_EXPIRE: +#if NLKEY_SUPPORT + return key_notify_sa_expire(net, x, c); +#else return key_notify_sa_expire(x, c); +#endif case XFRM_MSG_DELSA: case XFRM_MSG_NEWSA: case XFRM_MSG_UPDSA: - return key_notify_sa(x, c); +#if NLKEY_SUPPORT + return key_notify_sa(net, x, c); +#else + return key_notify_sa(x, c); +#endif case XFRM_MSG_FLUSHSA: +#if NLKEY_SUPPORT + return key_notify_sa_flush(net, c); +#else return key_notify_sa_flush(c); +#endif case XFRM_MSG_NEWAE: /* not yet supported */ break; default: @@ -3754,6 +3972,539 @@ static struct xfrm_mgr pfkeyv2_mgr = .migrate = pfkey_send_migrate, }; + +#ifdef NLKEY_SUPPORT +extern struct xfrm_state *xfrm_state_lookup_byhandle(struct net *net, u16 handle); + +static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code) +{ + unsigned nlkey_code; + + switch (sa_code) + { + case XFRM_MSG_DELSA: + nlkey_code = NLKEY_SA_DELETE; + break; + case XFRM_MSG_NEWSA: + case XFRM_MSG_UPDSA: + nlkey_code = NLKEY_SA_CREATE; + break; + case XFRM_MSG_FLUSHSA: + nlkey_code = NLKEY_SA_FLUSH; + break; + case XFRM_MSG_EXPIRE: + nlkey_code = NLKEY_SA_SET_STATE; + break; + default: + nlkey_code = NLKEY_NULL_MSG; + break; + } + + return nlkey_code; +} + +static void ipsec_nlkey_rcv(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = NULL; + struct nlkey_msg *msg = NULL; + struct flowi flow; + unsigned short *p; + unsigned short family, dir; + struct xfrm_state *x; + struct nlkey_sa_notify sa_notify_msg; + struct nlkey_sa_info sa_info_msg; + + /* extract message from skb */ + nlh = (struct nlmsghdr *)skb->data; + + msg = (struct nlkey_msg *)NLMSG_DATA(nlh); + + //printk(KERN_INFO "ipsec_nlkey_rcv fcode: 0x%x length: %d bytes\n",msg->fcode,msg->length); + + /* process command received from user space */ + switch(msg->fcode) + { + case NLKEY_FLOW_REMOVE: + //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_FLOW_REMOVE\n"); + p = msg->payload; + memcpy(&flow, p, sizeof(struct flowi)); p += sizeof(struct flowi)/2; + family = *p; p++; + dir = *p; p++; + flow_cache_remove(&flow, family, dir); + break; + + case NLKEY_SA_NOTIFY: + //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_SA_NOTIFY\n"); + memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify)); + x = xfrm_state_lookup_byhandle(&init_net, sa_notify_msg.sagd); + if (x) { + spin_lock(&x->lock); + + if (sa_notify_msg.action) { + // hard expired + x->km.state = XFRM_STATE_EXPIRED; + tasklet_hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL); + } + else if (!x->km.dying) { + x->km.dying = 1; + km_state_expired(x, 0, 0); + } + + spin_unlock(&x->lock); + xfrm_state_put(x); + } + break; + + case NLKEY_SA_INFO_UPDATE: + memcpy(&sa_info_msg, msg->payload, sizeof(struct nlkey_sa_info)); + + x = xfrm_state_lookup_byhandle(&init_net,sa_info_msg.sagd); + if (x) { + spin_lock(&x->lock); + + x->curlft.bytes = sa_info_msg.bytes; + x->curlft.packets = sa_info_msg.packets; + + spin_unlock(&x->lock); + xfrm_state_put(x); + } + break; + default: + //printk(KERN_INFO "ipsec_nlkey_rcv fcode 0x%x not supported\n", msg->fcode); + break; + } + +} + +extern struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, + xfrm_address_t *saddr, + xfrm_address_t *daddr, + int family); +static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, + const struct km_event *c, struct nlkey_sa_id *said) +{ + + struct flowi fl; + int tos; + xfrm_address_t saddr, daddr; + struct dst_entry *dst; + int rc = 0; + + memset(&fl, 0, sizeof(struct flowi)); + + /* SPI */ + said->spi = x->id.spi; + /* SA Type (AH or ESP) */ + said->sa_type = x->id.proto; + /* Protocol Family (IPv4 or IPv6) */ + said->proto_family = x->props.family; + /* Replay window */ + said->replay_window = x->props.replay_window; + /* Destination IP Address */ + if(x->props.family == AF_INET6) { + memcpy(&said->dst_ip, x->id.daddr.a6, sizeof(struct in6_addr)); + ipv6_addr_copy(&fl.u.ip6.daddr, (struct in6_addr *)x->id.daddr.a6); + memcpy(&said->src_ip, x->props.saddr.a6, sizeof(struct in6_addr)); + } + else { + said->dst_ip[0] = x->id.daddr.a4; + fl.u.ip4.daddr = x->id.daddr.a4; + said->src_ip[0] = x->props.saddr.a4; + } + said->mtu = 0; + + if(x->props.flags & XFRM_STATE_ESN) + said->flags = NLKEY_SAFLAGS_ESN; + xfrm_flowi_addr_get(&fl, &saddr, &daddr, x->props.family); + + tos = xfrm_get_tos(&fl, x->props.family); + if (tos < 0) { + printk(KERN_ERR "%s:%d: FIXME\n",__FUNCTION__,__LINE__); + rc = -1; + goto error; + } + + dst = __xfrm_dst_lookup(net, tos, NULL, &daddr, x->props.family); + if (IS_ERR(dst)) { + printk(KERN_ERR "%s:%d: FIXME\n",__FUNCTION__,__LINE__); + rc = -1; + goto error; + } + said->dev_mtu = dst_mtu(dst); + said->mtu = xfrm_state_mtu(x,dst_mtu(dst)); + + dst_release(dst); +error: + return rc; +} + +static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, + const struct km_event *c, unsigned short *msg_id) +{ + struct nlkey_sa_id sa_id_msg; + struct nlkey_sa_create sa_create_msg; + struct nlkey_sa_delete sa_delete_msg; + struct nlkey_sa_set_keys sa_set_keys_msg; + struct nlkey_sa_set_tunnel sa_set_tunnel_msg; + struct nlkey_sa_set_natt sa_set_natt_msg; + struct nlkey_sa_set_state sa_set_state_msg; + struct nlkey_sa_set_lifetime sa_set_lifetime_msg; + struct nlkey_msg msg; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + gfp_t allocation = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + unsigned char tunnel, keys, natt, state, lifetime; + + /* supported SA informations */ + keys = 1; state = 1; tunnel = 1; lifetime = 1; natt = 1; + + /* next message to build */ + memset(&msg, 0, sizeof(struct nlkey_msg)); + msg.fcode = *msg_id; + + //printk(KERN_INFO "\n\nipsec_xfrm2nlkey: processing event 0x%x\n", msg.fcode); + + switch (msg.fcode) + { + case NLKEY_SA_CREATE: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_CREATE\n"); + if(x) { + /* some check before builing message */ + if((x->id.proto != IPPROTO_ESP) && (x->id.proto != IPPROTO_AH)) { + printk(KERN_ERR "ipsec_xfrm2nlkey: protocol %d not supported\n", x->id.proto); + *msg_id = NLKEY_NULL_MSG; + goto exit; + } + memset(&sa_create_msg, 0, sizeof(struct nlkey_sa_create)); + + /* SA global handler */ + sa_create_msg.sagd = x->handle; + + /* SA identifier */ + if(ipsec_nlkey_set_said(net, x, c, &sa_create_msg.said) < 0) + { + printk(KERN_ERR "%s: set sa ID failed\n", __func__); + *msg_id = NLKEY_NULL_MSG; /* next message */ + goto exit; + } + memcpy(msg.payload, &sa_create_msg, sizeof(struct nlkey_sa_create)); + msg.length = sizeof(struct nlkey_sa_create); + *msg_id = NLKEY_SA_SET_KEYS; /* next message */ + } else { + *msg_id = NLKEY_NULL_MSG; /* next message */ + goto exit; + } + + break; + + case NLKEY_SA_SET_KEYS: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_KEYS\n"); + if(keys) { + memset(&sa_set_keys_msg, 0, sizeof(struct nlkey_sa_set_keys)); + + /* SA global handler */ + sa_set_keys_msg.sagd = x->handle; + + /* auth key */ + if(x->aalg) { + if (x->aalg->alg_key_len) { + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aalg->alg_key_len; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.aalgo; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 0; + memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aalg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8)); + //printk(KERN_INFO "ipsec_xfrm2nlkey: AUTH - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); + sa_set_keys_msg.num_keys++; + } + } + /* encrypt key */ + if(x->ealg) { + if (x->ealg->alg_key_len) { + + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->ealg->alg_key_len; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.ealgo; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1; + memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->ealg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8)); + //printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); + sa_set_keys_msg.num_keys++; + } + } + memcpy(msg.payload, &sa_set_keys_msg, sizeof(struct nlkey_sa_set_keys)); + msg.length = sizeof(struct nlkey_sa_set_keys); + *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_TUNNEL: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_TUNNEL\n"); + if(tunnel && (x->props.mode == XFRM_MODE_TUNNEL)) { + memset(&sa_set_tunnel_msg, 0, sizeof(struct nlkey_sa_set_tunnel)); + + /* SA global handler */ + sa_set_tunnel_msg.sagd = x->handle; + + /* Tunnel */ + sa_set_tunnel_msg.proto_family = x->props.family; + if(x->props.family == AF_INET6) { + struct ipv6hdr *top_iph = &sa_set_tunnel_msg.h.ipv6h; + int dsfield; + top_iph->version = 6; + top_iph->priority = 0; + top_iph->flow_lbl[0] = 0; + top_iph->flow_lbl[1] = 0; + top_iph->flow_lbl[2] = 0; + top_iph->nexthdr = IPPROTO_IPIP; + dsfield = ipv6_get_dsfield(top_iph); + dsfield = INET_ECN_encapsulate(dsfield, dsfield); + if (x->props.flags & XFRM_STATE_NOECN) + dsfield &= ~INET_ECN_MASK; + ipv6_change_dsfield(top_iph, 0, dsfield); + top_iph->hop_limit = 64; + memcpy(&top_iph->daddr, x->id.daddr.a6, sizeof(struct in6_addr)); + memcpy(&top_iph->saddr, x->props.saddr.a6, sizeof(struct in6_addr)); + //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv6 tunnel\n"); + //printk(KERN_INFO "dst: %x %x %x %x\n", x->id.daddr.a6[0], x->id.daddr.a6[1], x->id.daddr.a6[2], x->id.daddr.a6[3]); + //(KERN_INFO "src: %x %x %x %x\n", x->props.saddr.a6[0], x->props.saddr.a6[1], x->props.saddr.a6[2], x->props.saddr.a6[3]); + } + else { + struct iphdr *top_iph = &sa_set_tunnel_msg.h.ipv4h; + top_iph->ihl = 5; + top_iph->version = 4; + top_iph->tos = 0; + top_iph->frag_off = 0; + top_iph->ttl = 64; + top_iph->saddr = x->props.saddr.a4; + top_iph->daddr = x->id.daddr.a4; + //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv4 tunnel dst:%x - src:%x \n", x->id.daddr.a4, x->props.saddr.a4); + } + memcpy(msg.payload, &sa_set_tunnel_msg, sizeof(struct nlkey_sa_set_tunnel)); + msg.length = sizeof(struct nlkey_sa_set_tunnel); + *msg_id = NLKEY_SA_SET_NATT; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_NATT; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_NATT: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_NATT\n"); + if((natt) && (x->encap)){ + memset(&sa_set_natt_msg, 0, sizeof(struct nlkey_sa_set_natt)); + + /* SA global handler */ + sa_set_natt_msg.sagd = x->handle; + sa_set_natt_msg.sport = x->encap->encap_sport; + sa_set_natt_msg.dport = x->encap->encap_dport; + //printk(KERN_INFO "src port: %d dst port: %d \n", ntohs(sa_set_natt_msg.sport), ntohs( sa_set_natt_msg.dport)); + memcpy(msg.payload, &sa_set_natt_msg, sizeof(struct nlkey_sa_set_natt)); + msg.length = sizeof(struct nlkey_sa_set_natt); + *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_LIFETIME: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_LIFETIME\n"); + if(lifetime) { + memset(&sa_set_lifetime_msg, 0, sizeof(struct nlkey_sa_set_lifetime)); + + /* SA global handler */ + sa_set_lifetime_msg.sagd = x->handle; + + /* hard time */ + sa_set_lifetime_msg.hard_time.allocations = _X2KEY(x->lft.hard_packet_limit); + if(_X2KEY(x->lft.hard_byte_limit)) + memcpy(sa_set_lifetime_msg.hard_time.bytes, &x->lft.hard_byte_limit, sizeof(uint64_t)); + + /* soft time */ + sa_set_lifetime_msg.soft_time.allocations = _X2KEY(x->lft.soft_packet_limit); + if(_X2KEY(x->lft.soft_byte_limit)) + memcpy(sa_set_lifetime_msg.soft_time.bytes, &x->lft.soft_byte_limit, sizeof(uint64_t)); + + /* current time */ + sa_set_lifetime_msg.current_time.allocations = x->curlft.packets; + memcpy(sa_set_lifetime_msg.current_time.bytes, &x->curlft.bytes, sizeof(uint64_t)); + + memcpy(msg.payload, &sa_set_lifetime_msg, sizeof(struct nlkey_sa_set_lifetime)); + msg.length = sizeof(struct nlkey_sa_set_lifetime); + *msg_id = NLKEY_SA_SET_STATE; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_STATE; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_STATE: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SET_STATE\n"); + if(state) { + memset(&sa_set_state_msg, 0, sizeof(struct nlkey_sa_set_state)); + memset(&sa_id_msg, 0, sizeof(struct nlkey_sa_id)); + + /* SA global handler */ + sa_set_state_msg.sagd = x->handle; + /* State */ + sa_set_state_msg.state = x->km.state; + // TODO: set the offloaded state once ack received ! + x->offloaded = 1; + atomic_inc(&flow_cache_genid); + + memcpy(msg.payload, &sa_set_state_msg, sizeof(struct nlkey_sa_set_state)); + msg.length = sizeof(struct nlkey_sa_set_state); + *msg_id = NLKEY_NULL_MSG; /* next message */ + } else { + *msg_id = NLKEY_NULL_MSG; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_DELETE: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_DELETE\n"); + memset(&sa_delete_msg, 0, sizeof(struct nlkey_sa_delete)); + + /* SA global handler */ + sa_delete_msg.sagd = x->handle; + memcpy(msg.payload, &sa_delete_msg, sizeof(struct nlkey_sa_delete)); + msg.length = sizeof(struct nlkey_sa_delete); + atomic_inc(&flow_cache_genid); + + + *msg_id = NLKEY_NULL_MSG; /* next message */ + break; + + case NLKEY_SA_FLUSH: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_FLUSH\n"); + /* No data required for flush SA command */ + atomic_inc(&flow_cache_genid); + + *msg_id = NLKEY_NULL_MSG; /* next message */ + break; + + default: + printk(KERN_ERR "ipsec_xfrm2nlkey: event 0x%x not supported\n", c->event); + *msg_id = NLKEY_NULL_MSG; /* next message */ + break; + } + + /* prepare netlink message for kernel to user space direction */ + if(msg.length > NLKEY_MSG_LEN) + { + printk(KERN_ERR "ipsec_xfrm2nlkey: maximum message size reached (%d bytes)\n", msg.length); + goto exit; + } + + skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); + if (skb == NULL) + goto exit; + + nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); + memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); + + /* whole length of the message i.e. header + payload */ + nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); + + /* from kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = 0; + NETLINK_CB(skb).pid = 0; + NETLINK_CB(skb).dst_group = 1; +exit: + return skb; +} + +static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c) +{ + struct sk_buff *skb; + unsigned short msg_type; + int rc = 0; + + /* We may generate more than one message when adding new SA (sa_create + sa_set_state + sa_set_tunnel...) */ + msg_type = ipsec_sacode_to_nlkeycode((unsigned short)c->event); + + while(msg_type != NLKEY_NULL_MSG) + { + /* build nlkey message */ + skb = ipsec_xfrm2nlkey(net, x, c, &msg_type); + + if(skb != NULL) + if((rc = netlink_broadcast(nlkey_socket, skb, 0, 1, GFP_ATOMIC)) < 0) + return rc; + } + + return rc; +} + + +int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, const struct flowi *fl, u16 family, u16 dir) +{ + struct sk_buff *skb; + struct nlkey_msg msg; + struct nlmsghdr *nlh = NULL; + unsigned short *p; + gfp_t allocation = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + + //printk(KERN_INFO "ipsec_nlkey_flow \n"); + + /* next message to build */ + memset(&msg, 0, sizeof(struct nlkey_msg)); + msg.fcode = NLKEY_FLOW_ADD; + + // Number of SA for this flow + p = msg.payload; + *p++ = xfrm_nr; + msg.length += sizeof(unsigned short); + // SA handles list + memcpy(p, xfrm_handle, xfrm_nr*sizeof(unsigned short)); + msg.length += xfrm_nr*sizeof(unsigned short); + p+=xfrm_nr; + // flow family + *p++ = family; + msg.length += sizeof(unsigned short); + // flow family + *p++ = dir; + msg.length += sizeof(unsigned short); + // flow descriptor + memcpy(p, fl, sizeof(struct flowi)); + msg.length +=sizeof(struct flowi); + p+=sizeof(struct flowi) / sizeof(u16); + + skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); + if (skb == NULL) + return -ENOMEM; + + /* prepare netlink message for kernel to user space direction */ + nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); + memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); + + /* whole length of the message i.e. header + payload */ + nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); + + /* from kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = 0; + NETLINK_CB(skb).pid = 0; + NETLINK_CB(skb).dst_group = 1; + + return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation)); +} +EXPORT_SYMBOL(ipsec_nlkey_flow); + + +static void ipsec_nlkey_init(void) +{ + printk(KERN_INFO "Initializing NETLINK_KEY socket\n"); + + nlkey_socket = netlink_kernel_create(&init_net, NETLINK_KEY, 1, + ipsec_nlkey_rcv, NULL, THIS_MODULE); +} +#endif + + static int __net_init pfkey_net_init(struct net *net) { struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); @@ -3788,6 +4539,11 @@ static void __exit ipsec_pfkey_exit(void) sock_unregister(PF_KEY); unregister_pernet_subsys(&pfkey_net_ops); proto_unregister(&key_proto); + +#ifdef NLKEY_SUPPORT + /* release NETLINK_KEY socket */ + sock_release(nlkey_socket->sk_socket); +#endif } static int __init ipsec_pfkey_init(void) @@ -3806,6 +4562,12 @@ static int __init ipsec_pfkey_init(void) err = xfrm_register_km(&pfkeyv2_mgr); if (err != 0) goto out_sock_unregister; + +#ifdef NLKEY_SUPPORT + /* create NETLINK_KEY socket for IPSec offload on Comcerto */ + ipsec_nlkey_init(); +#endif + out: return err; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index d5597b7..b2e6f7e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -163,7 +163,6 @@ config NF_CONNTRACK_FTP config NF_CONNTRACK_H323 tristate "H.323 protocol support" - depends on (IPV6 || IPV6=n) depends on NETFILTER_ADVANCED help H.323 is a VoIP signalling protocol from ITU-T. As one of the most @@ -609,7 +608,6 @@ config NETFILTER_XT_TARGET_SECMARK config NETFILTER_XT_TARGET_TCPMSS tristate '"TCPMSS" target support' - depends on (IPV6 || IPV6=n) default m if NETFILTER_ADVANCED=n ---help--- This option adds a `TCPMSS' target, which allows you to alter the @@ -832,6 +830,27 @@ config NETFILTER_XT_MATCH_IPVS If unsure, say N. +config NETFILTER_XT_MATCH_LAYER7 + tristate '"layer7" match support' + depends on EXPERIMENTAL + depends on NETFILTER_XTABLES + depends on NETFILTER_ADVANCED + depends on NF_CONNTRACK + help + Say Y if you want to be able to classify connections (and their + packets) based on regular expression matching of their application + layer data. This is one way to classify applications such as + peer-to-peer filesharing systems that do not always use the same + port. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_LAYER7_DEBUG + bool 'Layer 7 debugging output' + depends on NETFILTER_XT_MATCH_LAYER7 + help + Say Y to get lots of debugging output. + config NETFILTER_XT_MATCH_LENGTH tristate '"length" match support' depends on NETFILTER_ADVANCED @@ -1018,6 +1037,12 @@ config NETFILTER_XT_MATCH_STATE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_LAYER7_DEBUG + bool 'Layer 7 debugging output' + depends on NETFILTER_XT_MATCH_LAYER7 + help + Say Y to get lots of debugging output. + config NETFILTER_XT_MATCH_STATISTIC tristate '"statistic" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 1a02853..e1e1ced 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -6,6 +6,8 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o obj-$(CONFIG_NETFILTER) = netfilter.o +obj-$(CONFIG_COMCERTO_FP) += comcerto_fp_netfilter.o + obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o @@ -102,6 +104,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o obj-$(CONFIG_NETFILTER_XT_MATCH_SOCKET) += xt_socket.o obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o +obj-$(CONFIG_NETFILTER_XT_MATCH_LAYER7) += xt_layer7.o obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 7489bd3..84eb74b 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -213,6 +213,14 @@ destroy_conntrack(struct nf_conntrack *nfct) * too. */ nf_ct_remove_expectations(ct); + #if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) + if(ct->layer7.app_proto) + kfree(ct->layer7.app_proto); + if(ct->layer7.app_data) + kfree(ct->layer7.app_data); + #endif + + /* We overload first tuple to link into unconfirmed list. */ if (!nf_ct_is_confirmed(ct)) { BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); @@ -290,11 +298,36 @@ static void death_by_timeout(unsigned long ul_conntrack) { struct nf_conn *ct = (void *)ul_conntrack; struct nf_conn_tstamp *tstamp; +#ifdef CONFIG_COMCERTO_FP + struct nf_conntrack_l4proto *l4proto; +#endif tstamp = nf_conn_tstamp_find(ct); if (tstamp && tstamp->stop == 0) tstamp->stop = ktime_to_ns(ktime_get_real()); +#ifdef CONFIG_COMCERTO_FP + l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); + + if (test_bit(IPS_DYING_BIT, &ct->status) || + (!test_bit(IPS_PERMANENT_BIT, &ct->status)) || + ((l4proto->l4proto == IPPROTO_TCP) && (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED))) { + if (!test_bit(IPS_DYING_BIT, &ct->status) && + unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) { + /* destroy event was not delivered */ + nf_ct_delete_from_lists(ct); + nf_ct_insert_dying_list(ct); + return; + } + set_bit(IPS_DYING_BIT, &ct->status); + nf_ct_delete_from_lists(ct); + nf_ct_put(ct); + } else { + ct->timeout.expires = jiffies + COMCERTO_PERMANENT_TIMEOUT * HZ; + add_timer(&ct->timeout); + } +#else + if (!test_bit(IPS_DYING_BIT, &ct->status) && unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) { /* destroy event was not delivered */ @@ -305,6 +338,7 @@ static void death_by_timeout(unsigned long ul_conntrack) set_bit(IPS_DYING_BIT, &ct->status); nf_ct_delete_from_lists(ct); nf_ct_put(ct); +#endif } /* @@ -608,7 +642,13 @@ static noinline int early_drop(struct net *net, unsigned int hash) if (!ct) return dropped; +#ifdef CONFIG_COMCERTO_FP + clear_bit(IPS_PERMANENT_BIT, &ct->status); + /* Avoid race with timer expiration */ + if (del_timer_sync(&ct->timeout)) { +#else if (del_timer(&ct->timeout)) { +#endif death_by_timeout((unsigned long)ct); dropped = 1; NF_CT_STAT_INC_ATOMIC(net, early_drop); @@ -1077,7 +1117,13 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, } } +#ifdef CONFIG_COMCERTO_FP + clear_bit(IPS_PERMANENT_BIT, &ct->status); + /* Avoid race with timer expiration */ + if (del_timer_sync(&ct->timeout)) { +#else if (del_timer(&ct->timeout)) { +#endif ct->timeout.function((unsigned long)ct); return true; } @@ -1198,7 +1244,14 @@ void nf_ct_iterate_cleanup(struct net *net, while ((ct = get_next_corpse(net, iter, data, &bucket)) != NULL) { /* Time to push up daises... */ + +#ifdef CONFIG_COMCERTO_FP + clear_bit(IPS_PERMANENT_BIT, &ct->status); + /* Avoid race with timer expiration */ + if (del_timer_sync(&ct->timeout)) +#else if (del_timer(&ct->timeout)) +#endif death_by_timeout((unsigned long)ct); /* ... else the timer will get him soon. */ @@ -1360,6 +1413,42 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) } EXPORT_SYMBOL_GPL(nf_ct_alloc_hashtable); +int nf_conntrack_set_dpi_allow_report(struct sk_buff *skb) +{ + int err = 0; + struct nf_conn *ct = (struct nf_conn *)skb->nfct; + + nf_conntrack_get(skb->nfct); + + set_bit(IPS_DPI_ALLOWED_BIT, &ct->status); + + nf_conntrack_event_cache(IPCT_PROTOINFO, ct); + + nf_conntrack_put(skb->nfct); + + return err; +} +EXPORT_SYMBOL(nf_conntrack_set_dpi_allow_report); + +int nf_conntrack_set_dpi_allow_and_mark(struct sk_buff *skb, int mark) +{ + int err = 0; + struct nf_conn *ct = (struct nf_conn *)skb->nfct; + + nf_conntrack_get(skb->nfct); + + set_bit(IPS_DPI_ALLOWED_BIT, &ct->status); + + ct->mark = mark; + + nf_conntrack_event_cache(IPCT_PROTOINFO, ct); + + nf_conntrack_put(skb->nfct); + + return err; +} +EXPORT_SYMBOL(nf_conntrack_set_dpi_allow_and_mark); + int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) { int i, bucket; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 782cdcd..2c48c8f 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -301,6 +301,41 @@ nla_put_failure: #define ctnetlink_dump_secctx(a, b) (0) #endif +#if defined(CONFIG_COMCERTO_FP) +static int +ctnetlink_dump_comcerto_fp(struct sk_buff *skb, const struct nf_conn *ct) +{ + struct nlattr *nest_count; + + nest_count = nla_nest_start(skb, CTA_COMCERTO_FP_ORIG | NLA_F_NESTED); + if (!nest_count) + goto nla_put_failure; + + NLA_PUT_U32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_ORIGINAL].mark); + NLA_PUT_U32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_ORIGINAL].ifindex); + NLA_PUT_U32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_ORIGINAL].iif); + + nla_nest_end(skb, nest_count); + + nest_count = nla_nest_start(skb, CTA_COMCERTO_FP_REPLY | NLA_F_NESTED); + if (!nest_count) + goto nla_put_failure; + + NLA_PUT_U32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_REPLY].mark); + NLA_PUT_U32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_REPLY].ifindex); + NLA_PUT_U32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_REPLY].iif); + + nla_nest_end(skb, nest_count); + + return 0; + +nla_put_failure: + return -1; +} +#else +#define ctnetlink_dump_comcerto_fp(a, b) (0) +#endif + #define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple) static inline int @@ -437,6 +472,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 || ctnetlink_dump_secctx(skb, ct) < 0 || + ctnetlink_dump_comcerto_fp(skb, ct) < 0 || ctnetlink_dump_id(skb, ct) < 0 || ctnetlink_dump_use(skb, ct) < 0 || ctnetlink_dump_master(skb, ct) < 0 || @@ -528,6 +564,12 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct) + nla_total_size(0) /* CTA_HELP */ + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */ + ctnetlink_secctx_size(ct) +#ifdef CONFIG_COMCERTO_FP + + 2 * nla_total_size(0) /* CTA_COMCERTO_FP_ORIG|REPL */ + + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_MARK */ + + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IFINDEX */ + + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IIF */ +#endif #ifdef CONFIG_NF_NAT_NEEDED + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */ + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */ @@ -605,6 +647,9 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) if (nf_ct_zone(ct)) NLA_PUT_BE16(skb, CTA_ZONE, htons(nf_ct_zone(ct))); + if (ctnetlink_dump_comcerto_fp(skb, ct) < 0) + goto nla_put_failure; + if (ctnetlink_dump_id(skb, ct) < 0) goto nla_put_failure; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 57ad466..37c4324 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -29,6 +29,9 @@ #include #include +/* Do not check the TCP window for incoming packets */ +static int nf_ct_tcp_no_window_check __read_mostly = 1; + /* "Be conservative in what you do, be liberal in what you accept from others." If it's non-zero, we mark only out of window RST segments as INVALID. */ @@ -521,6 +524,9 @@ static bool tcp_in_window(const struct nf_conn *ct, s16 receiver_offset; bool res; + if (nf_ct_tcp_no_window_check) + return true; + /* * Get the required data from the packet. */ @@ -1312,6 +1318,13 @@ static struct ctl_table tcp_sysctl_table[] = { .proc_handler = proc_dointvec, }, { + .procname = "nf_conntrack_tcp_no_window_check", + .data = &nf_ct_tcp_no_window_check, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { .procname = "nf_conntrack_tcp_be_liberal", .data = &nf_ct_tcp_be_liberal, .maxlen = sizeof(unsigned int), diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 4a8c55b..4f108f7 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1363,8 +1363,25 @@ static int process_sip_request(struct sk_buff *skb, unsigned int dataoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + struct nf_conn_help *help = nfct_help(ct); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); unsigned int matchoff, matchlen; unsigned int cseq, i; + union nf_inet_addr addr; + __be16 port; + + /* Many Cisco IP phones use a high source port for SIP requests, but + * listen for the response on port 5060. If we are the local + * router for one of these phones, save the port number from the + * Via: header so that nf_nat_sip can redirect the responses to + * the correct port. + */ + if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, + SIP_HDR_VIA_UDP, NULL, &matchoff, + &matchlen, &addr, &port) > 0 && + port != ct->tuplehash[dir].tuple.src.u.udp.port && + nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3)) + help->help.ct_sip_info.forced_dport = port; for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { const struct sip_handler *handler; diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 05e9feb..621e2da 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -239,6 +239,12 @@ static int ct_seq_show(struct seq_file *s, void *v) if (ct_show_delta_time(s, ct)) goto release; +#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) + if(ct->layer7.app_proto && + seq_printf(s, "l7proto=%s ", ct->layer7.app_proto)) + return -ENOSPC; +#endif + if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) goto release; diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 64670fc..ae82716 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -1,11 +1,14 @@ -/* IP tables module for matching the value of the IPv4/IPv6 DSCP field +/* x_tables module for setting the IPv4/IPv6 DSCP field, Version 1.8 * * (C) 2002 by Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * * 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. - */ + * + * See RFC2474 for a description of the DSCP field within the IP Header. +*/ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include @@ -14,102 +17,148 @@ #include #include -#include +#include MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Xtables: DSCP/TOS field match"); +MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_dscp"); -MODULE_ALIAS("ip6t_dscp"); -MODULE_ALIAS("ipt_tos"); -MODULE_ALIAS("ip6t_tos"); +MODULE_ALIAS("ipt_DSCP"); +MODULE_ALIAS("ip6t_DSCP"); +MODULE_ALIAS("ipt_TOS"); +MODULE_ALIAS("ip6t_TOS"); -static bool -dscp_mt(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +dscp_tg(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_dscp_info *info = par->matchinfo; + const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; - return (dscp == info->dscp) ^ !!info->invert; + if (dscp != dinfo->dscp) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK), + dinfo->dscp << XT_DSCP_SHIFT); + + } + return XT_CONTINUE; } -static bool -dscp_mt6(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_dscp_info *info = par->matchinfo; + const struct xt_DSCP_info *dinfo = par->targinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; - return (dscp == info->dscp) ^ !!info->invert; + if (dscp != dinfo->dscp) { + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK), + dinfo->dscp << XT_DSCP_SHIFT); + } + return XT_CONTINUE; } -static int dscp_mt_check(const struct xt_mtchk_param *par) +static int dscp_tg_check(const struct xt_tgchk_param *par) { - const struct xt_dscp_info *info = par->matchinfo; + const struct xt_DSCP_info *info = par->targinfo; if (info->dscp > XT_DSCP_MAX) { pr_info("dscp %x out of range\n", info->dscp); return -EDOM; } - return 0; } -static bool tos_mt(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +tos_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_tos_target_info *info = par->targinfo; + struct iphdr *iph = ip_hdr(skb); + u_int8_t orig, nv; + + orig = ipv4_get_dsfield(iph); + nv = (orig & ~info->tos_mask) ^ info->tos_value; + + if (orig != nv) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + iph = ip_hdr(skb); + ipv4_change_dsfield(iph, 0, nv); + } + + return XT_CONTINUE; +} + +static unsigned int +tos_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_tos_match_info *info = par->matchinfo; - - if (par->family == NFPROTO_IPV4) - return ((ip_hdr(skb)->tos & info->tos_mask) == - info->tos_value) ^ !!info->invert; - else - return ((ipv6_get_dsfield(ipv6_hdr(skb)) & info->tos_mask) == - info->tos_value) ^ !!info->invert; + const struct xt_tos_target_info *info = par->targinfo; + struct ipv6hdr *iph = ipv6_hdr(skb); + u_int8_t orig, nv; + + orig = ipv6_get_dsfield(iph); + nv = (orig & ~info->tos_mask) ^ info->tos_value; + + if (orig != nv) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + iph = ipv6_hdr(skb); + ipv6_change_dsfield(iph, 0, nv); + } + + return XT_CONTINUE; } -static struct xt_match dscp_mt_reg[] __read_mostly = { +static struct xt_target dscp_tg_reg[] __read_mostly = { { - .name = "dscp", + .name = "DSCP", .family = NFPROTO_IPV4, - .checkentry = dscp_mt_check, - .match = dscp_mt, - .matchsize = sizeof(struct xt_dscp_info), + .checkentry = dscp_tg_check, + .target = dscp_tg, + .targetsize = sizeof(struct xt_DSCP_info), + .table = "mangle", .me = THIS_MODULE, }, { - .name = "dscp", + .name = "DSCP", .family = NFPROTO_IPV6, - .checkentry = dscp_mt_check, - .match = dscp_mt6, - .matchsize = sizeof(struct xt_dscp_info), + .checkentry = dscp_tg_check, + .target = dscp_tg6, + .targetsize = sizeof(struct xt_DSCP_info), + .table = "mangle", .me = THIS_MODULE, }, { - .name = "tos", + .name = "TOS", .revision = 1, .family = NFPROTO_IPV4, - .match = tos_mt, - .matchsize = sizeof(struct xt_tos_match_info), + .table = "mangle", + .target = tos_tg, + .targetsize = sizeof(struct xt_tos_target_info), .me = THIS_MODULE, }, { - .name = "tos", + .name = "TOS", .revision = 1, .family = NFPROTO_IPV6, - .match = tos_mt, - .matchsize = sizeof(struct xt_tos_match_info), + .table = "mangle", + .target = tos_tg6, + .targetsize = sizeof(struct xt_tos_target_info), .me = THIS_MODULE, }, }; -static int __init dscp_mt_init(void) +static int __init dscp_tg_init(void) { - return xt_register_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); + return xt_register_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); } -static void __exit dscp_mt_exit(void) +static void __exit dscp_tg_exit(void) { - xt_unregister_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); + xt_unregister_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); } -module_init(dscp_mt_init); -module_exit(dscp_mt_exit); +module_init(dscp_tg_init); +module_exit(dscp_tg_exit); diff --git a/net/netfilter/xt_hl.c b/net/netfilter/xt_hl.c index 0039511..1535e87 100644 --- a/net/netfilter/xt_hl.c +++ b/net/netfilter/xt_hl.c @@ -1,96 +1,169 @@ /* - * IP tables module for matching the value of the TTL - * (C) 2000,2001 by Harald Welte + * TTL modification target for IP tables + * (C) 2000,2005 by Harald Welte * - * Hop Limit matching module - * (C) 2001-2002 Maciej Soltysiak + * Hop Limit modification target for ip6tables + * Maciej Soltysiak * * 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. */ - -#include -#include +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include +#include +#include +#include #include -#include -#include +#include +#include +MODULE_AUTHOR("Harald Welte "); MODULE_AUTHOR("Maciej Soltysiak "); -MODULE_DESCRIPTION("Xtables: Hoplimit/TTL field match"); +MODULE_DESCRIPTION("Xtables: Hoplimit/TTL Limit field modification target"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_ttl"); -MODULE_ALIAS("ip6t_hl"); -static bool ttl_mt(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +ttl_tg(struct sk_buff *skb, const struct xt_action_param *par) { - const struct ipt_ttl_info *info = par->matchinfo; - const u8 ttl = ip_hdr(skb)->ttl; + struct iphdr *iph; + const struct ipt_TTL_info *info = par->targinfo; + int new_ttl; + + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + iph = ip_hdr(skb); switch (info->mode) { - case IPT_TTL_EQ: - return ttl == info->ttl; - case IPT_TTL_NE: - return ttl != info->ttl; - case IPT_TTL_LT: - return ttl < info->ttl; - case IPT_TTL_GT: - return ttl > info->ttl; + case IPT_TTL_SET: + new_ttl = info->ttl; + break; + case IPT_TTL_INC: + new_ttl = iph->ttl + info->ttl; + if (new_ttl > 255) + new_ttl = 255; + break; + case IPT_TTL_DEC: + new_ttl = iph->ttl - info->ttl; + if (new_ttl < 0) + new_ttl = 0; + break; + default: + new_ttl = iph->ttl; + break; + } + + if (new_ttl != iph->ttl) { + csum_replace2(&iph->check, htons(iph->ttl << 8), + htons(new_ttl << 8)); + iph->ttl = new_ttl; } - return false; + return XT_CONTINUE; } -static bool hl_mt6(const struct sk_buff *skb, struct xt_action_param *par) +static unsigned int +hl_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - const struct ip6t_hl_info *info = par->matchinfo; - const struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct ipv6hdr *ip6h; + const struct ip6t_HL_info *info = par->targinfo; + int new_hl; + + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + ip6h = ipv6_hdr(skb); switch (info->mode) { - case IP6T_HL_EQ: - return ip6h->hop_limit == info->hop_limit; - case IP6T_HL_NE: - return ip6h->hop_limit != info->hop_limit; - case IP6T_HL_LT: - return ip6h->hop_limit < info->hop_limit; - case IP6T_HL_GT: - return ip6h->hop_limit > info->hop_limit; + case IP6T_HL_SET: + new_hl = info->hop_limit; + break; + case IP6T_HL_INC: + new_hl = ip6h->hop_limit + info->hop_limit; + if (new_hl > 255) + new_hl = 255; + break; + case IP6T_HL_DEC: + new_hl = ip6h->hop_limit - info->hop_limit; + if (new_hl < 0) + new_hl = 0; + break; + default: + new_hl = ip6h->hop_limit; + break; } - return false; + ip6h->hop_limit = new_hl; + + return XT_CONTINUE; +} + +static int ttl_tg_check(const struct xt_tgchk_param *par) +{ + const struct ipt_TTL_info *info = par->targinfo; + + if (info->mode > IPT_TTL_MAXMODE) { + pr_info("TTL: invalid or unknown mode %u\n", info->mode); + return -EINVAL; + } + if (info->mode != IPT_TTL_SET && info->ttl == 0) + return -EINVAL; + return 0; +} + +static int hl_tg6_check(const struct xt_tgchk_param *par) +{ + const struct ip6t_HL_info *info = par->targinfo; + + if (info->mode > IP6T_HL_MAXMODE) { + pr_info("invalid or unknown mode %u\n", info->mode); + return -EINVAL; + } + if (info->mode != IP6T_HL_SET && info->hop_limit == 0) { + pr_info("increment/decrement does not " + "make sense with value 0\n"); + return -EINVAL; + } + return 0; } -static struct xt_match hl_mt_reg[] __read_mostly = { +static struct xt_target hl_tg_reg[] __read_mostly = { { - .name = "ttl", + .name = "TTL", .revision = 0, .family = NFPROTO_IPV4, - .match = ttl_mt, - .matchsize = sizeof(struct ipt_ttl_info), + .target = ttl_tg, + .targetsize = sizeof(struct ipt_TTL_info), + .table = "mangle", + .checkentry = ttl_tg_check, .me = THIS_MODULE, }, { - .name = "hl", + .name = "HL", .revision = 0, .family = NFPROTO_IPV6, - .match = hl_mt6, - .matchsize = sizeof(struct ip6t_hl_info), + .target = hl_tg6, + .targetsize = sizeof(struct ip6t_HL_info), + .table = "mangle", + .checkentry = hl_tg6_check, .me = THIS_MODULE, }, }; -static int __init hl_mt_init(void) +static int __init hl_tg_init(void) { - return xt_register_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg)); + return xt_register_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); } -static void __exit hl_mt_exit(void) +static void __exit hl_tg_exit(void) { - xt_unregister_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg)); + xt_unregister_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); } -module_init(hl_mt_init); -module_exit(hl_mt_exit); +module_init(hl_tg_init); +module_exit(hl_tg_exit); +MODULE_ALIAS("ipt_TTL"); +MODULE_ALIAS("ip6t_HL"); diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index ed0db15..f264032 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -8,150 +8,188 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include -#include +#include #include +static DEFINE_MUTEX(xt_rateest_mutex); -static bool -xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) +#define RATEEST_HSIZE 16 +static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly; +static unsigned int jhash_rnd __read_mostly; +static bool rnd_inited __read_mostly; + +static unsigned int xt_rateest_hash(const char *name) { - const struct xt_rateest_match_info *info = par->matchinfo; - struct gnet_stats_rate_est *r; - u_int32_t bps1, bps2, pps1, pps2; - bool ret = true; - - spin_lock_bh(&info->est1->lock); - r = &info->est1->rstats; - if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps1 = info->bps1 >= r->bps ? info->bps1 - r->bps : 0; - pps1 = info->pps1 >= r->pps ? info->pps1 - r->pps : 0; - } else { - bps1 = r->bps; - pps1 = r->pps; - } - spin_unlock_bh(&info->est1->lock); - - if (info->flags & XT_RATEEST_MATCH_ABS) { - bps2 = info->bps2; - pps2 = info->pps2; - } else { - spin_lock_bh(&info->est2->lock); - r = &info->est2->rstats; - if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps2 = info->bps2 >= r->bps ? info->bps2 - r->bps : 0; - pps2 = info->pps2 >= r->pps ? info->pps2 - r->pps : 0; - } else { - bps2 = r->bps; - pps2 = r->pps; + return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) & + (RATEEST_HSIZE - 1); +} + +static void xt_rateest_hash_insert(struct xt_rateest *est) +{ + unsigned int h; + + h = xt_rateest_hash(est->name); + hlist_add_head(&est->list, &rateest_hash[h]); +} + +struct xt_rateest *xt_rateest_lookup(const char *name) +{ + struct xt_rateest *est; + struct hlist_node *n; + unsigned int h; + + h = xt_rateest_hash(name); + mutex_lock(&xt_rateest_mutex); + hlist_for_each_entry(est, n, &rateest_hash[h], list) { + if (strcmp(est->name, name) == 0) { + est->refcnt++; + mutex_unlock(&xt_rateest_mutex); + return est; } - spin_unlock_bh(&info->est2->lock); } + mutex_unlock(&xt_rateest_mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(xt_rateest_lookup); - switch (info->mode) { - case XT_RATEEST_MATCH_LT: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 < bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 < pps2; - break; - case XT_RATEEST_MATCH_GT: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 > bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 > pps2; - break; - case XT_RATEEST_MATCH_EQ: - if (info->flags & XT_RATEEST_MATCH_BPS) - ret &= bps1 == bps2; - if (info->flags & XT_RATEEST_MATCH_PPS) - ret &= pps1 == pps2; - break; +void xt_rateest_put(struct xt_rateest *est) +{ + mutex_lock(&xt_rateest_mutex); + if (--est->refcnt == 0) { + hlist_del(&est->list); + gen_kill_estimator(&est->bstats, &est->rstats); + /* + * gen_estimator est_timer() might access est->lock or bstats, + * wait a RCU grace period before freeing 'est' + */ + kfree_rcu(est, rcu); } - - ret ^= info->flags & XT_RATEEST_MATCH_INVERT ? true : false; - return ret; + mutex_unlock(&xt_rateest_mutex); } +EXPORT_SYMBOL_GPL(xt_rateest_put); -static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) +static unsigned int +xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par) { - struct xt_rateest_match_info *info = par->matchinfo; - struct xt_rateest *est1, *est2; - int ret = -EINVAL; + const struct xt_rateest_target_info *info = par->targinfo; + struct gnet_stats_basic_packed *stats = &info->est->bstats; - if (hweight32(info->flags & (XT_RATEEST_MATCH_ABS | - XT_RATEEST_MATCH_REL)) != 1) - goto err1; + spin_lock_bh(&info->est->lock); + stats->bytes += skb->len; + stats->packets++; + spin_unlock_bh(&info->est->lock); - if (!(info->flags & (XT_RATEEST_MATCH_BPS | XT_RATEEST_MATCH_PPS))) - goto err1; + return XT_CONTINUE; +} - switch (info->mode) { - case XT_RATEEST_MATCH_EQ: - case XT_RATEEST_MATCH_LT: - case XT_RATEEST_MATCH_GT: - break; - default: - goto err1; +static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) +{ + struct xt_rateest_target_info *info = par->targinfo; + struct xt_rateest *est; + struct { + struct nlattr opt; + struct gnet_estimator est; + } cfg; + int ret; + + if (unlikely(!rnd_inited)) { + get_random_bytes(&jhash_rnd, sizeof(jhash_rnd)); + rnd_inited = true; + } + + est = xt_rateest_lookup(info->name); + if (est) { + /* + * If estimator parameters are specified, they must match the + * existing estimator. + */ + if ((!info->interval && !info->ewma_log) || + (info->interval != est->params.interval || + info->ewma_log != est->params.ewma_log)) { + xt_rateest_put(est); + return -EINVAL; + } + info->est = est; + return 0; } - ret = -ENOENT; - est1 = xt_rateest_lookup(info->name1); - if (!est1) + ret = -ENOMEM; + est = kzalloc(sizeof(*est), GFP_KERNEL); + if (!est) goto err1; - est2 = NULL; - if (info->flags & XT_RATEEST_MATCH_REL) { - est2 = xt_rateest_lookup(info->name2); - if (!est2) - goto err2; - } + strlcpy(est->name, info->name, sizeof(est->name)); + spin_lock_init(&est->lock); + est->refcnt = 1; + est->params.interval = info->interval; + est->params.ewma_log = info->ewma_log; + + cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est)); + cfg.opt.nla_type = TCA_STATS_RATE_EST; + cfg.est.interval = info->interval; + cfg.est.ewma_log = info->ewma_log; + + ret = gen_new_estimator(&est->bstats, &est->rstats, + &est->lock, &cfg.opt); + if (ret < 0) + goto err2; - info->est1 = est1; - info->est2 = est2; + info->est = est; + xt_rateest_hash_insert(est); return 0; err2: - xt_rateest_put(est1); + kfree(est); err1: return ret; } -static void xt_rateest_mt_destroy(const struct xt_mtdtor_param *par) +static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par) { - struct xt_rateest_match_info *info = par->matchinfo; + struct xt_rateest_target_info *info = par->targinfo; - xt_rateest_put(info->est1); - if (info->est2) - xt_rateest_put(info->est2); + xt_rateest_put(info->est); } -static struct xt_match xt_rateest_mt_reg __read_mostly = { - .name = "rateest", +static struct xt_target xt_rateest_tg_reg __read_mostly = { + .name = "RATEEST", .revision = 0, .family = NFPROTO_UNSPEC, - .match = xt_rateest_mt, - .checkentry = xt_rateest_mt_checkentry, - .destroy = xt_rateest_mt_destroy, - .matchsize = sizeof(struct xt_rateest_match_info), + .target = xt_rateest_tg, + .checkentry = xt_rateest_tg_checkentry, + .destroy = xt_rateest_tg_destroy, + .targetsize = sizeof(struct xt_rateest_target_info), .me = THIS_MODULE, }; -static int __init xt_rateest_mt_init(void) +static int __init xt_rateest_tg_init(void) { - return xt_register_match(&xt_rateest_mt_reg); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rateest_hash); i++) + INIT_HLIST_HEAD(&rateest_hash[i]); + + return xt_register_target(&xt_rateest_tg_reg); } -static void __exit xt_rateest_mt_fini(void) +static void __exit xt_rateest_tg_fini(void) { - xt_unregister_match(&xt_rateest_mt_reg); + xt_unregister_target(&xt_rateest_tg_reg); } + MODULE_AUTHOR("Patrick McHardy "); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("xtables rate estimator match"); -MODULE_ALIAS("ipt_rateest"); -MODULE_ALIAS("ip6t_rateest"); -module_init(xt_rateest_mt_init); -module_exit(xt_rateest_mt_fini); +MODULE_DESCRIPTION("Xtables: packet rate estimator"); +MODULE_ALIAS("ipt_RATEEST"); +MODULE_ALIAS("ip6t_RATEEST"); +module_init(xt_rateest_tg_init); +module_exit(xt_rateest_tg_fini); diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c index c53d4d1..9e63b43 100644 --- a/net/netfilter/xt_tcpmss.c +++ b/net/netfilter/xt_tcpmss.c @@ -1,110 +1,319 @@ -/* Kernel module to match TCP MSS values. */ - -/* Copyright (C) 2000 Marc Boucher - * Portions (C) 2005 by Harald Welte +/* + * This is a module which is used for setting the MSS option in TCP packets. + * + * Copyright (C) 2000 Marc Boucher * * 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 #include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include - #include #include +#include +#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); -MODULE_DESCRIPTION("Xtables: TCP MSS match"); -MODULE_ALIAS("ipt_tcpmss"); -MODULE_ALIAS("ip6t_tcpmss"); +MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment"); +MODULE_ALIAS("ipt_TCPMSS"); +MODULE_ALIAS("ip6t_TCPMSS"); -static bool -tcpmss_mt(const struct sk_buff *skb, struct xt_action_param *par) +static inline unsigned int +optlen(const u_int8_t *opt, unsigned int offset) { - const struct xt_tcpmss_match_info *info = par->matchinfo; - const struct tcphdr *th; - struct tcphdr _tcph; - /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ - const u_int8_t *op; - u8 _opt[15 * 4 - sizeof(_tcph)]; - unsigned int i, optlen; - - /* If we don't have the whole header, drop packet. */ - th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); - if (th == NULL) - goto dropit; - - /* Malformed. */ - if (th->doff*4 < sizeof(*th)) - goto dropit; - - optlen = th->doff*4 - sizeof(*th); - if (!optlen) - goto out; - - /* Truncated options. */ - op = skb_header_pointer(skb, par->thoff + sizeof(*th), optlen, _opt); - if (op == NULL) - goto dropit; - - for (i = 0; i < optlen; ) { - if (op[i] == TCPOPT_MSS - && (optlen - i) >= TCPOLEN_MSS - && op[i+1] == TCPOLEN_MSS) { - u_int16_t mssval; - - mssval = (op[i+2] << 8) | op[i+3]; - - return (mssval >= info->mss_min && - mssval <= info->mss_max) ^ info->invert; + /* Beware zero-length options: make finite progress */ + if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) + return 1; + else + return opt[offset+1]; +} + +static int +tcpmss_mangle_packet(struct sk_buff *skb, + const struct xt_tcpmss_info *info, + unsigned int in_mtu, + unsigned int tcphoff, + unsigned int minlen) +{ + struct tcphdr *tcph; + unsigned int tcplen, i; + __be16 oldval; + u16 newmss; + u8 *opt; + + if (!skb_make_writable(skb, skb->len)) + return -1; + + tcplen = skb->len - tcphoff; + tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); + + /* Header cannot be larger than the packet */ + if (tcplen < tcph->doff*4) + return -1; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU) { + if (dst_mtu(skb_dst(skb)) <= minlen) { + if (net_ratelimit()) + pr_err("unknown or invalid path-MTU (%u)\n", + dst_mtu(skb_dst(skb))); + return -1; + } + if (in_mtu <= minlen) { + if (net_ratelimit()) + pr_err("unknown or invalid path-MTU (%u)\n", + in_mtu); + return -1; + } + newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen; + } else + newmss = info->mss; + + opt = (u_int8_t *)tcph; + for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { + if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && + opt[i+1] == TCPOLEN_MSS) { + u_int16_t oldmss; + + oldmss = (opt[i+2] << 8) | opt[i+3]; + + /* Never increase MSS, even when setting it, as + * doing so results in problems for hosts that rely + * on MSS being set correctly. + */ + if (oldmss <= newmss) + return 0; + + opt[i+2] = (newmss & 0xff00) >> 8; + opt[i+3] = newmss & 0x00ff; + + inet_proto_csum_replace2(&tcph->check, skb, + htons(oldmss), htons(newmss), + 0); + return 0; } - if (op[i] < 2) - i++; - else - i += op[i+1] ? : 1; } -out: - return info->invert; -dropit: - par->hotdrop = true; + /* There is data after the header so the option can't be added + without moving it, and doing so may make the SYN packet + itself too large. Accept the packet unmodified instead. */ + if (tcplen > tcph->doff*4) + return 0; + + /* + * MSS Option not found ?! add it.. + */ + if (skb_tailroom(skb) < TCPOLEN_MSS) { + if (pskb_expand_head(skb, 0, + TCPOLEN_MSS - skb_tailroom(skb), + GFP_ATOMIC)) + return -1; + tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); + } + + skb_put(skb, TCPOLEN_MSS); + + opt = (u_int8_t *)tcph + sizeof(struct tcphdr); + memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); + + inet_proto_csum_replace2(&tcph->check, skb, + htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); + opt[0] = TCPOPT_MSS; + opt[1] = TCPOLEN_MSS; + opt[2] = (newmss & 0xff00) >> 8; + opt[3] = newmss & 0x00ff; + + inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0); + + oldval = ((__be16 *)tcph)[6]; + tcph->doff += TCPOLEN_MSS/4; + inet_proto_csum_replace2(&tcph->check, skb, + oldval, ((__be16 *)tcph)[6], 0); + return TCPOLEN_MSS; +} + +static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, + unsigned int family) +{ + struct flowi fl; + const struct nf_afinfo *ai; + struct rtable *rt = NULL; + u_int32_t mtu = ~0U; + + if (family == PF_INET) { + struct flowi4 *fl4 = &fl.u.ip4; + memset(fl4, 0, sizeof(*fl4)); + fl4->daddr = ip_hdr(skb)->saddr; + } else { + struct flowi6 *fl6 = &fl.u.ip6; + + memset(fl6, 0, sizeof(*fl6)); + ipv6_addr_copy(&fl6->daddr, &ipv6_hdr(skb)->saddr); + } + rcu_read_lock(); + ai = nf_get_afinfo(family); + if (ai != NULL) + ai->route(&init_net, (struct dst_entry **)&rt, &fl, false); + rcu_read_unlock(); + + if (rt != NULL) { + mtu = dst_mtu(&rt->dst); + dst_release(&rt->dst); + } + return mtu; +} + +static unsigned int +tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct iphdr *iph = ip_hdr(skb); + __be16 newlen; + int ret; + + ret = tcpmss_mangle_packet(skb, par->targinfo, + tcpmss_reverse_mtu(skb, PF_INET), + iph->ihl * 4, + sizeof(*iph) + sizeof(struct tcphdr)); + if (ret < 0) + return NF_DROP; + if (ret > 0) { + iph = ip_hdr(skb); + newlen = htons(ntohs(iph->tot_len) + ret); + csum_replace2(&iph->check, iph->tot_len, newlen); + iph->tot_len = newlen; + } + return XT_CONTINUE; +} + +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +static unsigned int +tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + u8 nexthdr; + int tcphoff; + int ret; + + nexthdr = ipv6h->nexthdr; + tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); + if (tcphoff < 0) + return NF_DROP; + ret = tcpmss_mangle_packet(skb, par->targinfo, + tcpmss_reverse_mtu(skb, PF_INET6), + tcphoff, + sizeof(*ipv6h) + sizeof(struct tcphdr)); + if (ret < 0) + return NF_DROP; + if (ret > 0) { + ipv6h = ipv6_hdr(skb); + ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); + } + return XT_CONTINUE; +} +#endif + +/* Must specify -p tcp --syn */ +static inline bool find_syn_match(const struct xt_entry_match *m) +{ + const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data; + + if (strcmp(m->u.kernel.match->name, "tcp") == 0 && + tcpinfo->flg_cmp & TCPHDR_SYN && + !(tcpinfo->invflags & XT_TCP_INV_FLAGS)) + return true; + return false; } -static struct xt_match tcpmss_mt_reg[] __read_mostly = { +static int tcpmss_tg4_check(const struct xt_tgchk_param *par) +{ + const struct xt_tcpmss_info *info = par->targinfo; + const struct ipt_entry *e = par->entryinfo; + const struct xt_entry_match *ematch; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU && + (par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) != 0) { + pr_info("path-MTU clamping only supported in " + "FORWARD, OUTPUT and POSTROUTING hooks\n"); + return -EINVAL; + } + xt_ematch_foreach(ematch, e) + if (find_syn_match(ematch)) + return 0; + pr_info("Only works on TCP SYN packets\n"); + return -EINVAL; +} + +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +static int tcpmss_tg6_check(const struct xt_tgchk_param *par) +{ + const struct xt_tcpmss_info *info = par->targinfo; + const struct ip6t_entry *e = par->entryinfo; + const struct xt_entry_match *ematch; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU && + (par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) != 0) { + pr_info("path-MTU clamping only supported in " + "FORWARD, OUTPUT and POSTROUTING hooks\n"); + return -EINVAL; + } + xt_ematch_foreach(ematch, e) + if (find_syn_match(ematch)) + return 0; + pr_info("Only works on TCP SYN packets\n"); + return -EINVAL; +} +#endif + +static struct xt_target tcpmss_tg_reg[] __read_mostly = { { - .name = "tcpmss", .family = NFPROTO_IPV4, - .match = tcpmss_mt, - .matchsize = sizeof(struct xt_tcpmss_match_info), + .name = "TCPMSS", + .checkentry = tcpmss_tg4_check, + .target = tcpmss_tg4, + .targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) { - .name = "tcpmss", .family = NFPROTO_IPV6, - .match = tcpmss_mt, - .matchsize = sizeof(struct xt_tcpmss_match_info), + .name = "TCPMSS", + .checkentry = tcpmss_tg6_check, + .target = tcpmss_tg6, + .targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, +#endif }; -static int __init tcpmss_mt_init(void) +static int __init tcpmss_tg_init(void) { - return xt_register_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); + return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); } -static void __exit tcpmss_mt_exit(void) +static void __exit tcpmss_tg_exit(void) { - xt_unregister_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); + xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); } -module_init(tcpmss_mt_init); -module_exit(tcpmss_mt_exit); +module_init(tcpmss_tg_init); +module_exit(tcpmss_tg_exit); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4f19bf2..79bc8c9 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -297,6 +297,7 @@ struct packet_sock { unsigned int tp_tstamp; struct net_device __rcu *cached_dev; struct packet_type prot_hook ____cacheline_aligned_in_smp; + unsigned int pkt_type; }; #define PACKET_FANOUT_MAX 256 @@ -1386,6 +1387,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, { struct sock *sk; struct sockaddr_pkt *spkt; + struct packet_sock *po; /* * When we registered the protocol we saved the socket in the data @@ -1393,6 +1395,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, */ sk = pt->af_packet_priv; + po = pkt_sk(sk); /* * Yank back the headers [hope the device set this @@ -1405,7 +1408,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, * so that this procedure is noop. */ - if (skb->pkt_type == PACKET_LOOPBACK) + if (!(po->pkt_type & (1 << skb->pkt_type))) goto out; if (!net_eq(dev_net(dev), sock_net(sk))) @@ -1598,12 +1601,12 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, int skb_len = skb->len; unsigned int snaplen, res; - if (skb->pkt_type == PACKET_LOOPBACK) - goto drop; - sk = pt->af_packet_priv; po = pkt_sk(sk); + if (!(po->pkt_type & (1 << skb->pkt_type))) + goto drop; + if (!net_eq(dev_net(dev), sock_net(sk))) goto drop; @@ -1722,12 +1725,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct timespec ts; struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); - if (skb->pkt_type == PACKET_LOOPBACK) - goto drop; - sk = pt->af_packet_priv; po = pkt_sk(sk); + if (!(po->pkt_type & (1 << skb->pkt_type))) + goto drop; + if (!net_eq(dev_net(dev), sock_net(sk))) goto drop; @@ -2600,6 +2603,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol, spin_lock_init(&po->bind_lock); mutex_init(&po->pg_vec_lock); po->prot_hook.func = packet_rcv; + po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK); if (sock->type == SOCK_PACKET) po->prot_hook.func = packet_rcv_spkt; @@ -3194,6 +3198,16 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv return fanout_add(sk, val & 0xffff, val >> 16); } + case PACKET_RECV_TYPE: + { + unsigned int val; + if (optlen != sizeof(val)) + return -EINVAL; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + po->pkt_type = val & ~PACKET_LOOPBACK; + return 0; + } default: return -ENOPROTOOPT; } @@ -3264,6 +3278,13 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, data = &val; break; + case PACKET_RECV_TYPE: + if (len > sizeof(unsigned int)) + len = sizeof(unsigned int); + val = po->pkt_type; + + data = &val; + break; case PACKET_VERSION: if (len > sizeof(int)) len = sizeof(int); diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 2590e91..61a944a 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -148,6 +148,37 @@ config NET_SCH_SFQ To compile this code as a module, choose M here: the module will be called sch_sfq. +config NET_SCH_ESFQ + tristate "Enhanced Stochastic Fairness Queueing (ESFQ)" + ---help--- + Say Y here if you want to use the Enhanced Stochastic Fairness + Queueing (ESFQ) packet scheduling algorithm for some of your network + devices or as a leaf discipline for a classful qdisc such as HTB or + CBQ (see the top of for details and + references to the SFQ algorithm). + + This is an enchanced SFQ version which allows you to control some + hardcoded values in the SFQ scheduler. + + ESFQ also adds control of the hash function used to identify packet + flows. The original SFQ discipline hashes by connection; ESFQ add + several other hashing methods, such as by src IP or by dst IP, which + can be more fair to users in some networking situations. + + To compile this code as a module, choose M here: the + module will be called sch_esfq. + +config NET_SCH_ESFQ_NFCT + bool "Connection Tracking Hash Types" + depends on NET_SCH_ESFQ && NF_CONNTRACK + ---help--- + Say Y here to enable support for hashing based on netfilter connection + tracking information. This is useful for a router that is also using + NAT to connect privately-addressed hosts to the Internet. If you want + to provide fair distribution of upstream bandwidth, ESFQ must use + connection tracking information, since all outgoing packets will share + the same source address. + config NET_SCH_TEQL tristate "True Link Equalizer (TEQL)" ---help--- @@ -571,6 +602,19 @@ config NET_ACT_CSUM To compile this code as a module, choose M here: the module will be called act_csum. +config NET_ACT_CONNMARK + tristate "Connection Tracking Marking" + depends on NET_CLS_ACT + depends on NF_CONNTRACK + depends on NF_CONNTRACK_MARK + ---help--- + Say Y here to restore the connmark from a scheduler action + + If unsure, say N. + + To compile this code as a module, choose M here: the + module will be called act_connmark. + config NET_CLS_IND bool "Incoming device classification" depends on NET_CLS_U32 || NET_CLS_FW diff --git a/net/sched/Makefile b/net/sched/Makefile index dc5889c..4261c96 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_NET_ACT_PEDIT) += act_pedit.o obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o +obj-$(CONFIG_NET_ACT_CONNMARK) += act_connmark.o obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o @@ -26,6 +27,7 @@ obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o obj-$(CONFIG_NET_SCH_DSMARK) += sch_dsmark.o obj-$(CONFIG_NET_SCH_SFB) += sch_sfb.o obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o +obj-$(CONFIG_NET_SCH_ESFQ) += sch_esfq.o obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 1f1ef70..e6dd593 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -1,5 +1,5 @@ config WIRELESS_EXT - bool + bool "Wireless extensions" config WEXT_CORE def_bool y @@ -11,10 +11,10 @@ config WEXT_PROC depends on WEXT_CORE config WEXT_SPY - bool + bool "WEXT_SPY" config WEXT_PRIV - bool + bool "WEXT_PRIV" config CFG80211 tristate "cfg80211 - wireless configuration API" @@ -142,13 +142,13 @@ config LIB80211 you want this built into your kernel. config LIB80211_CRYPT_WEP - tristate + tristate "LIB80211_CRYPT_WEP" config LIB80211_CRYPT_CCMP - tristate + tristate "LIB80211_CRYPT_CCMP" config LIB80211_CRYPT_TKIP - tristate + tristate "LIB80211_CRYPT_TKIP" config LIB80211_DEBUG bool "lib80211 debugging messages" diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 47bacd8..bd1c628 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -43,11 +43,32 @@ static int xfrm_output_one(struct sk_buff *skb, int err) struct dst_entry *dst = skb_dst(skb); struct xfrm_state *x = dst->xfrm; struct net *net = xs_net(x); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; + int xfrm_nr = 0; + int i; +#endif if (err <= 0) goto resume; do { +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (x->offloaded) { + + if (xfrm_nr == XFRM_MAX_DEPTH) { + err = -ENOBUFS; + goto out_exit; + } + + if (!x->curlft.use_time) + x->curlft.use_time = get_seconds(); + + xfrm_vec[xfrm_nr++] = x; + skb->ipsec_offload = 1; + goto next_dst; + } +#endif err = xfrm_state_check_space(x, skb); if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); @@ -90,6 +111,9 @@ resume: goto error_nolock; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +next_dst: +#endif dst = skb_dst_pop(skb); if (!dst) { XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); @@ -101,6 +125,26 @@ resume: } while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL)); err = 0; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { + struct sec_path *sp; + + sp = secpath_dup(skb->sp); + if (!sp) + goto error_nolock; + if (skb->sp) + secpath_put(skb->sp); + skb->sp = sp; + } + if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) + goto error_nolock; + + memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec, + xfrm_nr * sizeof(xfrm_vec[0])); + skb->sp->len += xfrm_nr; + for (i = 0; i < skb->sp->len; i++) + xfrm_state_hold(skb->sp->xvec[i]); +#endif out_exit: return err; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 113d20e..c1f8f63 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -46,7 +46,15 @@ static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO]; static struct kmem_cache *xfrm_dst_cache __read_mostly; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +extern int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, + const struct flowi *fl, u16 family, u16 dir); +#endif + +#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD) static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); +#endif + static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo); static void xfrm_init_pmtu(struct dst_entry *dst); static int stale_bundle(struct dst_entry *dst); @@ -94,7 +102,10 @@ int xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, return 0; } -static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, +#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD) +static inline +#endif +struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr, int family) @@ -112,8 +123,14 @@ static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, return dst; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +EXPORT_SYMBOL(__xfrm_dst_lookup); +#endif -static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, +#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD) +static inline +#endif +struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, xfrm_address_t *prev_saddr, xfrm_address_t *prev_daddr, int family) @@ -143,6 +160,9 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, return dst; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +EXPORT_SYMBOL(xfrm_dst_lookup); +#endif static inline unsigned long make_jiffies(long secs) { @@ -1264,7 +1284,10 @@ xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, const struct flowi *fl, * still valid. */ -static inline int xfrm_get_tos(const struct flowi *fl, int family) +#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD) +static inline +#endif +int xfrm_get_tos(const struct flowi *fl, int family) { struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); int tos; @@ -1278,6 +1301,9 @@ static inline int xfrm_get_tos(const struct flowi *fl, int family) return tos; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +EXPORT_SYMBOL(xfrm_get_tos); +#endif static struct flow_cache_object *xfrm_bundle_flo_get(struct flow_cache_object *flo) { @@ -1461,7 +1487,12 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, xdst->route = dst; dst_copy_metrics(dst1, dst); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if ((xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) && + (!xfrm[i]->offloaded)) { +#else if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { +#endif family = xfrm[i]->props.family; dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr, family); @@ -1786,6 +1817,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, u16 family = dst_orig->ops->family; u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); int i, err, num_pols, num_xfrms = 0, drop_pols = 0; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + u8 new_flow = 0; +#endif restart: dst = NULL; @@ -1836,8 +1870,13 @@ restart: !net->xfrm.policy_count[XFRM_POLICY_OUT]) goto nopol; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + flo = flow_cache_lookup(net, fl, family, dir, &new_flow, + xfrm_bundle_lookup, dst_orig); +#else flo = flow_cache_lookup(net, fl, family, dir, xfrm_bundle_lookup, dst_orig); +#endif if (flo == NULL) goto nopol; if (IS_ERR(flo)) { @@ -1917,6 +1956,30 @@ no_transform: dst_release(dst); dst = dst_orig; } + +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (new_flow) { + struct dst_entry *dst1 = dst; + struct xfrm_state *x; + u16 xfrm_handle[XFRM_POLICY_TYPE_MAX]; + + num_xfrms = 0; + memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX*sizeof(u16)); + while((x = dst1->xfrm) != NULL) { + if (!x->offloaded) + goto ok; + xfrm_handle[num_xfrms++] = x->handle; + dst1 = dst1->child; + if (dst1 == NULL) { + err = -EHOSTUNREACH; + goto error; + } + } + // sent flow notification to cmm with sa_handle + ipsec_nlkey_flow(num_xfrms, xfrm_handle, fl, family, (unsigned short)dir); + } +#endif + ok: xfrm_pols_put(pols, drop_pols); if (dst && dst->xfrm && @@ -2042,9 +2105,14 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, int xfrm_nr; int pi; int reverse; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + u8 new_flow = 0; +#endif struct flowi fl; u8 fl_dir; int xerr_idx = -1; + + //printk(KERN_INFO "%s\n", __func__); reverse = dir & ~XFRM_POLICY_MASK; dir &= XFRM_POLICY_MASK; @@ -2081,9 +2149,13 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, if (!pol) { struct flow_cache_object *flo; - +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + flo = flow_cache_lookup(net, &fl, family, fl_dir, &new_flow, + xfrm_policy_lookup, NULL); +#else flo = flow_cache_lookup(net, &fl, family, fl_dir, xfrm_policy_lookup, NULL); +#endif if (IS_ERR_OR_NULL(flo)) pol = ERR_CAST(flo); else @@ -2177,6 +2249,28 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, goto reject; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (new_flow) { + struct xfrm_state *x; + u16 xfrm_handle[XFRM_POLICY_TYPE_MAX]; + + xfrm_nr = 0; + memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX*sizeof(u16)); + for (i=skb->sp->len-1; i>=0; i--) + { + x = skb->sp->xvec[i]; + + if (!x->offloaded) + goto std_path; + + xfrm_handle[xfrm_nr++] = x->handle; + } + // sent flow notification to cmm with sa_handle + ipsec_nlkey_flow(xfrm_nr, xfrm_handle, (const struct flowi *)&fl, family, fl_dir); + } + +std_path: +#endif xfrm_pols_put(pols, npols); return 1; } @@ -2506,7 +2600,10 @@ static void __net_init xfrm_dst_ops_init(struct net *net) read_unlock_bh(&xfrm_policy_afinfo_lock); } -static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) +#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD) +static +#endif +struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) { struct xfrm_policy_afinfo *afinfo; if (unlikely(family >= NPROTO)) @@ -2517,6 +2614,9 @@ static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) read_unlock(&xfrm_policy_afinfo_lock); return afinfo; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +EXPORT_SYMBOL(xfrm_policy_get_afinfo); +#endif static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) { diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 9414b9c..520035a 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -39,6 +39,10 @@ static DEFINE_SPINLOCK(xfrm_state_lock); static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +static unsigned short xfrm_state_handle; +#endif + static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); @@ -66,11 +70,20 @@ xfrm_spi_hash(struct net *net, const xfrm_address_t *daddr, return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask); } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +static void xfrm_hash_transfer(struct hlist_head *list, + struct hlist_head *ndsttable, + struct hlist_head *nsrctable, + struct hlist_head *nspitable, + struct hlist_head *nhtable, + unsigned int nhashmask) +#else static void xfrm_hash_transfer(struct hlist_head *list, struct hlist_head *ndsttable, struct hlist_head *nsrctable, struct hlist_head *nspitable, unsigned int nhashmask) +#endif { struct hlist_node *entry, *tmp; struct xfrm_state *x; @@ -94,6 +107,11 @@ static void xfrm_hash_transfer(struct hlist_head *list, nhashmask); hlist_add_head(&x->byspi, nspitable+h); } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (x->handle) { + hlist_add_head(&x->byh, nhtable+(x->handle & nhashmask)); + } +#endif } } @@ -108,6 +126,9 @@ static void xfrm_hash_resize(struct work_struct *work) { struct net *net = container_of(work, struct net, xfrm.state_hash_work); struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + struct hlist_head *nh, *oh; +#endif unsigned long nsize, osize; unsigned int nhashmask, ohashmask; int i; @@ -129,22 +150,41 @@ static void xfrm_hash_resize(struct work_struct *work) xfrm_hash_free(nsrc, nsize); goto out_unlock; } - +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + nh = xfrm_hash_alloc(nsize); + if (!nh) { + xfrm_hash_free(ndst, nsize); + xfrm_hash_free(nsrc, nsize); + xfrm_hash_free(nspi, nsize); + goto out_unlock; + } +#endif spin_lock_bh(&xfrm_state_lock); nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; for (i = net->xfrm.state_hmask; i >= 0; i--) - xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi, +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi, nh, nhashmask); +#else + xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi, + nhashmask); +#endif odst = net->xfrm.state_bydst; osrc = net->xfrm.state_bysrc; ospi = net->xfrm.state_byspi; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + oh = net->xfrm.state_byh; +#endif ohashmask = net->xfrm.state_hmask; net->xfrm.state_bydst = ndst; net->xfrm.state_bysrc = nsrc; net->xfrm.state_byspi = nspi; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + net->xfrm.state_byh = nh; +#endif net->xfrm.state_hmask = nhashmask; spin_unlock_bh(&xfrm_state_lock); @@ -153,6 +193,9 @@ static void xfrm_hash_resize(struct work_struct *work) xfrm_hash_free(odst, osize); xfrm_hash_free(osrc, osize); xfrm_hash_free(ospi, osize); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + xfrm_hash_free(oh, osize); +#endif out_unlock: mutex_unlock(&hash_resize_mutex); @@ -494,6 +537,9 @@ struct xfrm_state *xfrm_state_alloc(struct net *net) INIT_HLIST_NODE(&x->bydst); INIT_HLIST_NODE(&x->bysrc); INIT_HLIST_NODE(&x->byspi); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + INIT_HLIST_NODE(&x->byh); +#endif tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler, CLOCK_REALTIME, HRTIMER_MODE_ABS); setup_timer(&x->rtimer, xfrm_replay_timer_handler, (unsigned long)x); @@ -504,6 +550,12 @@ struct xfrm_state *xfrm_state_alloc(struct net *net) x->lft.hard_packet_limit = XFRM_INF; x->replay_maxage = 0; x->replay_maxdiff = 0; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + x->handle = xfrm_state_handle++; + if (x->handle == 0) + x->handle = xfrm_state_handle++; + hlist_add_head(&x->byh, net->xfrm.state_byh+(x->handle & net->xfrm.state_hmask)); +#endif x->inner_mode = NULL; x->inner_mode_iaf = NULL; spin_lock_init(&x->lock); @@ -538,6 +590,10 @@ int __xfrm_state_delete(struct xfrm_state *x) hlist_del(&x->bysrc); if (x->id.spi) hlist_del(&x->byspi); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + if (x->handle) + hlist_del(&x->byh); +#endif net->xfrm.state_num--; spin_unlock(&xfrm_state_lock); @@ -1408,6 +1464,37 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark, } EXPORT_SYMBOL(xfrm_state_lookup_byaddr); +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +struct xfrm_state *__xfrm_state_lookup_byhandle(struct net *net, u16 handle) +{ + unsigned int h = (handle & net->xfrm.state_hmask); + struct xfrm_state *x; + struct hlist_node *entry; + + hlist_for_each_entry(x, entry, net->xfrm.state_byh+h, byh) { + if (x->handle != handle) + continue; + + xfrm_state_hold(x); + return x; + } + + return NULL; +} + +struct xfrm_state * +xfrm_state_lookup_byhandle(struct net *net, u16 handle) +{ + struct xfrm_state *x; + + spin_lock_bh(&xfrm_state_lock); + x = __xfrm_state_lookup_byhandle(net, handle); + spin_unlock_bh(&xfrm_state_lock); + return x; +} +EXPORT_SYMBOL(xfrm_state_lookup_byhandle); +#endif + struct xfrm_state * xfrm_find_acq(struct net *net, struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto, const xfrm_address_t *daddr, const xfrm_address_t *saddr, @@ -2026,6 +2113,12 @@ int __net_init xfrm_state_init(struct net *net) net->xfrm.state_byspi = xfrm_hash_alloc(sz); if (!net->xfrm.state_byspi) goto out_byspi; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + net->xfrm.state_byh = xfrm_hash_alloc(sz); + if (!net->xfrm.state_byh) + goto out_byh; + get_random_bytes(&xfrm_state_handle, sizeof(xfrm_state_handle)); +#endif net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1); net->xfrm.state_num = 0; @@ -2035,6 +2128,10 @@ int __net_init xfrm_state_init(struct net *net) init_waitqueue_head(&net->xfrm.km_waitq); return 0; +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +out_byh: + xfrm_hash_free(net->xfrm.state_byspi, sz); +#endif out_byspi: xfrm_hash_free(net->xfrm.state_bysrc, sz); out_bysrc: diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 5d986d9..973325c 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -296,7 +296,7 @@ cmd_bzip2 = (cat $(filter-out FORCE,$^) | \ quiet_cmd_lzma = LZMA $@ cmd_lzma = (cat $(filter-out FORCE,$^) | \ - lzma -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ + lzma e -d20 -lc1 -lp2 -pb2 -eos -si -so && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ (rm -f $@ ; false) quiet_cmd_lzo = LZO $@ diff --git a/scripts/gen_initramfs_list.sh b/scripts/gen_initramfs_list.sh index b482f16..59ffa51 100644 --- a/scripts/gen_initramfs_list.sh +++ b/scripts/gen_initramfs_list.sh @@ -226,7 +226,7 @@ cpio_list= output="/dev/stdout" output_file="" is_cpio_compressed= -compr="gzip -n -9 -f" +compr="gzip -n -9 -f -" arg="$1" case "$arg" in @@ -240,9 +240,9 @@ case "$arg" in output_file="$1" cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)" output=${cpio_list} - echo "$output_file" | grep -q "\.gz$" && compr="gzip -n -9 -f" - echo "$output_file" | grep -q "\.bz2$" && compr="bzip2 -9 -f" - echo "$output_file" | grep -q "\.lzma$" && compr="lzma -9 -f" + echo "$output_file" | grep -q "\.gz$" && compr="gzip -n -9 -f -" + echo "$output_file" | grep -q "\.bz2$" && compr="bzip2 -9 -f -" + echo "$output_file" | grep -q "\.lzma$" && compr="lzma e -d20 -lc1 -lp2 -pb2 -eos -si -so" echo "$output_file" | grep -q "\.xz$" && \ compr="xz --check=crc32 --lzma2=dict=1MiB" echo "$output_file" | grep -q "\.lzo$" && compr="lzop -9 -f" @@ -303,7 +303,7 @@ if [ ! -z ${output_file} ]; then if [ "${is_cpio_compressed}" = "compressed" ]; then cat ${cpio_tfile} > ${output_file} else - (cat ${cpio_tfile} | ${compr} - > ${output_file}) \ + (cat ${cpio_tfile} | ${compr} > ${output_file}) \ || (rm -f ${output_file} ; false) fi [ -z ${cpio_file} ] && rm ${cpio_tfile} diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 487ac6f..95ca1f3 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -22,6 +22,35 @@ #include #include #include +#ifdef __APPLE__ +/* Darwin has no memmem implementation, this one is ripped of the uClibc-0.9.28 source */ +void *memmem (const void *haystack, size_t haystack_len, + const void *needle, size_t needle_len) +{ + const char *begin; + const char *const last_possible + = (const char *) haystack + haystack_len - needle_len; + + if (needle_len == 0) + /* The first occurrence of the empty string is deemed to occur at + the beginning of the string. */ + return (void *) haystack; + + /* Sanity check, otherwise the loop might search through the whole + memory. */ + if (__builtin_expect (haystack_len < needle_len, 0)) + return NULL; + + for (begin = (const char *) haystack; begin <= last_possible; ++begin) + if (begin[0] == ((const char *) needle)[0] && + !memcmp ((const void *) &begin[1], + (const void *) ((const char *) needle + 1), + needle_len - 1)) + return (void *) begin; + + return NULL; +} +#endif #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 914833d..d127f25 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -123,6 +123,9 @@ check-lxdialog := $(srctree)/$(src)/lxdialog/check-lxdialog.sh # we really need to do so. (Do not call gcc as part of make mrproper) HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) \ -DLOCALE +ifeq ($(shell uname -s),Darwin) +HOST_LOADLIBES += -lncurses +endif # =========================================================================== # Shared Makefile for the various kconfig executables: diff --git a/scripts/mod/mk_elfconfig.c b/scripts/mod/mk_elfconfig.c index 639bca7..d20b17e 100644 --- a/scripts/mod/mk_elfconfig.c +++ b/scripts/mod/mk_elfconfig.c @@ -1,7 +1,11 @@ #include #include #include +#ifndef __APPLE__ #include +#else +#include "../../../../../tools/sstrip/include/elf.h" +#endif int main(int argc, char **argv) diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index 51207e4..9c67fb3 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -7,7 +7,11 @@ #include #include #include +#if !(defined(__APPLE__) || defined(__CYGWIN__)) #include +#else +#include "../../../../../tools/sstrip/include/elf.h" +#endif #include "elfconfig.h"