Did this fix your NFS problem?
I'm working on discontiguous memory platforms as well.
Jon Fraser
Broadcom
Andover, Mass.
On Wed, 2008-03-12 at 19:31 -0700, David VomLehn wrote:
> This patch is a work in progress, per Ralf's suggestion from last week. It is
> intended to fix dcache flushing issues when using HIGHMEM support. We get
> much
> better results with this patch applied, but I would not characterize our
> 2.6.24
> port as stable, yet, so there may be other HIGHMEM-related issues.
>
> Any comments welcome. Thanks!
>
> David VomLehn
>
> diff -urN a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
> --- a/arch/mips/mm/cache.c 2008-01-24 14:58:37.000000000 -0800
> +++ b/arch/mips/mm/cache.c 2008-03-12 19:11:39.000000000 -0700
> @@ -19,6 +19,7 @@
> #include <asm/processor.h>
> #include <asm/cpu.h>
> #include <asm/cpu-features.h>
> +#include <asm/highmem.h>
>
> /* Cache operations. */
> void (*flush_cache_all)(void);
> @@ -70,10 +71,28 @@
> void __flush_dcache_page(struct page *page)
> {
> struct address_space *mapping = page_mapping(page);
> - unsigned long addr;
> + void * addr;
>
> +#ifndef CONFIG_HIGHMEM
> if (PageHighMem(page))
> return;
> +#endif
> +
> + /* If there is a temporary kernel mapping, i.e. if kmap_atomic was
> + * used to map a page, we only need to flush the page. We can skip
> + * the other work here because it won't be used in any other way. */
> + if (PageHighMem(page)) {
> + addr = kmap_atomic_to_vaddr(page);
> + if (addr != NULL) {
> + flush_data_cache_page((unsigned long) addr);
> + return;
> + }
> + }
> +
> + /* If page_mapping returned a non-NULL value, then the page is not
> + * in the swap cache and it isn't anonymously mapped. If it's not
> + * already mapped into user space, we can just set the dirty bit to
> + * get the cache flushed later, if needed */
> if (mapping && !mapping_mapped(mapping)) {
> SetPageDcacheDirty(page);
> return;
> @@ -84,8 +103,10 @@
> * case is for exec env/arg pages and those are %99 certainly going to
> * get faulted into the tlb (and thus flushed) anyways.
> */
> - addr = (unsigned long) page_address(page);
> - flush_data_cache_page(addr);
> + addr = page_address(page);
> + /* If the page is not mapped for kernel access, don't flush it */
> + if (addr != NULL)
> + flush_data_cache_page((unsigned long) addr);
> }
>
> EXPORT_SYMBOL(__flush_dcache_page);
> diff -urN a/arch/mips/mm/highmem.c b/arch/mips/mm/highmem.c
> --- a/arch/mips/mm/highmem.c 2008-01-24 14:58:37.000000000 -0800
> +++ b/arch/mips/mm/highmem.c 2008-03-12 18:58:10.000000000 -0700
> @@ -25,6 +25,25 @@
> }
>
> /*
> + * Describes one page->virtual association in kmap_atomic
> + */
> +struct kmap_atomic_map {
> + struct list_head list;
> + struct page *page;
> +};
> +
> +/*
> + * Array of linked lists of the mappings currently in use. The array is
> + * indexed by the CPU number, so we don't have to worry about synchronizing
> + * between the CPUs. We can add maps in interrupt handlers, however, so
> + * we will need to block interrupts when manipulating the linked list.
> + */
> +static struct kmap_atomic_map_list {
> + struct list_head lh;
> + struct kmap_atomic_map map_pool[KM_TYPE_NR];
> +} ____cacheline_aligned_in_smp map_list[NR_CPUS];
> +
> +/*
> * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap
> because
> * no global lock is needed and because the kmap code must perform a global
> TLB
> * invalidation when the kmap pool wraps.
> @@ -37,6 +56,7 @@
> {
> enum fixed_addresses idx;
> unsigned long vaddr;
> + unsigned long flags;
>
> /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
> pagefault_disable();
> @@ -52,11 +72,18 @@
> set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));
> local_flush_tlb_one((unsigned long)vaddr);
>
> + local_irq_save(flags);
> + map_list[smp_processor_id()].map_pool[type].page = page;
> + list_add(&(map_list[smp_processor_id()].map_pool[type]).list,
> &map_list[smp_processor_id()].lh);
> + local_irq_restore(flags);
> +
> return (void*) vaddr;
> }
>
> void __kunmap_atomic(void *kvaddr, enum km_type type)
> {
> + unsigned long flags;
> +
> #ifdef CONFIG_DEBUG_HIGHMEM
> unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
> enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
> @@ -75,8 +102,14 @@
> */
> pte_clear(&init_mm, vaddr, kmap_pte-idx);
> local_flush_tlb_one(vaddr);
> +
> + map_list[smp_processor_id()].map_pool[type].page = NULL;
> #endif
>
> + local_irq_save(flags);
> + list_del(&(map_list[smp_processor_id()].map_pool[type]).list);
> + local_irq_restore(flags);
> +
> pagefault_enable();
> }
>
> @@ -112,6 +145,40 @@
> return pte_page(*pte);
> }
>
> +void *kmap_atomic_to_vaddr(struct page *page)
> +{
> + unsigned long flags;
> + struct kmap_atomic_map *map;
> + unsigned long vaddr = 0;
> +
> + local_irq_save(flags);
> + list_for_each_entry(map, &map_list[smp_processor_id()].lh, list) {
> + if (map->page == page) {
> + vaddr = __fix_to_virt(FIX_KMAP_BEGIN +
> + (map - map_list[smp_processor_id()].map_pool));
> + break;
> + }
> + }
> + local_irq_restore(flags);
> +
> + return (void *)vaddr;
> +}
> +
> +void __init kmap_atomic_init(void)
> +{
> + int i, j;
> +
> + for (i = 0; i < NR_CPUS; i++) {
> + INIT_LIST_HEAD(&map_list[i].lh);
> +#ifdef CONFIG_DEBUG_HIGHMEM
> + for (j = 0; j < KM_TYPE_NR; j++) {
> + INIT_LIST_HEAD(&(map_list[i].map_pool[j]).list);
> + map_list[i].map_pool[j].page = NULL;
> + }
> +#endif
> + }
> +}
> +
> EXPORT_SYMBOL(__kmap);
> EXPORT_SYMBOL(__kunmap);
> EXPORT_SYMBOL(__kmap_atomic);
> diff -urN a/arch/mips/mm/init.c b/arch/mips/mm/init.c
> --- a/arch/mips/mm/init.c 2008-01-24 14:58:37.000000000 -0800
> +++ b/arch/mips/mm/init.c 2008-03-12 19:12:51.000000000 -0700
> @@ -354,6 +354,7 @@
>
> #ifdef CONFIG_HIGHMEM
> kmap_init();
> + kmap_atomic_init();
> #endif
> kmap_coherent_init();
>
> diff -urN a/include/asm-mips/highmem.h b/include/asm-mips/highmem.h
> --- a/include/asm-mips/highmem.h 2008-01-24 14:58:37.000000000 -0800
> +++ b/include/asm-mips/highmem.h 2008-03-12 18:57:22.000000000 -0700
> @@ -55,6 +55,9 @@
> extern void *kmap_atomic_pfn(unsigned long pfn, enum km_type type);
> extern struct page *__kmap_atomic_to_page(void *ptr);
>
> +extern void *kmap_atomic_to_vaddr(struct page *page);
> +extern void kmap_atomic_init(void);
> +
> #define kmap __kmap
> #define kunmap __kunmap
> #define kmap_atomic __kmap_atomic
>
>
>
|