Since upgrading our Linux kernel from 2.6.17 to 2.6.18.1, we've had problems
booting a board using the MIPS 34K core. The kernel is configured for UP (no
SMP or SMTC). The boot hangs right around the time when /sbin/init is executed
(after the "Freeing unused kernel memory" message). This only happens when
booting right after power-cycling the board. The boot succeeds after a reset.
My investigation shows this behaviour was introduced between 2.6.17.10 and
2.6.17.12 (2.6.17.11 failed to build, so I didn't test that version). In fact,
the patch below, applied against 2.6.17.10, is sufficient to cause the boot
problem. The bulk of this patch includes a fix from Aug. 31, 2006 for dcache
aliasing on fork. If I exclude the dcache aliasing fix from this patch, the
boot problem apparently disappears.
Some more interesting details:
- We're using a squashfs root filesystem in RAM (squashfs 3.1-r2). We have not
been able to reproduce with cramfs.
- /sbin/init is from util-linux 2.12r. We have not been able to reproduce with
sysvinit 2.86.
- When using a EJTAG probe to trace what the kernel is doing at this time it
appears to be looping through do_signal(), work_notifysig(), do_notify_resume().
Any insight into possible causes for this lock-up would be greatly appreciated.
Thanks,
Trevor
2.6.17.10-2.6.17.12 patch
=========================
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index 6344be4..ff59d8e 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -648,6 +648,10 @@ #endif /* CONFIG_MIPS_MT_FPAFF */
sys sys_splice 4
sys sys_sync_file_range 7 /* 4305 */
sys sys_tee 4
+ sys sys_vmsplice 4
+ sys sys_ni_syscall 0
+ sys sys_set_robust_list 2
+ sys sys_get_robust_list 3
.endm
/* We pre-compute the number of _instruction_ bytes needed to
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index 12d96c7..9de0778 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -463,3 +463,7 @@ sys_call_table:
PTR sys_splice
PTR sys_sync_file_range
PTR sys_tee /* 5265 */
+ PTR sys_vmsplice
+ PTR sys_ni_syscall
+ PTR sys_set_robust_list
+ PTR sys_get_robust_list
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 6856985..13e36b6 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -389,3 +389,7 @@ EXPORT(sysn32_call_table)
PTR sys_splice
PTR sys_sync_file_range
PTR sys_tee
+ PTR sys_vmsplice /* 6270 */
+ PTR sys_ni_syscall
+ PTR compat_sys_set_robust_list
+ PTR compat_sys_get_robust_list
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 0e63293..e6c8d1a 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -511,4 +511,8 @@ sys_call_table:
PTR sys_splice
PTR sys32_sync_file_range /* 4305 */
PTR sys_tee
+ PTR sys_vmsplice
+ PTR sys_ni_syscall
+ PTR compat_sys_set_robust_list
+ PTR compat_sys_get_robust_list /* 4310 */
.size sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
index 5e8a18a..b280f51 100644
--- a/arch/mips/kernel/syscall.c
+++ b/arch/mips/kernel/syscall.c
@@ -117,6 +117,22 @@ unsigned long arch_get_unmapped_area(str
}
}
+int mips_mmap_check(unsigned long addr, unsigned long len,
+ unsigned long flags)
+{
+#ifdef CONFIG_MIPS32_COMPAT
+ if (current->thread.mflags & MF_32BIT_ADDR) {
+ if (len > TASK_SIZE32)
+ return -EINVAL;
+ if (flags & MAP_FIXED &&
+ (addr >= TASK_SIZE32 || addr + len >= TASK_SIZE32))
+ return -EINVAL;
+ }
+#endif
+
+ return 0;
+}
+
/* common code for old and new mmaps */
static inline unsigned long
do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index de327b1..e18c39b 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -456,7 +456,7 @@ static inline void local_r4k_flush_cache
}
}
if (exec) {
- if (cpu_has_vtag_icache) {
+ if (cpu_has_vtag_icache && mm == current->active_mm) {
int cpu = smp_processor_id();
if (cpu_context(cpu, mm) != 0)
@@ -580,7 +580,7 @@ static inline void local_r4k_flush_icach
* We're not sure of the virtual address(es) involved here, so
* we have to flush the entire I-cache.
*/
- if (cpu_has_vtag_icache) {
+ if (cpu_has_vtag_icache && vma->vm_mm == current->active_mm) {
int cpu = smp_processor_id();
if (cpu_context(cpu, vma->vm_mm) != 0)
diff --git a/arch/mips/mm/c-sb1.c b/arch/mips/mm/c-sb1.c
index 87c417f..7849678 100644
--- a/arch/mips/mm/c-sb1.c
+++ b/arch/mips/mm/c-sb1.c
@@ -156,6 +156,26 @@ static inline void __sb1_flush_icache_al
}
/*
+ * Invalidate a range of the icache. The addresses are virtual, and
+ * the cache is virtually indexed and tagged. However, we don't
+ * necessarily have the right ASID context, so use index ops instead
+ * of hit ops.
+ */
+static inline void __sb1_flush_icache_range(unsigned long start,
+ unsigned long end)
+{
+ start &= ~(icache_line_size - 1);
+ end = (end + icache_line_size - 1) & ~(icache_line_size - 1);
+
+ while (start != end) {
+ cache_set_op(Index_Invalidate_I, start & icache_index_mask);
+ start += icache_line_size;
+ }
+ mispredict();
+ sync();
+}
+
+/*
* Flush the icache for a given physical page. Need to writeback the
* dcache first, then invalidate the icache. If the page isn't
* executable, nothing is required.
@@ -174,8 +194,11 @@ #endif
/*
* Bumping the ASID is probably cheaper than the flush ...
*/
- if (cpu_context(cpu, vma->vm_mm) != 0)
- drop_mmu_context(vma->vm_mm, cpu);
+ if (vma->vm_mm == current->active_mm) {
+ if (cpu_context(cpu, vma->vm_mm) != 0)
+ drop_mmu_context(vma->vm_mm, cpu);
+ } else
+ __sb1_flush_icache_range(addr, addr + PAGE_SIZE);
}
#ifdef CONFIG_SMP
@@ -211,26 +234,6 @@ void sb1_flush_cache_page(struct vm_area
__attribute__((alias("local_sb1_flush_cache_page")));
#endif
-/*
- * Invalidate a range of the icache. The addresses are virtual, and
- * the cache is virtually indexed and tagged. However, we don't
- * necessarily have the right ASID context, so use index ops instead
- * of hit ops.
- */
-static inline void __sb1_flush_icache_range(unsigned long start,
- unsigned long end)
-{
- start &= ~(icache_line_size - 1);
- end = (end + icache_line_size - 1) & ~(icache_line_size - 1);
-
- while (start != end) {
- cache_set_op(Index_Invalidate_I, start & icache_index_mask);
- start += icache_line_size;
- }
- mispredict();
- sync();
-}
-
/*
* Invalidate all caches on this CPU
@@ -327,9 +330,12 @@ #endif
* If there's a context, bump the ASID (cheaper than a flush,
* since we don't know VAs!)
*/
- if (cpu_context(cpu, vma->vm_mm) != 0) {
- drop_mmu_context(vma->vm_mm, cpu);
- }
+ if (vma->vm_mm == current->active_mm) {
+ if (cpu_context(cpu, vma->vm_mm) != 0)
+ drop_mmu_context(vma->vm_mm, cpu);
+ } else
+ __sb1_flush_icache_range(start, start + PAGE_SIZE);
+
}
#ifdef CONFIG_SMP
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 33f6e1c..9068eb3 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -31,11 +31,39 @@ #include <asm/bootinfo.h>
#include <asm/cachectl.h>
#include <asm/cpu.h>
#include <asm/dma.h>
+#include <asm/kmap_types.h>
#include <asm/mmu_context.h>
#include <asm/sections.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/tlb.h>
+#include <asm/fixmap.h>
+
+/* CP0 hazard avoidance. */
+#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \
+ "nop; nop; nop; nop; nop; nop;\n\t" \
+ ".set reorder\n\t")
+
+/* Atomicity and interruptability */
+#ifdef CONFIG_MIPS_MT_SMTC
+
+#include <asm/mipsmtregs.h>
+
+#define ENTER_CRITICAL(flags) \
+ { \
+ unsigned int mvpflags; \
+ local_irq_save(flags);\
+ mvpflags = dvpe()
+#define EXIT_CRITICAL(flags) \
+ evpe(mvpflags); \
+ local_irq_restore(flags); \
+ }
+#else
+
+#define ENTER_CRITICAL(flags) local_irq_save(flags)
+#define EXIT_CRITICAL(flags) local_irq_restore(flags)
+
+#endif /* CONFIG_MIPS_MT_SMTC */
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
@@ -81,13 +109,183 @@ unsigned long setup_zero_pages(void)
return 1UL << order;
}
-#ifdef CONFIG_HIGHMEM
-pte_t *kmap_pte;
-pgprot_t kmap_prot;
+/*
+ * These are almost like kmap_atomic / kunmap_atmic except they take an
+ * additional address argument as the hint.
+ */
#define kmap_get_fixmap_pte(vaddr) \
pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)),
(vaddr)), (vaddr))
+#ifdef CONFIG_MIPS_MT_SMTC
+static pte_t *kmap_coherent_pte;
+static void __init kmap_coherent_init(void)
+{
+ unsigned long vaddr;
+
+ /* cache the first coherent kmap pte */
+ vaddr = __fix_to_virt(FIX_CMAP_BEGIN);
+ kmap_coherent_pte = kmap_get_fixmap_pte(vaddr);
+}
+#else
+static inline void kmap_coherent_init(void) {}
+#endif
+
+static inline void *kmap_coherent(struct page *page, unsigned long addr)
+{
+ enum fixed_addresses idx;
+ unsigned long vaddr, flags, entrylo;
+ unsigned long old_ctx;
+ pte_t pte;
+ unsigned int tlbidx;
+
+ inc_preempt_count();
+ idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1);
+#ifdef CONFIG_MIPS_MT_SMTC
+ idx += FIX_N_COLOURS * smp_processor_id();
+#endif
+ vaddr = __fix_to_virt(FIX_CMAP_END - idx);
+ pte = mk_pte(page, PAGE_KERNEL);
+#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32_R1)
+ entrylo = pte.pte_high;
+#else
+ entrylo = pte_val(pte) >> 6;
+#endif
+
+ ENTER_CRITICAL(flags);
+ old_ctx = read_c0_entryhi();
+ write_c0_entryhi(vaddr & (PAGE_MASK << 1));
+ write_c0_entrylo0(entrylo);
+ write_c0_entrylo1(entrylo);
+#ifdef CONFIG_MIPS_MT_SMTC
+ set_pte(kmap_coherent_pte - (FIX_CMAP_END - idx), pte);
+ /* preload TLB instead of local_flush_tlb_one() */
+ mtc0_tlbw_hazard();
+ tlb_probe();
+ BARRIER;
+ tlbidx = read_c0_index();
+ mtc0_tlbw_hazard();
+ if (tlbidx < 0)
+ tlb_write_random();
+ else
+ tlb_write_indexed();
+#else
+ tlbidx = read_c0_wired();
+ write_c0_wired(tlbidx + 1);
+ write_c0_index(tlbidx);
+ mtc0_tlbw_hazard();
+ tlb_write_indexed();
+#endif
+ tlbw_use_hazard();
+ write_c0_entryhi(old_ctx);
+ EXIT_CRITICAL(flags);
+
+ return (void*) vaddr;
+}
+
+#define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1)))
+
+static inline void kunmap_coherent(struct page *page)
+{
+#ifndef CONFIG_MIPS_MT_SMTC
+ unsigned int wired;
+ unsigned long flags, old_ctx;
+
+ ENTER_CRITICAL(flags);
+ old_ctx = read_c0_entryhi();
+ wired = read_c0_wired() - 1;
+ write_c0_wired(wired);
+ write_c0_index(wired);
+ write_c0_entryhi(UNIQUE_ENTRYHI(wired));
+ write_c0_entrylo0(0);
+ write_c0_entrylo1(0);
+ mtc0_tlbw_hazard();
+ tlb_write_indexed();
+ write_c0_entryhi(old_ctx);
+ EXIT_CRITICAL(flags);
+#endif
+ dec_preempt_count();
+ preempt_check_resched();
+}
+
+void copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr, struct vm_area_struct *vma)
+{
+ void *vfrom, *vto;
+
+ vto = kmap_atomic(to, KM_USER1);
+ if (cpu_has_dc_aliases) {
+ vfrom = kmap_coherent(from, vaddr);
+ copy_page(vto, vfrom);
+ kunmap_coherent(from);
+ } else {
+ vfrom = kmap_atomic(from, KM_USER0);
+ copy_page(vto, vfrom);
+ kunmap_atomic(vfrom, KM_USER0);
+ }
+ if (((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) ||
+ pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
+ flush_data_cache_page((unsigned long)vto);
+ kunmap_atomic(vto, KM_USER1);
+ /* Make sure this page is cleared on other CPU's too before using it */
+ smp_wmb();
+}
+
+EXPORT_SYMBOL(copy_user_highpage);
+
+void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
+ struct page *to)
+{
+ if (cpu_has_dc_aliases) {
+ struct page *from = virt_to_page(vfrom);
+ vfrom = kmap_coherent(from, vaddr);
+ copy_page(vto, vfrom);
+ kunmap_coherent(from);
+ } else
+ copy_page(vto, vfrom);
+ if (!cpu_has_ic_fills_f_dc ||
+ pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
+ flush_data_cache_page((unsigned long)vto);
+}
+
+EXPORT_SYMBOL(copy_user_page);
+
+void copy_to_user_page(struct vm_area_struct *vma,
+ struct page *page, unsigned long vaddr, void *dst, const void *src,
+ unsigned long len)
+{
+ if (cpu_has_dc_aliases) {
+ void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ memcpy(vto, src, len);
+ kunmap_coherent(page);
+ } else
+ memcpy(dst, src, len);
+ if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc)
+ flush_cache_page(vma, vaddr, page_to_pfn(page));
+}
+
+EXPORT_SYMBOL(copy_to_user_page);
+
+void copy_from_user_page(struct vm_area_struct *vma,
+ struct page *page, unsigned long vaddr, void *dst, const void *src,
+ unsigned long len)
+{
+ if (cpu_has_dc_aliases) {
+ void *vfrom =
+ kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ memcpy(dst, vfrom, len);
+ kunmap_coherent(page);
+ } else
+ memcpy(dst, src, len);
+}
+
+EXPORT_SYMBOL(copy_from_user_page);
+
+
+#ifdef CONFIG_HIGHMEM
+pte_t *kmap_pte;
+pgprot_t kmap_prot;
+
static void __init kmap_init(void)
{
unsigned long kmap_vstart;
@@ -98,11 +296,12 @@ static void __init kmap_init(void)
kmap_prot = PAGE_KERNEL;
}
+#endif /* CONFIG_HIGHMEM */
-#ifdef CONFIG_32BIT
void __init fixrange_init(unsigned long start, unsigned long end,
pgd_t *pgd_base)
{
+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_MIPS_MT_SMTC)
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
@@ -123,7 +322,7 @@ void __init fixrange_init(unsigned long
for (; (k < PTRS_PER_PMD) && (vaddr != end); pmd++,
k++) {
if (pmd_none(*pmd)) {
pte = (pte_t *)
alloc_bootmem_low_pages(PAGE_SIZE);
- set_pmd(pmd, __pmd(pte));
+ set_pmd(pmd, __pmd((unsigned long)pte));
if (pte != pte_offset_kernel(pmd, 0))
BUG();
}
@@ -133,9 +332,8 @@ void __init fixrange_init(unsigned long
}
j = 0;
}
+#endif
}
-#endif /* CONFIG_32BIT */
-#endif /* CONFIG_HIGHMEM */
#ifndef CONFIG_NEED_MULTIPLE_NODES
extern void pagetable_init(void);
@@ -150,6 +348,7 @@ void __init paging_init(void)
#ifdef CONFIG_HIGHMEM
kmap_init();
#endif
+ kmap_coherent_init();
max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
low = max_low_pfn;
diff --git a/arch/mips/mm/pgtable-32.c b/arch/mips/mm/pgtable-32.c
index 4a3c491..9977f03 100644
--- a/arch/mips/mm/pgtable-32.c
+++ b/arch/mips/mm/pgtable-32.c
@@ -32,9 +32,10 @@ void pgd_init(unsigned long page)
void __init pagetable_init(void)
{
-#ifdef CONFIG_HIGHMEM
unsigned long vaddr;
- pgd_t *pgd, *pgd_base;
+ pgd_t *pgd_base;
+#ifdef CONFIG_HIGHMEM
+ pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
@@ -45,7 +46,6 @@ #endif
pgd_init((unsigned long)swapper_pg_dir
+ sizeof(pgd_t) * USER_PTRS_PER_PGD);
-#ifdef CONFIG_HIGHMEM
pgd_base = swapper_pg_dir;
/*
@@ -54,6 +54,7 @@ #ifdef CONFIG_HIGHMEM
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
fixrange_init(vaddr, 0, pgd_base);
+#ifdef CONFIG_HIGHMEM
/*
* Permanent kmaps:
*/
diff --git a/arch/mips/mm/pgtable-64.c b/arch/mips/mm/pgtable-64.c
index 44b5e97..8d600d3 100644
--- a/arch/mips/mm/pgtable-64.c
+++ b/arch/mips/mm/pgtable-64.c
@@ -8,6 +8,7 @@
*/
#include <linux/init.h>
#include <linux/mm.h>
+#include <asm/fixmap.h>
#include <asm/pgtable.h>
void pgd_init(unsigned long page)
@@ -52,7 +53,17 @@ void pmd_init(unsigned long addr, unsign
void __init pagetable_init(void)
{
+ unsigned long vaddr;
+ pgd_t *pgd_base;
+
/* Initialize the entire pgd. */
pgd_init((unsigned long)swapper_pg_dir);
pmd_init((unsigned long)invalid_pmd_table, (unsigned
long)invalid_pte_table);
+
+ pgd_base = swapper_pg_dir;
+ /*
+ * Fixed mappings:
+ */
+ vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
+ fixrange_init(vaddr, 0, pgd_base);
}
diff --git a/arch/mips/pci/ops-au1000.c b/arch/mips/pci/ops-au1000.c
index be14201..25e6fad 100644
--- a/arch/mips/pci/ops-au1000.c
+++ b/arch/mips/pci/ops-au1000.c
@@ -111,7 +111,7 @@ #if defined( CONFIG_SOC_AU1500 ) || defi
if (first_cfg) {
/* reserve a wired entry for pci config accesses */
first_cfg = 0;
- pci_cfg_vm = get_vm_area(0x2000, 0);
+ pci_cfg_vm = get_vm_area(0x2000, VM_IOREMAP);
if (!pci_cfg_vm)
panic (KERN_ERR "PCI unable to get vm area\n");
pci_cfg_wired_entry = read_c0_wired();
diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h
index 36416fd..d10517c 100644
--- a/include/asm-mips/cacheflush.h
+++ b/include/asm-mips/cacheflush.h
@@ -57,24 +57,13 @@ extern void (*flush_icache_range)(unsign
#define flush_cache_vmap(start, end) flush_cache_all()
#define flush_cache_vunmap(start, end) flush_cache_all()
-static inline void copy_to_user_page(struct vm_area_struct *vma,
+extern void copy_to_user_page(struct vm_area_struct *vma,
struct page *page, unsigned long vaddr, void *dst, const void *src,
- unsigned long len)
-{
- if (cpu_has_dc_aliases)
- flush_cache_page(vma, vaddr, page_to_pfn(page));
- memcpy(dst, src, len);
- __flush_icache_page(vma, page);
-}
+ unsigned long len);
-static inline void copy_from_user_page(struct vm_area_struct *vma,
+extern void copy_from_user_page(struct vm_area_struct *vma,
struct page *page, unsigned long vaddr, void *dst, const void *src,
- unsigned long len)
-{
- if (cpu_has_dc_aliases)
- flush_cache_page(vma, vaddr, page_to_pfn(page));
- memcpy(dst, src, len);
-}
+ unsigned long len);
extern void (*flush_cache_sigtramp)(unsigned long addr);
extern void (*flush_icache_all)(void);
diff --git a/include/asm-mips/fixmap.h b/include/asm-mips/fixmap.h
index 73a3028..4878926 100644
--- a/include/asm-mips/fixmap.h
+++ b/include/asm-mips/fixmap.h
@@ -46,8 +46,16 @@ #endif
* fix-mapped?
*/
enum fixed_addresses {
+#define FIX_N_COLOURS 8
+ FIX_CMAP_BEGIN,
+#ifdef CONFIG_MIPS_MT_SMTC
+ FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * NR_CPUS),
+#else
+ FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS,
+#endif
#ifdef CONFIG_HIGHMEM
- FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
+ /* reserved pte's for temporary kernel mappings */
+ FIX_KMAP_BEGIN = FIX_CMAP_END + 1,
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
#endif
__end_of_fixed_addresses
@@ -70,7 +78,7 @@ #define set_fixmap_nocache(idx, phys) \
* the start of the fixmap, and leave one page empty
* at the top of mem..
*/
-#define FIXADDR_TOP (0xffffe000UL)
+#define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000)
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
diff --git a/include/asm-mips/mman.h b/include/asm-mips/mman.h
index 046cf68..e39c01b 100644
--- a/include/asm-mips/mman.h
+++ b/include/asm-mips/mman.h
@@ -75,4 +75,13 @@ #define MADV_DOFORK 11 /* do inherit ac
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
+#ifdef __KERNEL__
+
+#define arch_mmap_check mips_mmap_check
+
+extern int mips_mmap_check(unsigned long addr, unsigned long len,
+ unsigned long flags);
+
+#endif
+
#endif /* _ASM_MMAN_H */
diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h
index c014279..152e573 100644
--- a/include/asm-mips/page.h
+++ b/include/asm-mips/page.h
@@ -39,8 +39,6 @@ #define PAGE_MASK (~((1 << PAGE_SH
#ifdef __KERNEL__
#ifndef __ASSEMBLY__
-#include <asm/cpu-features.h>
-
extern void clear_page(void * page);
extern void copy_page(void * to, void * from);
@@ -64,16 +62,13 @@ static inline void clear_user_page(void
flush_data_cache_page((unsigned long)addr);
}
-static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
- struct page *to)
-{
- extern void (*flush_data_cache_page)(unsigned long addr);
+extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
+ struct page *to);
+struct vm_area_struct;
+extern void copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr, struct vm_area_struct *vma);
- copy_page(vto, vfrom);
- if (!cpu_has_ic_fills_f_dc ||
- pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
- flush_data_cache_page((unsigned long)vto);
-}
+#define __HAVE_ARCH_COPY_USER_HIGHPAGE
/*
* These are used to make use of C type-checking..
@@ -82,15 +77,17 @@ #ifdef CONFIG_64BIT_PHYS_ADDR
#ifdef CONFIG_CPU_MIPS32
typedef struct { unsigned long pte_low, pte_high; } pte_t;
#define pte_val(x) ((x).pte_low | ((unsigned long long)(x).pte_high <<
32))
+ #define __pte(x) ({ pte_t __pte = {(x), ((unsigned long long)(x)) >>
32}; __pte; })
#else
typedef struct { unsigned long long pte; } pte_t;
#define pte_val(x) ((x).pte)
+ #define __pte(x) ((pte_t) { (x) } )
#endif
#else
typedef struct { unsigned long pte; } pte_t;
#define pte_val(x) ((x).pte)
-#endif
#define __pte(x) ((pte_t) { (x) } )
+#endif
/*
* For 3-level pagetables we defines these ourselves, for 2-level the
diff --git a/include/asm-mips/spinlock.h b/include/asm-mips/spinlock.h
index 669b8e3..4c1a1b5 100644
--- a/include/asm-mips/spinlock.h
+++ b/include/asm-mips/spinlock.h
@@ -239,7 +239,51 @@ static inline void __raw_write_unlock(ra
: "memory");
}
-#define __raw_read_trylock(lock) generic__raw_read_trylock(lock)
+static inline int __raw_read_trylock(raw_rwlock_t *rw)
+{
+ unsigned int tmp;
+ int ret;
+
+ if (R10000_LLSC_WAR) {
+ __asm__ __volatile__(
+ " .set noreorder # __raw_read_trylock \n"
+ " li %2, 0 \n"
+ "1: ll %1, %3 \n"
+ " bnez %1, 2f \n"
+ " addu %1, 1 \n"
+ " sc %1, %0 \n"
+ " beqzl %1, 1b \n"
+ " .set reorder \n"
+#ifdef CONFIG_SMP
+ " sync \n"
+#endif
+ " li %2, 1 \n"
+ "2: \n"
+ : "=m" (rw->lock), "=&r" (tmp), "=&r" (ret)
+ : "m" (rw->lock)
+ : "memory");
+ } else {
+ __asm__ __volatile__(
+ " .set noreorder # __raw_read_trylock \n"
+ " li %2, 0 \n"
+ "1: ll %1, %3 \n"
+ " bnez %1, 2f \n"
+ " addu %1, 1 \n"
+ " sc %1, %0 \n"
+ " beqz %1, 1b \n"
+ " .set reorder \n"
+#ifdef CONFIG_SMP
+ " sync \n"
+#endif
+ " li %2, 1 \n"
+ "2: \n"
+ : "=m" (rw->lock), "=&r" (tmp), "=&r" (ret)
+ : "m" (rw->lock)
+ : "memory");
+ }
+
+ return ret;
+}
static inline int __raw_write_trylock(raw_rwlock_t *rw)
{
@@ -283,4 +327,5 @@ static inline int __raw_write_trylock(ra
return ret;
}
+
#endif /* _ASM_SPINLOCK_H */
diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h
index 13fe6e8..2031601 100644
--- a/include/asm-mips/unistd.h
+++ b/include/asm-mips/unistd.h
@@ -327,16 +327,20 @@ #define __NR_unshare (__NR_Linux +
303
#define __NR_splice (__NR_Linux + 304)
#define __NR_sync_file_range (__NR_Linux + 305)
#define __NR_tee (__NR_Linux + 306)
+#define __NR_vmsplice (__NR_Linux + 307)
+#define __NR_move_pages (__NR_Linux + 308)
+#define __NR_set_robust_list (__NR_Linux + 309)
+#define __NR_get_robust_list (__NR_Linux + 310)
/*
* Offset of the last Linux o32 flavoured syscall
*/
-#define __NR_Linux_syscalls 306
+#define __NR_Linux_syscalls 310
#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
#define __NR_O32_Linux 4000
-#define __NR_O32_Linux_syscalls 306
+#define __NR_O32_Linux_syscalls 310
#if _MIPS_SIM == _MIPS_SIM_ABI64
@@ -610,16 +614,20 @@ #define __NR_unshare (__NR_Linux +
262
#define __NR_splice (__NR_Linux + 263)
#define __NR_sync_file_range (__NR_Linux + 264)
#define __NR_tee (__NR_Linux + 265)
+#define __NR_vmsplice (__NR_Linux + 266)
+#define __NR_move_pages (__NR_Linux + 267)
+#define __NR_set_robust_list (__NR_Linux + 268)
+#define __NR_get_robust_list (__NR_Linux + 269)
/*
* Offset of the last Linux 64-bit flavoured syscall
*/
-#define __NR_Linux_syscalls 265
+#define __NR_Linux_syscalls 269
#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
#define __NR_64_Linux 5000
-#define __NR_64_Linux_syscalls 265
+#define __NR_64_Linux_syscalls 269
#if _MIPS_SIM == _MIPS_SIM_NABI32
@@ -897,16 +905,20 @@ #define __NR_unshare (__NR_Linux +
266
#define __NR_splice (__NR_Linux + 267)
#define __NR_sync_file_range (__NR_Linux + 268)
#define __NR_tee (__NR_Linux + 269)
+#define __NR_vmsplice (__NR_Linux + 270)
+#define __NR_move_pages (__NR_Linux + 271)
+#define __NR_set_robust_list (__NR_Linux + 272)
+#define __NR_get_robust_list (__NR_Linux + 273)
/*
* Offset of the last N32 flavoured syscall
*/
-#define __NR_Linux_syscalls 269
+#define __NR_Linux_syscalls 273
#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
#define __NR_N32_Linux 6000
-#define __NR_N32_Linux_syscalls 269
+#define __NR_N32_Linux_syscalls 273
#ifndef __ASSEMBLY__
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index 892c4ea..26e49a1 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -90,7 +90,10 @@ static inline void memclear_highpage_flu
kunmap_atomic(kaddr, KM_USER0);
}
-static inline void copy_user_highpage(struct page *to, struct page *from,
unsigned long vaddr)
+#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE
+
+static inline void copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr, struct vm_area_struct *vma)
{
char *vfrom, *vto;
@@ -103,6 +106,8 @@ static inline void copy_user_highpage(st
smp_wmb();
}
+#endif
+
static inline void copy_highpage(struct page *to, struct page *from)
{
char *vfrom, *vto;
diff --git a/mm/memory.c b/mm/memory.c
index 0ec7bc6..3acd848 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1394,7 +1394,8 @@ static inline pte_t maybe_mkwrite(pte_t
return pte;
}
-static inline void cow_user_page(struct page *dst, struct page *src, unsigned
long va)
+static inline void cow_user_page(struct page *dst, struct page *src,
+ unsigned long va, struct vm_area_struct *vma)
{
/*
* If the source page was a PFN mapping, we don't have
@@ -1418,7 +1419,7 @@ static inline void cow_user_page(struct
return;
}
- copy_user_highpage(dst, src, va);
+ copy_user_highpage(dst, src, va, vma);
}
/*
@@ -1483,7 +1484,7 @@ gotten:
new_page = alloc_page_vma(GFP_HIGHUSER, vma, address);
if (!new_page)
goto oom;
- cow_user_page(new_page, old_page, address);
+ cow_user_page(new_page, old_page, address, vma);
}
/*
@@ -2082,7 +2083,7 @@ retry:
page = alloc_page_vma(GFP_HIGHUSER, vma, address);
if (!page)
goto oom;
- copy_user_highpage(page, new_page, address);
+ copy_user_highpage(page, new_page, address, vma);
page_cache_release(new_page);
new_page = page;
anon = 1;
|