From 2c431537cba5280a4ee99ce2a575bd2bd71cf632 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Mon, 5 Jun 2006 12:09:40 -0700 Subject: [PATCH] sparc64 patches from DaveM --- queue-2.6.16/series | 3 + ...c64-fix-d-cache-corruption-in-mremap.patch | 94 ++++++++ ...fix-missing-fold-at-end-of-checksums.patch | 55 +++++ ...gfp_t-argument-to-dma_alloc_coherent.patch | 225 ++++++++++++++++++ 4 files changed, 377 insertions(+) create mode 100644 queue-2.6.16/sparc64-fix-d-cache-corruption-in-mremap.patch create mode 100644 queue-2.6.16/sparc64-fix-missing-fold-at-end-of-checksums.patch create mode 100644 queue-2.6.16/sparc64-respect-gfp_t-argument-to-dma_alloc_coherent.patch diff --git a/queue-2.6.16/series b/queue-2.6.16/series index 507b183da17..e722024a5ff 100644 --- a/queue-2.6.16/series +++ b/queue-2.6.16/series @@ -1 +1,4 @@ usb-whiteheat-fix-firmware-spurious-errors.patch +sparc64-fix-d-cache-corruption-in-mremap.patch +sparc64-respect-gfp_t-argument-to-dma_alloc_coherent.patch +sparc64-fix-missing-fold-at-end-of-checksums.patch diff --git a/queue-2.6.16/sparc64-fix-d-cache-corruption-in-mremap.patch b/queue-2.6.16/sparc64-fix-d-cache-corruption-in-mremap.patch new file mode 100644 index 00000000000..7be4d58be5c --- /dev/null +++ b/queue-2.6.16/sparc64-fix-d-cache-corruption-in-mremap.patch @@ -0,0 +1,94 @@ +From stable-bounces@linux.kernel.org Fri Jun 2 18:34:53 2006 +Date: Fri, 02 Jun 2006 18:30:58 -0700 (PDT) +From: David Miller +To: stable@kernel.org +Cc: +Subject: SPARC64: Fix D-cache corruption in mremap + +If we move a mapping from one virtual address to another, +and this changes the virtual color of the mapping to those +pages, we can see corrupt data due to D-cache aliasing. + +Check for and deal with this by overriding the move_pte() +macro. Set things up so that other platforms can cleanly +override the move_pte() macro too. + +This long standing bug corrupts user memory, and in particular +has been notorious for corrupting Debian package database +files on sparc64 boxes. + +Signed-off-by: David S. Miller +Signed-off-by: Chris Wright +--- + + include/asm-generic/pgtable.h | 11 +---------- + include/asm-mips/pgtable.h | 10 +++++++++- + include/asm-sparc64/pgtable.h | 17 +++++++++++++++++ + 3 files changed, 27 insertions(+), 11 deletions(-) + +--- linux-2.6.16.20.orig/include/asm-generic/pgtable.h ++++ linux-2.6.16.20/include/asm-generic/pgtable.h +@@ -159,17 +159,8 @@ static inline void ptep_set_wrprotect(st + #define lazy_mmu_prot_update(pte) do { } while (0) + #endif + +-#ifndef __HAVE_ARCH_MULTIPLE_ZERO_PAGE ++#ifndef __HAVE_ARCH_MOVE_PTE + #define move_pte(pte, prot, old_addr, new_addr) (pte) +-#else +-#define move_pte(pte, prot, old_addr, new_addr) \ +-({ \ +- pte_t newpte = (pte); \ +- if (pte_present(pte) && pfn_valid(pte_pfn(pte)) && \ +- pte_page(pte) == ZERO_PAGE(old_addr)) \ +- newpte = mk_pte(ZERO_PAGE(new_addr), (prot)); \ +- newpte; \ +-}) + #endif + + /* +--- linux-2.6.16.20.orig/include/asm-mips/pgtable.h ++++ linux-2.6.16.20/include/asm-mips/pgtable.h +@@ -70,7 +70,15 @@ extern unsigned long zero_page_mask; + #define ZERO_PAGE(vaddr) \ + (virt_to_page(empty_zero_page + (((unsigned long)(vaddr)) & zero_page_mask))) + +-#define __HAVE_ARCH_MULTIPLE_ZERO_PAGE ++#define __HAVE_ARCH_MOVE_PTE ++#define move_pte(pte, prot, old_addr, new_addr) \ ++({ \ ++ pte_t newpte = (pte); \ ++ if (pte_present(pte) && pfn_valid(pte_pfn(pte)) && \ ++ pte_page(pte) == ZERO_PAGE(old_addr)) \ ++ newpte = mk_pte(ZERO_PAGE(new_addr), (prot)); \ ++ newpte; \ ++}) + + extern void paging_init(void); + +--- linux-2.6.16.20.orig/include/asm-sparc64/pgtable.h ++++ linux-2.6.16.20/include/asm-sparc64/pgtable.h +@@ -335,6 +335,23 @@ static inline void set_pte_at(struct mm_ + #define pte_clear(mm,addr,ptep) \ + set_pte_at((mm), (addr), (ptep), __pte(0UL)) + ++#ifdef DCACHE_ALIASING_POSSIBLE ++#define __HAVE_ARCH_MOVE_PTE ++#define move_pte(pte, prot, old_addr, new_addr) \ ++({ \ ++ pte_t newpte = (pte); \ ++ if (pte_present(pte)) { \ ++ unsigned long this_pfn = pte_pfn(pte); \ ++ \ ++ if (pfn_valid(this_pfn) && \ ++ (((old_addr) ^ (new_addr)) & (1 << 13))) \ ++ flush_dcache_page_all(current->mm, \ ++ pfn_to_page(this_pfn)); \ ++ } \ ++ newpte; \ ++}) ++#endif ++ + extern pgd_t swapper_pg_dir[2048]; + extern pmd_t swapper_low_pmd_dir[2048]; + diff --git a/queue-2.6.16/sparc64-fix-missing-fold-at-end-of-checksums.patch b/queue-2.6.16/sparc64-fix-missing-fold-at-end-of-checksums.patch new file mode 100644 index 00000000000..a539dba0404 --- /dev/null +++ b/queue-2.6.16/sparc64-fix-missing-fold-at-end-of-checksums.patch @@ -0,0 +1,55 @@ +From stable-bounces@linux.kernel.org Mon Jun 5 11:30:48 2006 +Date: Mon, 05 Jun 2006 11:27:10 -0700 (PDT) +From: David Miller +To: stable@kernel.org +Cc: +Subject: SPARC64: Fix missing fold at end of checksums. + +Both csum_partial() and the csum_partial_copy*() family of routines +forget to do a final fold on the computed checksum value on sparc64. +So do the standard Sparc "add + set condition codes, add carry" +sequence, then make sure the high 32-bits of the return value are +clear. + +Based upon some excellent detective work and debugging done by +Richard Braun and Samuel Thibault. + +Signed-off-by: David S. Miller +Signed-off-by: Chris Wright +--- + + arch/sparc64/lib/checksum.S | 5 +++-- + arch/sparc64/lib/csum_copy.S | 5 +++-- + 2 files changed, 6 insertions(+), 4 deletions(-) + +--- linux-2.6.16.20.orig/arch/sparc64/lib/checksum.S ++++ linux-2.6.16.20/arch/sparc64/lib/checksum.S +@@ -165,8 +165,9 @@ csum_partial_end_cruft: + sll %g1, 8, %g1 + or %o5, %g1, %o4 + +-1: add %o2, %o4, %o2 ++1: addcc %o2, %o4, %o2 ++ addc %g0, %o2, %o2 + + csum_partial_finish: + retl +- mov %o2, %o0 ++ srl %o2, 0, %o0 +--- linux-2.6.16.20.orig/arch/sparc64/lib/csum_copy.S ++++ linux-2.6.16.20/arch/sparc64/lib/csum_copy.S +@@ -221,11 +221,12 @@ FUNC_NAME: /* %o0=src, %o1=dst, %o2=len + sll %g1, 8, %g1 + or %o5, %g1, %o4 + +-1: add %o3, %o4, %o3 ++1: addcc %o3, %o4, %o3 ++ addc %g0, %o3, %o3 + + 70: + retl +- mov %o3, %o0 ++ srl %o3, 0, %o0 + + 95: mov 0, GLOBAL_SPARE + brlez,pn %o2, 4f diff --git a/queue-2.6.16/sparc64-respect-gfp_t-argument-to-dma_alloc_coherent.patch b/queue-2.6.16/sparc64-respect-gfp_t-argument-to-dma_alloc_coherent.patch new file mode 100644 index 00000000000..73a9d6e9137 --- /dev/null +++ b/queue-2.6.16/sparc64-respect-gfp_t-argument-to-dma_alloc_coherent.patch @@ -0,0 +1,225 @@ +From stable-bounces@linux.kernel.org Sun Jun 4 20:44:37 2006 +Date: Sun, 04 Jun 2006 20:41:00 -0700 (PDT) +From: David Miller +To: stable@kernel.org +Cc: +Subject: SPARC64: Respect gfp_t argument to dma_alloc_coherent(). + +Using asm-generic/dma-mapping.h does not work because pushing +the call down to pci_alloc_coherent() causes the gfp_t argument +of dma_alloc_coherent() to be ignored. + +Fix this by implementing things directly, and adding a gfp_t +argument we can use in the internal call down to the PCI DMA +implementation of pci_alloc_coherent(). + +This fixes massive memory corruption when using the sound driver +layer, which passes things like __GFP_COMP down into these +routines and (correctly) expects that to work. + +This is a disk eater when sound is used, so it's pretty critical. + +Signed-off-by: David S. Miller +Signed-off-by: Chris Wright +--- + + arch/sparc64/kernel/pci_iommu.c | 4 - + arch/sparc64/kernel/sparc64_ksyms.c | 2 + include/asm-sparc64/dma-mapping.h | 141 +++++++++++++++++++++++++++++++++++- + include/asm-sparc64/pci.h | 4 - + 4 files changed, 146 insertions(+), 5 deletions(-) + +--- linux-2.6.16.20.orig/arch/sparc64/kernel/pci_iommu.c ++++ linux-2.6.16.20/arch/sparc64/kernel/pci_iommu.c +@@ -219,7 +219,7 @@ static inline void iommu_free_ctx(struct + * DMA for PCI device PDEV. Return non-NULL cpu-side address if + * successful and set *DMA_ADDRP to the PCI side dma address. + */ +-void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) ++void *__pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp, gfp_t gfp) + { + struct pcidev_cookie *pcp; + struct pci_iommu *iommu; +@@ -233,7 +233,7 @@ void *pci_alloc_consistent(struct pci_de + if (order >= 10) + return NULL; + +- first_page = __get_free_pages(GFP_ATOMIC, order); ++ first_page = __get_free_pages(gfp, order); + if (first_page == 0UL) + return NULL; + memset((char *)first_page, 0, PAGE_SIZE << order); +--- linux-2.6.16.20.orig/arch/sparc64/kernel/sparc64_ksyms.c ++++ linux-2.6.16.20/arch/sparc64/kernel/sparc64_ksyms.c +@@ -221,7 +221,7 @@ EXPORT_SYMBOL(insl); + EXPORT_SYMBOL(ebus_chain); + EXPORT_SYMBOL(isa_chain); + EXPORT_SYMBOL(pci_memspace_mask); +-EXPORT_SYMBOL(pci_alloc_consistent); ++EXPORT_SYMBOL(__pci_alloc_consistent); + EXPORT_SYMBOL(pci_free_consistent); + EXPORT_SYMBOL(pci_map_single); + EXPORT_SYMBOL(pci_unmap_single); +--- linux-2.6.16.20.orig/include/asm-sparc64/dma-mapping.h ++++ linux-2.6.16.20/include/asm-sparc64/dma-mapping.h +@@ -4,7 +4,146 @@ + #include + + #ifdef CONFIG_PCI +-#include ++ ++/* we implement the API below in terms of the existing PCI one, ++ * so include it */ ++#include ++/* need struct page definitions */ ++#include ++ ++static inline int ++dma_supported(struct device *dev, u64 mask) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ return pci_dma_supported(to_pci_dev(dev), mask); ++} ++ ++static inline int ++dma_set_mask(struct device *dev, u64 dma_mask) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ return pci_set_dma_mask(to_pci_dev(dev), dma_mask); ++} ++ ++static inline void * ++dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, ++ gfp_t flag) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ return __pci_alloc_consistent(to_pci_dev(dev), size, dma_handle, flag); ++} ++ ++static inline void ++dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, ++ dma_addr_t dma_handle) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_free_consistent(to_pci_dev(dev), size, cpu_addr, dma_handle); ++} ++ ++static inline dma_addr_t ++dma_map_single(struct device *dev, void *cpu_addr, size_t size, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ return pci_map_single(to_pci_dev(dev), cpu_addr, size, (int)direction); ++} ++ ++static inline void ++dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_unmap_single(to_pci_dev(dev), dma_addr, size, (int)direction); ++} ++ ++static inline dma_addr_t ++dma_map_page(struct device *dev, struct page *page, ++ unsigned long offset, size_t size, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ return pci_map_page(to_pci_dev(dev), page, offset, size, (int)direction); ++} ++ ++static inline void ++dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_unmap_page(to_pci_dev(dev), dma_address, size, (int)direction); ++} ++ ++static inline int ++dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ return pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction); ++} ++ ++static inline void ++dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_unmap_sg(to_pci_dev(dev), sg, nhwentries, (int)direction); ++} ++ ++static inline void ++dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle, ++ size, (int)direction); ++} ++ ++static inline void ++dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle, ++ size, (int)direction); ++} ++ ++static inline void ++dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg, nelems, (int)direction); ++} ++ ++static inline void ++dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, ++ enum dma_data_direction direction) ++{ ++ BUG_ON(dev->bus != &pci_bus_type); ++ ++ pci_dma_sync_sg_for_device(to_pci_dev(dev), sg, nelems, (int)direction); ++} ++ ++static inline int ++dma_mapping_error(dma_addr_t dma_addr) ++{ ++ return pci_dma_mapping_error(dma_addr); ++} ++ + #else + + struct device; +--- linux-2.6.16.20.orig/include/asm-sparc64/pci.h ++++ linux-2.6.16.20/include/asm-sparc64/pci.h +@@ -44,7 +44,9 @@ struct pci_dev; + /* Allocate and map kernel buffer using consistent mode DMA for a device. + * hwdev should be valid struct pci_dev pointer for PCI devices. + */ +-extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle); ++extern void *__pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle, gfp_t gfp); ++#define pci_alloc_consistent(DEV,SZ,HANDLE) \ ++ __pci_alloc_consistent(DEV,SZ,HANDLE,GFP_ATOMIC) + + /* Free and unmap a consistent DMA buffer. + * cpu_addr is what was returned from pci_alloc_consistent, -- 2.47.2