atomic copy_*_user() infrastructure
Subsystems (2)
PATCH 1/3 atomic copy_*_user() infrastructure
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 | 2 +- arch/sparc/mm/fault.c | 2 +- include/asm-i386/highmem.h | 6 +++--- include/asm-ppc/hardirq.h | 2 ++ include/asm-ppc/highmem.h | 6 +++++- include/asm-sparc/hardirq.h | 6 ++++++ include/asm-sparc/highmem.h | 6 +++++- include/linux/highmem.h | 4 ++-- 8 files changed, 25 insertions(+), 9 deletions(-)
include/asm-i386/highmem.h renamed from include/asm-i386/highmem.h~copy_user_atomic +3 -3
@@ -81,7 +81,7 @@ static inline void *kmap_atomic(struct penum 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 *kenum 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 *k__flush_tlb_one (vaddr);#endif - preempt_enable(); + dec_preempt_count(); }#endif /* __KERNEL__ */
include/asm-ppc/highmem.h renamed from include/asm-ppc/highmem.h~copy_user_atomic +5 -1
@@ -88,6 +88,7 @@ static inline void *kmap_atomic(struct punsigned 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 *kunsigned 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 *kpte_clear (kmap_pte+idx);flush_tlb_page (0 , vaddr);#endif + dec_preempt_count(); }#endif /* __KERNEL__ */
include/asm-sparc/highmem.h renamed from include/asm-sparc/highmem.h~copy_user_atomic +5 -1
@@ -83,6 +83,7 @@ static inline void *kmap_atomic(struct punsigned 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 *kunsigned 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 *kflush_tlb_all ();#endif #endif + dec_preempt_count(); }#endif /* __KERNEL__ */
arch/sparc/mm/fault.c renamed from arch/sparc/mm/fault.c~copy_user_atomic +1 -1
@@ -233,7 +233,7 @@ asmlinkage void do_sparc_fault(struct pt* 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);
arch/ppc/mm/fault.c renamed from arch/ppc/mm/fault.c~copy_user_atomic +1 -1
@@ -102,7 +102,7 @@ void do_page_fault(struct pt_regs *regs,#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 ; }
include/asm-sparc/hardirq.h renamed from include/asm-sparc/hardirq.h~copy_user_atomic +6 -0
@@ -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()
include/asm-ppc/hardirq.h renamed from include/asm-ppc/hardirq.h~copy_user_atomic +2 -0
@@ -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 () \
include/linux/highmem.h renamed from include/linux/highmem.h~copy_user_atomic +2 -2
@@ -24,8 +24,8 @@ static inline void *kmap(struct page *pa#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 */
.