Merge branch 'akpm' (patches from Andrew)
Merge misc fixes from Andrew Morton: "7 fixes" * emailed patches from Andrew Morton <akpm@linux-foundation.org>: arch/Kconfig: update HAVE_RELIABLE_STACKTRACE description mm, hotplug: fix page online with DEBUG_PAGEALLOC compiled but not enabled mm/z3fold.c: do not include rwlock.h directly fat: fix uninit-memory access for partial initialized inode mm: avoid data corruption on CoW fault into PFN-mapped VMA mm: fix possible PMD dirty bit lost in set_pmd_migration_entry() mm, numa: fix bad pmd by atomically check for pmd_trans_huge when marking page tables prot_numa
This commit is contained in:
Коммит
aeb542a1b5
|
@ -738,8 +738,9 @@ config HAVE_STACK_VALIDATION
|
||||||
config HAVE_RELIABLE_STACKTRACE
|
config HAVE_RELIABLE_STACKTRACE
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
Architecture has a save_stack_trace_tsk_reliable() function which
|
Architecture has either save_stack_trace_tsk_reliable() or
|
||||||
only returns a stack trace if it can guarantee the trace is reliable.
|
arch_stack_walk_reliable() function which only returns a stack trace
|
||||||
|
if it can guarantee the trace is reliable.
|
||||||
|
|
||||||
config HAVE_ARCH_HASH
|
config HAVE_ARCH_HASH
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -750,6 +750,13 @@ static struct inode *fat_alloc_inode(struct super_block *sb)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
init_rwsem(&ei->truncate_lock);
|
init_rwsem(&ei->truncate_lock);
|
||||||
|
/* Zeroing to allow iput() even if partial initialized inode. */
|
||||||
|
ei->mmu_private = 0;
|
||||||
|
ei->i_start = 0;
|
||||||
|
ei->i_logstart = 0;
|
||||||
|
ei->i_attrs = 0;
|
||||||
|
ei->i_pos = 0;
|
||||||
|
|
||||||
return &ei->vfs_inode;
|
return &ei->vfs_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1374,16 +1381,6 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fat_dummy_inode_init(struct inode *inode)
|
|
||||||
{
|
|
||||||
/* Initialize this dummy inode to work as no-op. */
|
|
||||||
MSDOS_I(inode)->mmu_private = 0;
|
|
||||||
MSDOS_I(inode)->i_start = 0;
|
|
||||||
MSDOS_I(inode)->i_logstart = 0;
|
|
||||||
MSDOS_I(inode)->i_attrs = 0;
|
|
||||||
MSDOS_I(inode)->i_pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fat_read_root(struct inode *inode)
|
static int fat_read_root(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||||
|
@ -1844,13 +1841,11 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
|
||||||
fat_inode = new_inode(sb);
|
fat_inode = new_inode(sb);
|
||||||
if (!fat_inode)
|
if (!fat_inode)
|
||||||
goto out_fail;
|
goto out_fail;
|
||||||
fat_dummy_inode_init(fat_inode);
|
|
||||||
sbi->fat_inode = fat_inode;
|
sbi->fat_inode = fat_inode;
|
||||||
|
|
||||||
fsinfo_inode = new_inode(sb);
|
fsinfo_inode = new_inode(sb);
|
||||||
if (!fsinfo_inode)
|
if (!fsinfo_inode)
|
||||||
goto out_fail;
|
goto out_fail;
|
||||||
fat_dummy_inode_init(fsinfo_inode);
|
|
||||||
fsinfo_inode->i_ino = MSDOS_FSINFO_INO;
|
fsinfo_inode->i_ino = MSDOS_FSINFO_INO;
|
||||||
sbi->fsinfo_inode = fsinfo_inode;
|
sbi->fsinfo_inode = fsinfo_inode;
|
||||||
insert_inode_hash(fsinfo_inode);
|
insert_inode_hash(fsinfo_inode);
|
||||||
|
|
|
@ -2715,6 +2715,10 @@ static inline bool debug_pagealloc_enabled_static(void)
|
||||||
#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_ARCH_HAS_SET_DIRECT_MAP)
|
#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_ARCH_HAS_SET_DIRECT_MAP)
|
||||||
extern void __kernel_map_pages(struct page *page, int numpages, int enable);
|
extern void __kernel_map_pages(struct page *page, int numpages, int enable);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When called in DEBUG_PAGEALLOC context, the call should most likely be
|
||||||
|
* guarded by debug_pagealloc_enabled() or debug_pagealloc_enabled_static()
|
||||||
|
*/
|
||||||
static inline void
|
static inline void
|
||||||
kernel_map_pages(struct page *page, int numpages, int enable)
|
kernel_map_pages(struct page *page, int numpages, int enable)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3043,8 +3043,7 @@ void set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
flush_cache_range(vma, address, address + HPAGE_PMD_SIZE);
|
flush_cache_range(vma, address, address + HPAGE_PMD_SIZE);
|
||||||
pmdval = *pvmw->pmd;
|
pmdval = pmdp_invalidate(vma, address, pvmw->pmd);
|
||||||
pmdp_invalidate(vma, address, pvmw->pmd);
|
|
||||||
if (pmd_dirty(pmdval))
|
if (pmd_dirty(pmdval))
|
||||||
set_page_dirty(page);
|
set_page_dirty(page);
|
||||||
entry = make_migration_entry(page, pmd_write(pmdval));
|
entry = make_migration_entry(page, pmd_write(pmdval));
|
||||||
|
|
35
mm/memory.c
35
mm/memory.c
|
@ -2257,7 +2257,7 @@ static inline bool cow_user_page(struct page *dst, struct page *src,
|
||||||
bool ret;
|
bool ret;
|
||||||
void *kaddr;
|
void *kaddr;
|
||||||
void __user *uaddr;
|
void __user *uaddr;
|
||||||
bool force_mkyoung;
|
bool locked = false;
|
||||||
struct vm_area_struct *vma = vmf->vma;
|
struct vm_area_struct *vma = vmf->vma;
|
||||||
struct mm_struct *mm = vma->vm_mm;
|
struct mm_struct *mm = vma->vm_mm;
|
||||||
unsigned long addr = vmf->address;
|
unsigned long addr = vmf->address;
|
||||||
|
@ -2282,11 +2282,11 @@ static inline bool cow_user_page(struct page *dst, struct page *src,
|
||||||
* On architectures with software "accessed" bits, we would
|
* On architectures with software "accessed" bits, we would
|
||||||
* take a double page fault, so mark it accessed here.
|
* take a double page fault, so mark it accessed here.
|
||||||
*/
|
*/
|
||||||
force_mkyoung = arch_faults_on_old_pte() && !pte_young(vmf->orig_pte);
|
if (arch_faults_on_old_pte() && !pte_young(vmf->orig_pte)) {
|
||||||
if (force_mkyoung) {
|
|
||||||
pte_t entry;
|
pte_t entry;
|
||||||
|
|
||||||
vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl);
|
vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl);
|
||||||
|
locked = true;
|
||||||
if (!likely(pte_same(*vmf->pte, vmf->orig_pte))) {
|
if (!likely(pte_same(*vmf->pte, vmf->orig_pte))) {
|
||||||
/*
|
/*
|
||||||
* Other thread has already handled the fault
|
* Other thread has already handled the fault
|
||||||
|
@ -2310,18 +2310,37 @@ static inline bool cow_user_page(struct page *dst, struct page *src,
|
||||||
* zeroes.
|
* zeroes.
|
||||||
*/
|
*/
|
||||||
if (__copy_from_user_inatomic(kaddr, uaddr, PAGE_SIZE)) {
|
if (__copy_from_user_inatomic(kaddr, uaddr, PAGE_SIZE)) {
|
||||||
|
if (locked)
|
||||||
|
goto warn;
|
||||||
|
|
||||||
|
/* Re-validate under PTL if the page is still mapped */
|
||||||
|
vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl);
|
||||||
|
locked = true;
|
||||||
|
if (!likely(pte_same(*vmf->pte, vmf->orig_pte))) {
|
||||||
|
/* The PTE changed under us. Retry page fault. */
|
||||||
|
ret = false;
|
||||||
|
goto pte_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Give a warn in case there can be some obscure
|
* The same page can be mapped back since last copy attampt.
|
||||||
* use-case
|
* Try to copy again under PTL.
|
||||||
*/
|
*/
|
||||||
WARN_ON_ONCE(1);
|
if (__copy_from_user_inatomic(kaddr, uaddr, PAGE_SIZE)) {
|
||||||
clear_page(kaddr);
|
/*
|
||||||
|
* Give a warn in case there can be some obscure
|
||||||
|
* use-case
|
||||||
|
*/
|
||||||
|
warn:
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
clear_page(kaddr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
||||||
pte_unlock:
|
pte_unlock:
|
||||||
if (force_mkyoung)
|
if (locked)
|
||||||
pte_unmap_unlock(vmf->pte, vmf->ptl);
|
pte_unmap_unlock(vmf->pte, vmf->ptl);
|
||||||
kunmap_atomic(kaddr);
|
kunmap_atomic(kaddr);
|
||||||
flush_dcache_page(dst);
|
flush_dcache_page(dst);
|
||||||
|
|
|
@ -574,7 +574,13 @@ EXPORT_SYMBOL_GPL(restore_online_page_callback);
|
||||||
|
|
||||||
void generic_online_page(struct page *page, unsigned int order)
|
void generic_online_page(struct page *page, unsigned int order)
|
||||||
{
|
{
|
||||||
kernel_map_pages(page, 1 << order, 1);
|
/*
|
||||||
|
* Freeing the page with debug_pagealloc enabled will try to unmap it,
|
||||||
|
* so we should map it first. This is better than introducing a special
|
||||||
|
* case in page freeing fast path.
|
||||||
|
*/
|
||||||
|
if (debug_pagealloc_enabled_static())
|
||||||
|
kernel_map_pages(page, 1 << order, 1);
|
||||||
__free_pages_core(page, order);
|
__free_pages_core(page, order);
|
||||||
totalram_pages_add(1UL << order);
|
totalram_pages_add(1UL << order);
|
||||||
#ifdef CONFIG_HIGHMEM
|
#ifdef CONFIG_HIGHMEM
|
||||||
|
|
|
@ -161,6 +161,31 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used when setting automatic NUMA hinting protection where it is
|
||||||
|
* critical that a numa hinting PMD is not confused with a bad PMD.
|
||||||
|
*/
|
||||||
|
static inline int pmd_none_or_clear_bad_unless_trans_huge(pmd_t *pmd)
|
||||||
|
{
|
||||||
|
pmd_t pmdval = pmd_read_atomic(pmd);
|
||||||
|
|
||||||
|
/* See pmd_none_or_trans_huge_or_clear_bad for info on barrier */
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
barrier();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (pmd_none(pmdval))
|
||||||
|
return 1;
|
||||||
|
if (pmd_trans_huge(pmdval))
|
||||||
|
return 0;
|
||||||
|
if (unlikely(pmd_bad(pmdval))) {
|
||||||
|
pmd_clear_bad(pmd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
|
static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
|
||||||
pud_t *pud, unsigned long addr, unsigned long end,
|
pud_t *pud, unsigned long addr, unsigned long end,
|
||||||
pgprot_t newprot, int dirty_accountable, int prot_numa)
|
pgprot_t newprot, int dirty_accountable, int prot_numa)
|
||||||
|
@ -178,8 +203,17 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
|
||||||
unsigned long this_pages;
|
unsigned long this_pages;
|
||||||
|
|
||||||
next = pmd_addr_end(addr, end);
|
next = pmd_addr_end(addr, end);
|
||||||
if (!is_swap_pmd(*pmd) && !pmd_trans_huge(*pmd) && !pmd_devmap(*pmd)
|
|
||||||
&& pmd_none_or_clear_bad(pmd))
|
/*
|
||||||
|
* Automatic NUMA balancing walks the tables with mmap_sem
|
||||||
|
* held for read. It's possible a parallel update to occur
|
||||||
|
* between pmd_trans_huge() and a pmd_none_or_clear_bad()
|
||||||
|
* check leading to a false positive and clearing.
|
||||||
|
* Hence, it's necessary to atomically read the PMD value
|
||||||
|
* for all the checks.
|
||||||
|
*/
|
||||||
|
if (!is_swap_pmd(*pmd) && !pmd_devmap(*pmd) &&
|
||||||
|
pmd_none_or_clear_bad_unless_trans_huge(pmd))
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
/* invoke the mmu notifier if the pmd is populated */
|
/* invoke the mmu notifier if the pmd is populated */
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rwlock.h>
|
|
||||||
#include <linux/zpool.h>
|
#include <linux/zpool.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче