Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 11 Sep 2013 15:36:03 +0000 (08:36 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 11 Sep 2013 15:36:03 +0000 (08:36 -0700)
Pull more s390 updates from Heiko Carstens:
 "This includes one bpf/jit bug fix where the jit compiler could
  sometimes write generated code out of bounds of the allocated memory
  area.

  The rest of the patches are only cleanups and minor improvements"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/irq: reduce size of external interrupt handler hash array
  s390/compat,uid16: use current_cred()
  s390/ap_bus: use and-mask instead of a cast
  s390/ftrace: avoid pointer arithmetics with function pointers
  s390: make various functions static, add declarations to header files
  s390/compat signal: add couple of __force annotations
  s390/mm: add __releases()/__acquires() annotations to gmap_alloc_table()
  s390: keep Kconfig sorted
  s390/irq: rework irq subclass handling
  s390/irq: use hlists for external interrupt handler array
  s390/dumpstack: convert print_symbol to %pSR
  s390/perf: Remove print_hex_dump_bytes() debug output
  s390: update defconfig
  s390/bpf,jit: fix address randomization

1  2 
arch/s390/Kconfig
arch/s390/mm/pgtable.c

diff --combined arch/s390/Kconfig
index c696ad7d3439d55e0d763caa14325a5b2447edbc,665c8a20365121cb2e3a945b4b863d96d36458aa..fb2723e8ba65d3af88d82bca4f14d52898a87b90
@@@ -62,6 -62,7 +62,7 @@@ config S39
        def_bool y
        select ARCH_DISCARD_MEMBLOCK
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+       select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select ARCH_INLINE_READ_LOCK
        select ARCH_INLINE_READ_LOCK_BH
@@@ -91,7 -92,6 +92,6 @@@
        select ARCH_INLINE_WRITE_UNLOCK_BH
        select ARCH_INLINE_WRITE_UNLOCK_IRQ
        select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
-       select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
        select ARCH_SAVE_PAGE_KEYS if HIBERNATION
        select ARCH_WANT_IPC_PARSE_VERSION
        select BUILDTIME_EXTABLE_SORT
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_UID16 if 32BIT
        select HAVE_VIRT_CPU_ACCOUNTING
-       select VIRT_TO_BUS
        select INIT_ALL_POSSIBLE
        select KTIME_SCALAR if 32BIT
        select MODULES_USE_ELF_RELA
-       select OLD_SIGSUSPEND3
        select OLD_SIGACTION
+       select OLD_SIGSUSPEND3
        select SYSCTL_EXCEPTION_TRACE
        select USE_GENERIC_SMP_HELPERS if SMP
        select VIRT_CPU_ACCOUNTING
+       select VIRT_TO_BUS
  
  config SCHED_OMIT_FRAME_POINTER
        def_bool y
@@@ -431,6 -431,7 +431,6 @@@ menuconfig PC
        bool "PCI support"
        default n
        depends on 64BIT
 -      select ARCH_SUPPORTS_MSI
        select PCI_MSI
        help
          Enable PCI support.
diff --combined arch/s390/mm/pgtable.c
index bf7c0dc64a76111d307b7b48585cc40ecb6741aa,4645fafa42c7bedef7631765ad8fde2c01c829cd..de8cbc30dcd1be13cb34dd0fc0e7ad5a8a68372e
@@@ -245,7 -245,9 +245,9 @@@ EXPORT_SYMBOL_GPL(gmap_disable)
   * gmap_alloc_table is assumed to be called with mmap_sem held
   */
  static int gmap_alloc_table(struct gmap *gmap,
-                              unsigned long *table, unsigned long init)
+                           unsigned long *table, unsigned long init)
+       __releases(&gmap->mm->page_table_lock)
+       __acquires(&gmap->mm->page_table_lock)
  {
        struct page *page;
        unsigned long *new;
@@@ -335,7 -337,7 +337,7 @@@ int gmap_map_segment(struct gmap *gmap
  
        if ((from | to | len) & (PMD_SIZE - 1))
                return -EINVAL;
 -      if (len == 0 || from + len > PGDIR_SIZE ||
 +      if (len == 0 || from + len > TASK_MAX_SIZE ||
            from + len < from || to + len < to)
                return -EINVAL;
  
@@@ -732,11 -734,6 +734,11 @@@ void gmap_do_ipte_notify(struct mm_stru
        spin_unlock(&gmap_notifier_lock);
  }
  
 +static inline int page_table_with_pgste(struct page *page)
 +{
 +      return atomic_read(&page->_mapcount) == 0;
 +}
 +
  static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
                                                    unsigned long vmaddr)
  {
        mp->vmaddr = vmaddr & PMD_MASK;
        INIT_LIST_HEAD(&mp->mapper);
        page->index = (unsigned long) mp;
 -      atomic_set(&page->_mapcount, 3);
 +      atomic_set(&page->_mapcount, 0);
        table = (unsigned long *) page_to_phys(page);
        clear_table(table, _PAGE_INVALID, PAGE_SIZE/2);
        clear_table(table + PTRS_PER_PTE, PGSTE_HR_BIT | PGSTE_HC_BIT,
@@@ -823,11 -820,6 +825,11 @@@ EXPORT_SYMBOL(set_guest_storage_key)
  
  #else /* CONFIG_PGSTE */
  
 +static inline int page_table_with_pgste(struct page *page)
 +{
 +      return 0;
 +}
 +
  static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
                                                    unsigned long vmaddr)
  {
@@@ -904,12 -896,12 +906,12 @@@ void page_table_free(struct mm_struct *
        struct page *page;
        unsigned int bit, mask;
  
 -      if (mm_has_pgste(mm)) {
 +      page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
 +      if (page_table_with_pgste(page)) {
                gmap_disconnect_pgtable(mm, table);
                return page_table_free_pgste(table);
        }
        /* Free 1K/2K page table fragment of a 4K page */
 -      page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
        bit = 1 << ((__pa(table) & ~PAGE_MASK)/(PTRS_PER_PTE*sizeof(pte_t)));
        spin_lock_bh(&mm->context.list_lock);
        if ((atomic_read(&page->_mapcount) & FRAG_MASK) != FRAG_MASK)
@@@ -947,14 -939,14 +949,14 @@@ void page_table_free_rcu(struct mmu_gat
        unsigned int bit, mask;
  
        mm = tlb->mm;
 -      if (mm_has_pgste(mm)) {
 +      page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
 +      if (page_table_with_pgste(page)) {
                gmap_disconnect_pgtable(mm, table);
                table = (unsigned long *) (__pa(table) | FRAG_MASK);
                tlb_remove_table(tlb, table);
                return;
        }
        bit = 1 << ((__pa(table) & ~PAGE_MASK) / (PTRS_PER_PTE*sizeof(pte_t)));
 -      page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
        spin_lock_bh(&mm->context.list_lock);
        if ((atomic_read(&page->_mapcount) & FRAG_MASK) != FRAG_MASK)
                list_del(&page->lru);
        tlb_remove_table(tlb, table);
  }
  
- void __tlb_remove_table(void *_table)
static void __tlb_remove_table(void *_table)
  {
        const unsigned long mask = (FRAG_MASK << 4) | FRAG_MASK;
        void *table = (void *)((unsigned long) _table & ~mask);
@@@ -1040,120 -1032,36 +1042,120 @@@ void tlb_remove_table(struct mmu_gathe
  }
  
  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 -void thp_split_vma(struct vm_area_struct *vma)
 +static inline void thp_split_vma(struct vm_area_struct *vma)
  {
        unsigned long addr;
 -      struct page *page;
  
 -      for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
 -              page = follow_page(vma, addr, FOLL_SPLIT);
 -      }
 +      for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE)
 +              follow_page(vma, addr, FOLL_SPLIT);
  }
  
 -void thp_split_mm(struct mm_struct *mm)
 +static inline void thp_split_mm(struct mm_struct *mm)
  {
 -      struct vm_area_struct *vma = mm->mmap;
 +      struct vm_area_struct *vma;
  
 -      while (vma != NULL) {
 +      for (vma = mm->mmap; vma != NULL; vma = vma->vm_next) {
                thp_split_vma(vma);
                vma->vm_flags &= ~VM_HUGEPAGE;
                vma->vm_flags |= VM_NOHUGEPAGE;
 -              vma = vma->vm_next;
        }
 +      mm->def_flags |= VM_NOHUGEPAGE;
 +}
 +#else
 +static inline void thp_split_mm(struct mm_struct *mm)
 +{
  }
  #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
  
 +static unsigned long page_table_realloc_pmd(struct mmu_gather *tlb,
 +                              struct mm_struct *mm, pud_t *pud,
 +                              unsigned long addr, unsigned long end)
 +{
 +      unsigned long next, *table, *new;
 +      struct page *page;
 +      pmd_t *pmd;
 +
 +      pmd = pmd_offset(pud, addr);
 +      do {
 +              next = pmd_addr_end(addr, end);
 +again:
 +              if (pmd_none_or_clear_bad(pmd))
 +                      continue;
 +              table = (unsigned long *) pmd_deref(*pmd);
 +              page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
 +              if (page_table_with_pgste(page))
 +                      continue;
 +              /* Allocate new page table with pgstes */
 +              new = page_table_alloc_pgste(mm, addr);
 +              if (!new) {
 +                      mm->context.has_pgste = 0;
 +                      continue;
 +              }
 +              spin_lock(&mm->page_table_lock);
 +              if (likely((unsigned long *) pmd_deref(*pmd) == table)) {
 +                      /* Nuke pmd entry pointing to the "short" page table */
 +                      pmdp_flush_lazy(mm, addr, pmd);
 +                      pmd_clear(pmd);
 +                      /* Copy ptes from old table to new table */
 +                      memcpy(new, table, PAGE_SIZE/2);
 +                      clear_table(table, _PAGE_INVALID, PAGE_SIZE/2);
 +                      /* Establish new table */
 +                      pmd_populate(mm, pmd, (pte_t *) new);
 +                      /* Free old table with rcu, there might be a walker! */
 +                      page_table_free_rcu(tlb, table);
 +                      new = NULL;
 +              }
 +              spin_unlock(&mm->page_table_lock);
 +              if (new) {
 +                      page_table_free_pgste(new);
 +                      goto again;
 +              }
 +      } while (pmd++, addr = next, addr != end);
 +
 +      return addr;
 +}
 +
 +static unsigned long page_table_realloc_pud(struct mmu_gather *tlb,
 +                                 struct mm_struct *mm, pgd_t *pgd,
 +                                 unsigned long addr, unsigned long end)
 +{
 +      unsigned long next;
 +      pud_t *pud;
 +
 +      pud = pud_offset(pgd, addr);
 +      do {
 +              next = pud_addr_end(addr, end);
 +              if (pud_none_or_clear_bad(pud))
 +                      continue;
 +              next = page_table_realloc_pmd(tlb, mm, pud, addr, next);
 +      } while (pud++, addr = next, addr != end);
 +
 +      return addr;
 +}
 +
 +static void page_table_realloc(struct mmu_gather *tlb, struct mm_struct *mm,
 +                             unsigned long addr, unsigned long end)
 +{
 +      unsigned long next;
 +      pgd_t *pgd;
 +
 +      pgd = pgd_offset(mm, addr);
 +      do {
 +              next = pgd_addr_end(addr, end);
 +              if (pgd_none_or_clear_bad(pgd))
 +                      continue;
 +              next = page_table_realloc_pud(tlb, mm, pgd, addr, next);
 +      } while (pgd++, addr = next, addr != end);
 +}
 +
  /*
   * switch on pgstes for its userspace process (for kvm)
   */
  int s390_enable_sie(void)
  {
        struct task_struct *tsk = current;
 -      struct mm_struct *mm, *old_mm;
 +      struct mm_struct *mm = tsk->mm;
 +      struct mmu_gather tlb;
  
        /* Do we have switched amode? If no, we cannot do sie */
        if (s390_user_mode == HOME_SPACE_MODE)
        if (mm_has_pgste(tsk->mm))
                return 0;
  
 -      /* lets check if we are allowed to replace the mm */
 -      task_lock(tsk);
 -      if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 ||
 -#ifdef CONFIG_AIO
 -          !hlist_empty(&tsk->mm->ioctx_list) ||
 -#endif
 -          tsk->mm != tsk->active_mm) {
 -              task_unlock(tsk);
 -              return -EINVAL;
 -      }
 -      task_unlock(tsk);
 -
 -      /* we copy the mm and let dup_mm create the page tables with_pgstes */
 -      tsk->mm->context.alloc_pgste = 1;
 -      /* make sure that both mms have a correct rss state */
 -      sync_mm_rss(tsk->mm);
 -      mm = dup_mm(tsk);
 -      tsk->mm->context.alloc_pgste = 0;
 -      if (!mm)
 -              return -ENOMEM;
 -
 -#ifdef CONFIG_TRANSPARENT_HUGEPAGE
 +      down_write(&mm->mmap_sem);
        /* split thp mappings and disable thp for future mappings */
        thp_split_mm(mm);
 -      mm->def_flags |= VM_NOHUGEPAGE;
 -#endif
 -
 -      /* Now lets check again if something happened */
 -      task_lock(tsk);
 -      if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 ||
 -#ifdef CONFIG_AIO
 -          !hlist_empty(&tsk->mm->ioctx_list) ||
 -#endif
 -          tsk->mm != tsk->active_mm) {
 -              mmput(mm);
 -              task_unlock(tsk);
 -              return -EINVAL;
 -      }
 -
 -      /* ok, we are alone. No ptrace, no threads, etc. */
 -      old_mm = tsk->mm;
 -      tsk->mm = tsk->active_mm = mm;
 -      preempt_disable();
 -      update_mm(mm, tsk);
 -      atomic_inc(&mm->context.attach_count);
 -      atomic_dec(&old_mm->context.attach_count);
 -      cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
 -      preempt_enable();
 -      task_unlock(tsk);
 -      mmput(old_mm);
 -      return 0;
 +      /* Reallocate the page tables with pgstes */
 +      mm->context.has_pgste = 1;
 +      tlb_gather_mmu(&tlb, mm, 0, TASK_SIZE);
 +      page_table_realloc(&tlb, mm, 0, TASK_SIZE);
 +      tlb_finish_mmu(&tlb, 0, TASK_SIZE);
 +      up_write(&mm->mmap_sem);
 +      return mm->context.has_pgste ? 0 : -ENOMEM;
  }
  EXPORT_SYMBOL_GPL(s390_enable_sie);