--- orig/linux-2.6.11.4/arch/ppc/kernel/dma-mapping.c 2005-03-15 16:08:58.000000000 -0800 +++ linux-2.6.11.4/arch/ppc/kernel/dma-mapping.c 2005-05-27 19:06:27.956937000 -0700 @@ -17,6 +17,11 @@ * implementation. This is pulled straight from ARM and barely * modified. -Matt * + * Changed consistent_pte to consistent_ptes, in order to remove the + * limitation of 2MB consistent DMA memory. Now the size is confined by + * CONFIG_CONSISTENT_SIZE. And there are multiple pages containing PTE. + * - Xiaogeng (Shawn) Jin (Agilent Technologies), 2005 + * * 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. @@ -66,11 +71,14 @@ #define CONSISTENT_BASE (CONFIG_CONSISTENT_START) #define CONSISTENT_END (CONFIG_CONSISTENT_START + CONFIG_CONSISTENT_SIZE) #define CONSISTENT_OFFSET(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PAGE_SHIFT) +#define CONSISTENT_PTE_PAGENR ((CONFIG_CONSISTENT_SIZE >> PAGE_SHIFT) >> PTE_SHIFT) +#define CONSISTENT_SIZE_PER_PTEPAGE (1UL << (PAGE_SHIFT + PTE_SHIFT)) /* - * This is the page table (2MB) covering uncached, DMA consistent allocations + * This is page tables covering uncached, DMA consistent allocations, + * with the size of CONFIG_CONSISTENT_SIZE */ -static pte_t *consistent_pte; +pte_t **consistent_ptes = NULL; static DEFINE_SPINLOCK(consistent_lock); /* @@ -180,7 +188,7 @@ unsigned long order; u64 mask = 0x00ffffff, limit; /* ISA default */ - if (!consistent_pte) { + if (!consistent_ptes) { printk(KERN_ERR "%s: not initialised\n", __func__); dump_stack(); return NULL; @@ -219,9 +227,14 @@ c = vm_region_alloc(&consistent_head, size, gfp & ~(__GFP_DMA | __GFP_HIGHMEM)); if (c) { - pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start); + pte_t *pte; + int offset = CONSISTENT_OFFSET(c->vm_start); + int idx = offset >> PTE_SHIFT; struct page *end = page + (1 << order); + offset &= (1 << PTE_SHIFT) - 1; + pte = consistent_ptes[idx] + offset; + /* * Set the "dma handle" */ @@ -235,6 +248,11 @@ set_pte(pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL))); page++; pte++; + offset++; + if (offset > ((1 << PTE_SHIFT) - 1)) { + pte = consistent_ptes[++idx]; + offset = 0; + } } while (size -= PAGE_SIZE); /* @@ -264,6 +282,7 @@ struct vm_region *c; unsigned long flags; pte_t *ptep; + int offset, idx; size = PAGE_ALIGN(size); @@ -280,12 +299,21 @@ size = c->vm_end - c->vm_start; } - ptep = consistent_pte + CONSISTENT_OFFSET(c->vm_start); + offset = CONSISTENT_OFFSET(c->vm_start); + idx = offset >> PTE_SHIFT; + offset &= (1 << PTE_SHIFT) - 1; + ptep = consistent_ptes[idx] + offset; + do { pte_t pte = ptep_get_and_clear(ptep); unsigned long pfn; ptep++; + offset++; + if (offset > ((1 << PTE_SHIFT) - 1)) { + ptep = consistent_ptes[++idx]; + offset = 0; + } if (!pte_none(pte) && pte_present(pte)) { pfn = pte_pfn(pte); @@ -328,13 +356,22 @@ pgd_t *pgd; pmd_t *pmd; pte_t *pte; - int ret = 0; + int ret = 0, i; + unsigned long address; spin_lock(&init_mm.page_table_lock); - do { - pgd = pgd_offset(&init_mm, CONSISTENT_BASE); - pmd = pmd_alloc(&init_mm, pgd, CONSISTENT_BASE); + consistent_ptes = (pte_t **)kmalloc(sizeof(pte_t **) * + CONSISTENT_PTE_PAGENR, GFP_KERNEL); + if (!consistent_ptes) { + ret = -ENOMEM; + goto out; + } + + + for (i = 0, address = CONSISTENT_BASE; i < CONSISTENT_PTE_PAGENR; i++) { + pgd = pgd_offset(&init_mm, address); + pmd = pmd_alloc(&init_mm, pgd, address); if (!pmd) { printk(KERN_ERR "%s: no pmd tables\n", __func__); ret = -ENOMEM; @@ -342,16 +379,19 @@ } WARN_ON(!pmd_none(*pmd)); - pte = pte_alloc_kernel(&init_mm, pmd, CONSISTENT_BASE); + pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) { printk(KERN_ERR "%s: no pte tables\n", __func__); ret = -ENOMEM; break; } - consistent_pte = pte; - } while (0); + consistent_ptes[i] = pte; + + address += CONSISTENT_SIZE_PER_PTEPAGE; + } +out: spin_unlock(&init_mm.page_table_lock); return ret;