aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCarsten Otte <cotte@de.ibm.com>2011-10-30 10:17:02 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-10-30 10:16:45 -0400
commit499069e1a421e2a85e76846c3237f00f1a5cb435 (patch)
tree01cf74636909b06b5a986f97a90bf0af5040212d
parentcc772456ac9b460693492b3a3d89e8c81eda5874 (diff)
[S390] take mmap_sem when walking guest page table
gmap_fault needs to walk the guest page table. However, parts of that may change if some other thread does munmap. In that case gmap_unmap_notifier will also unmap the corresponding parts from the guest page table. We need to take mmap_sem in order to serialize these operations. do_exception now calls __gmap_fault with mmap_sem held which does not get exported to modules. The exported function, which is called from KVM, now takes mmap_sem. Reported-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Carsten Otte <cotte@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--arch/s390/include/asm/pgtable.h1
-rw-r--r--arch/s390/mm/fault.c2
-rw-r--r--arch/s390/mm/pgtable.c15
3 files changed, 16 insertions, 2 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index c0cb794bb365..bc5f520f6f87 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -696,6 +696,7 @@ void gmap_disable(struct gmap *gmap);
696int gmap_map_segment(struct gmap *gmap, unsigned long from, 696int gmap_map_segment(struct gmap *gmap, unsigned long from,
697 unsigned long to, unsigned long length); 697 unsigned long to, unsigned long length);
698int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len); 698int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len);
699unsigned long __gmap_fault(unsigned long address, struct gmap *);
699unsigned long gmap_fault(unsigned long address, struct gmap *); 700unsigned long gmap_fault(unsigned long address, struct gmap *);
700 701
701/* 702/*
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index de3af0c053c0..1766def5bc3f 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -307,7 +307,7 @@ static inline int do_exception(struct pt_regs *regs, int access,
307 307
308#ifdef CONFIG_PGSTE 308#ifdef CONFIG_PGSTE
309 if (test_tsk_thread_flag(current, TIF_SIE) && S390_lowcore.gmap) { 309 if (test_tsk_thread_flag(current, TIF_SIE) && S390_lowcore.gmap) {
310 address = gmap_fault(address, 310 address = __gmap_fault(address,
311 (struct gmap *) S390_lowcore.gmap); 311 (struct gmap *) S390_lowcore.gmap);
312 if (address == -EFAULT) { 312 if (address == -EFAULT) {
313 fault = VM_FAULT_BADMAP; 313 fault = VM_FAULT_BADMAP;
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 96e85ac89269..441d34445d0e 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -393,7 +393,10 @@ out_unmap:
393} 393}
394EXPORT_SYMBOL_GPL(gmap_map_segment); 394EXPORT_SYMBOL_GPL(gmap_map_segment);
395 395
396unsigned long gmap_fault(unsigned long address, struct gmap *gmap) 396/*
397 * this function is assumed to be called with mmap_sem held
398 */
399unsigned long __gmap_fault(unsigned long address, struct gmap *gmap)
397{ 400{
398 unsigned long *table, vmaddr, segment; 401 unsigned long *table, vmaddr, segment;
399 struct mm_struct *mm; 402 struct mm_struct *mm;
@@ -461,7 +464,17 @@ unsigned long gmap_fault(unsigned long address, struct gmap *gmap)
461 return vmaddr | (address & ~PMD_MASK); 464 return vmaddr | (address & ~PMD_MASK);
462 } 465 }
463 return -EFAULT; 466 return -EFAULT;
467}
468
469unsigned long gmap_fault(unsigned long address, struct gmap *gmap)
470{
471 unsigned long rc;
472
473 down_read(&gmap->mm->mmap_sem);
474 rc = __gmap_fault(address, gmap);
475 up_read(&gmap->mm->mmap_sem);
464 476
477 return rc;
465} 478}
466EXPORT_SYMBOL_GPL(gmap_fault); 479EXPORT_SYMBOL_GPL(gmap_fault);
467 480