]> git.hungrycats.org Git - linux/commitdiff
[PATCH] atomic copy_*_user infrastructure
authorAndrew Morton <akpm@digeo.com>
Sun, 8 Sep 2002 05:22:03 +0000 (22:22 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Sun, 8 Sep 2002 05:22:03 +0000 (22:22 -0700)
The patch implements the atomic copy_*_user() function.

If the kernel takes a pagefault while running copy_*_user() in an
atomic region, the copy_*_user() will fail (return a short value).

And with this patch, holding an atomic kmap() puts the CPU into an
atomic region.

- Increment preempt_count() in kmap_atomic() regardless of the
  setting of CONFIG_PREEMPT.  The pagefault handler recognises this as
  an atomic region and refuses to service the fault.  copy_*_user will
  return a non-zero value.

- Attempts to propagate the in_atomic() predicate to all the other
  highmem-capable architectures' pagefault handlers.  But the code is
  only tested on x86.

- Fixed a PPC bug in kunmap_atomic(): it forgot to reenable
  preemption if HIGHMEM_DEBUG is turned on.

- Fixed a sparc bug in kunmap_atomic(): it forgot to reenable
  preemption all the time, for non-fixmap pages.

- Fix an error in <linux/highmem.h> - in the CONFIG_HIGHMEM=n case,
  kunmap_atomic() takes an address, not a page *.

arch/ppc/mm/fault.c
arch/sparc/mm/fault.c
include/asm-i386/highmem.h
include/asm-ppc/hardirq.h
include/asm-ppc/highmem.h
include/asm-sparc/hardirq.h
include/asm-sparc/highmem.h
include/linux/highmem.h

index 1d7c7eb5dcf48f060c2775e8001d2710256d942a..65ed61f1344c04ab63023856c146ff808dde4827 100644 (file)
@@ -102,7 +102,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
 #endif /* !CONFIG_4xx */
 #endif /* CONFIG_XMON || CONFIG_KGDB */
 
-       if (in_interrupt() || mm == NULL) {
+       if (in_atomic() || mm == NULL) {
                bad_page_fault(regs, address, SIGSEGV);
                return;
        }
index 49eccf61be1d26c0400c634c793f5053bc6839ec..e07d4d1017a9d1be5c3479c64dd1d7c10d40b351 100644 (file)
@@ -233,7 +233,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
         * If we're in an interrupt or have no user
         * context, we must not take the fault..
         */
-        if (in_interrupt() || !mm)
+        if (in_atomic() || !mm)
                 goto no_context;
 
        down_read(&mm->mmap_sem);
index 1cba7fc4588288cb0f8cb6b617d68b71944c2d50..0316b53f868f55d77f8c9c7bcd9d8171de75de04 100644 (file)
@@ -81,7 +81,7 @@ static inline void *kmap_atomic(struct page *page, enum km_type type)
        enum fixed_addresses idx;
        unsigned long vaddr;
 
-       preempt_disable();
+       inc_preempt_count();
        if (page < highmem_start_page)
                return page_address(page);
 
@@ -104,7 +104,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
        enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
 
        if (vaddr < FIXADDR_START) { // FIXME
-               preempt_enable();
+               dec_preempt_count();
                return;
        }
 
@@ -119,7 +119,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
        __flush_tlb_one(vaddr);
 #endif
 
-       preempt_enable();
+       dec_preempt_count();
 }
 
 #endif /* __KERNEL__ */
index d56152a03ccca8132f3cb27f625c15e018e18227..547f2491000f5f73d4880e2cb5c7f44ab2cfe9a9 100644 (file)
@@ -85,8 +85,10 @@ typedef struct {
 #define irq_enter()            (preempt_count() += HARDIRQ_OFFSET)
 
 #if CONFIG_PREEMPT
+# define in_atomic()   (preempt_count() != kernel_locked())
 # define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1)
 #else
+# define in_atomic()   (preempt_count() != 0)
 # define IRQ_EXIT_OFFSET HARDIRQ_OFFSET
 #endif
 #define irq_exit()                                                     \
index 5a630083d0149342c1d45c59bf1f00c203243a17..472482ca3f367861ae2108353dbb649a293f0cc7 100644 (file)
@@ -88,6 +88,7 @@ static inline void *kmap_atomic(struct page *page, enum km_type type)
        unsigned int idx;
        unsigned long vaddr;
 
+       inc_preempt_count();
        if (page < highmem_start_page)
                return page_address(page);
 
@@ -109,8 +110,10 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
        unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
        unsigned int idx = type + KM_TYPE_NR*smp_processor_id();
 
-       if (vaddr < KMAP_FIX_BEGIN) // FIXME
+       if (vaddr < KMAP_FIX_BEGIN) { // FIXME
+               dec_preempt_count();
                return;
+       }
 
        if (vaddr != KMAP_FIX_BEGIN + idx * PAGE_SIZE)
                BUG();
@@ -122,6 +125,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
        pte_clear(kmap_pte+idx);
        flush_tlb_page(0, vaddr);
 #endif
+       dec_preempt_count();
 }
 
 #endif /* __KERNEL__ */
index a80212dc3a2aa8b6d8389e84d2adf6f1d6ef7e4d..f77ee7e415cf2a6d636dc4db6d3b3c64ea78ed38 100644 (file)
@@ -113,6 +113,12 @@ do {                                                                    \
 #define irq_exit()             br_read_unlock(BR_GLOBALIRQ_LOCK)
 #endif
 
+#if CONFIG_PREEMPT
+# define in_atomic()   (preempt_count() != kernel_locked())
+#else
+# define in_atomic()   (preempt_count() != 0)
+#endif
+
 #ifndef CONFIG_SMP
 
 #define synchronize_irq()      barrier()
index bb2fc2331b5bd7cfd7fc10f13161d2d23b1e7595..2ba438ea6111d58da99be71b9bbed36e654ff2fb 100644 (file)
@@ -83,6 +83,7 @@ static inline void *kmap_atomic(struct page *page, enum km_type type)
        unsigned long idx;
        unsigned long vaddr;
 
+       inc_preempt_count();
        if (page < highmem_start_page)
                return page_address(page);
 
@@ -116,8 +117,10 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
        unsigned long vaddr = (unsigned long) kvaddr;
        unsigned long idx = type + KM_TYPE_NR*smp_processor_id();
 
-       if (vaddr < FIX_KMAP_BEGIN) // FIXME
+       if (vaddr < FIX_KMAP_BEGIN) { // FIXME
+               dec_preempt_count();
                return;
+       }
 
        if (vaddr != FIX_KMAP_BEGIN + idx * PAGE_SIZE)
                BUG();
@@ -142,6 +145,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
        flush_tlb_all();
 #endif
 #endif
+       dec_preempt_count();
 }
 
 #endif /* __KERNEL__ */
index b389a75be5d7470e5ec7847142b6a4ad787a2a53..37017703731501769d7903696fa25019c2a108c0 100644 (file)
@@ -24,8 +24,8 @@ static inline void *kmap(struct page *page) { return page_address(page); }
 
 #define kunmap(page) do { (void) (page); } while (0)
 
-#define kmap_atomic(page,idx)          kmap(page)
-#define kunmap_atomic(page,idx)                kunmap(page)
+#define kmap_atomic(page, idx)         page_address(page)
+#define kunmap_atomic(addr, idx)       do { } while (0)
 
 #endif /* CONFIG_HIGHMEM */