Hi,

consistent_sync_page() is currently not highmem safe on
non coherent platforms.  PPC44x, for example, allows very
large system memory (2GB).  The following patch addresses
this by kmap/kunmapping each segment of the buffer before
calling consistent_sync().  It is interrupt safe since the
streaming DMA API callers can be called from an interrupt
context.

The code is only used on non-coherent systems and only if
highmem support is enabled.

Comments?

-Matt

===== include/asm-ppc/kmap_types.h 1.14 vs edited =====
--- 1.14/include/asm-ppc/kmap_types.h   Thu Feb  6 13:06:42 2003
+++ edited/include/asm-ppc/kmap_types.h Thu Nov 13 14:04:51 2003
@@ -16,6 +16,7 @@
        KM_IRQ1,
        KM_SOFTIRQ0,
        KM_SOFTIRQ1,
+       KM_PPC_SYNC_PAGE,
        KM_TYPE_NR
 };

===== arch/ppc/mm/cachemap.c 1.15 vs edited =====
--- 1.15/arch/ppc/mm/cachemap.c Sun Oct  5 18:25:15 2003
+++ edited/arch/ppc/mm/cachemap.c       Thu Nov 13 14:19:59 2003
@@ -36,6 +36,7 @@
 #include <linux/bootmem.h>
 #include <linux/highmem.h>
 #include <linux/pci.h>
+#include <linux/interrupt.h>

 #include <asm/pgalloc.h>
 #include <asm/prom.h>
@@ -157,16 +158,63 @@
        }
 }

+#ifdef CONFIG_HIGHMEM
 /*
- * consistent_sync_page make a page are consistent. identical
- * to consistent_sync, but takes a struct page instead of a virtual address
+ * consistent_sync_page() implementation for non cache coherent
+ * systems using highmem. In this case, each page of a buffer
+ * must be kmapped/kunmapped in order to have a virtual address
+ * for consistent_sync(). This must be interrupt safe so hard
+ * interrupts are disabled and kmap_atomic() are used when in an
+ * interrupt context.
  */
+static inline void __consistent_sync_page(struct page *page,
+               unsigned long offset, size_t size, int direction,
+               int in_int)
+{
+       size_t seg_size = min((size_t)PAGE_SIZE, size) - offset;
+       size_t cur_size = seg_size;
+       unsigned long start, seg_offset = offset;
+       int nr_segs = PAGE_ALIGN(size + (PAGE_SIZE - offset))/PAGE_SIZE;
+       int seg_nr = 0;
+       int flags;
+
+       if (in_int) local_irq_save(flags);
+
+       do {
+               if (in_int)
+                       start = (unsigned long)kmap_atomic(page + seg_nr, 
KM_PPC_SYNC_PAGE) + seg_offset;
+               else
+                       start = (unsigned long)kmap(page + seg_nr) + seg_offset;
+               /* Sync this buffer segment */
+               consistent_sync((void *)start, seg_size, direction);
+               if (in_int)
+                       kunmap_atomic((void *)start, KM_PPC_SYNC_PAGE);
+               else
+                       kunmap(page + seg_nr);
+               seg_nr++;
+               /* Calculate next buffer segment size */
+               seg_size = min((size_t)PAGE_SIZE, size - cur_size);
+               /* Add the segment size to our running total */
+               cur_size += seg_size;
+               seg_offset = 0;
+       } while (seg_nr < nr_segs);
+
+       if (in_int) local_irq_restore(flags);
+}
+#endif /* CONFIG_HIGHMEM */

+/*
+ * consistent_sync_page makes memory consistent. identical
+ * to consistent_sync, but takes a struct page instead of a
+ * virtual address
+ */
 void consistent_sync_page(struct page *page, unsigned long offset,
        size_t size, int direction)
 {
-       unsigned long start;
-
-       start = (unsigned long)page_address(page) + offset;
+#ifdef CONFIG_HIGHMEM
+       __consistent_sync_page(page, offset, size, direction, in_interrupt());
+#else
+       unsigned long start = (unsigned long)page_address(page) + offset;
        consistent_sync((void *)start, size, direction);
+#endif
 }

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/



Reply via email to