13 hotfixes. All are for MM. 10 are cc:stable and the remaining 3
address post-7.1 issues or aren't considered suitable for backporting. There's a 3 patch series "userfaultfd: verify VMA state across UFFDIO_COPY retry" from Mike Rapoport which fixes a few uffd things. The rest are singletons - please see the individual changelogs for details. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCah5U6wAKCRDdBJ7gKXxA jmzcAP9N+WQo4qNZYYjSURqJof48Q4nght5C0ZHtsVk5itNJEQEAiecouCreqDSE VUY9mQHyEawIfORkPTUijnkjV8b+lwc= =m8J3 -----END PGP SIGNATURE----- Merge tag 'mm-hotfixes-stable-2026-06-01-20-58' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Pull MM fixes from Andrew Morton: "13 hotfixes. All are for MM. 10 are cc:stable and the remaining 3 address post-7.1 issues or aren't considered suitable for backporting. There's a three-patch series "userfaultfd: verify VMA state across UFFDIO_COPY retry" from Mike Rapoport which fixes a few uffd things. The rest are singletons - please see the individual changelogs for details" * tag 'mm-hotfixes-stable-2026-06-01-20-58' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: userfaultfd: remove redundant check in vm_uffd_ops() userfaultfd: refuse to __mfill_atomic_pte() for unsupported VMAs userfaultfd: verify VMA state across UFFDIO_COPY retry mm/huge_memory: update file PMD counter before folio_put() mm/huge_memory: update file PUD counter before folio_put() mm/hugetlb_vmemmap: fix incorrect vmemmap restore in rollback mm/damon/ops-common: call folio_test_lru() after folio_get() mm/cma: fix reserved page leak on activation failure mm/memory-failure: fix hugetlb_lock AA deadlock in get_huge_page_for_hwpoison mm/hugetlb: restore reservation on error in hugetlb folio copy paths mm/cma_debug: fix invalid accesses for inactive CMA areas memcg: use round-robin victim selection in refill_stock mm/hugetlb: avoid false positive lockdep assertionmaster
commit
4a694a77c3
|
|
@ -153,8 +153,6 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
|
||||||
long freed);
|
long freed);
|
||||||
bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list);
|
bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list);
|
||||||
int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison);
|
int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison);
|
||||||
int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
|
||||||
bool *migratable_cleared);
|
|
||||||
void folio_putback_hugetlb(struct folio *folio);
|
void folio_putback_hugetlb(struct folio *folio);
|
||||||
void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason);
|
void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason);
|
||||||
void hugetlb_fix_reserve_counts(struct inode *inode);
|
void hugetlb_fix_reserve_counts(struct inode *inode);
|
||||||
|
|
@ -421,12 +419,6 @@ static inline int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
|
||||||
bool *migratable_cleared)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void folio_putback_hugetlb(struct folio *folio)
|
static inline void folio_putback_hugetlb(struct folio *folio)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4975,8 +4975,6 @@ extern int soft_offline_page(unsigned long pfn, int flags);
|
||||||
*/
|
*/
|
||||||
extern const struct attribute_group memory_failure_attr_group;
|
extern const struct attribute_group memory_failure_attr_group;
|
||||||
extern void memory_failure_queue(unsigned long pfn, int flags);
|
extern void memory_failure_queue(unsigned long pfn, int flags);
|
||||||
extern int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
|
||||||
bool *migratable_cleared);
|
|
||||||
void num_poisoned_pages_inc(unsigned long pfn);
|
void num_poisoned_pages_inc(unsigned long pfn);
|
||||||
void num_poisoned_pages_sub(unsigned long pfn, long i);
|
void num_poisoned_pages_sub(unsigned long pfn, long i);
|
||||||
#else
|
#else
|
||||||
|
|
@ -4984,12 +4982,6 @@ static inline void memory_failure_queue(unsigned long pfn, int flags)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
|
||||||
bool *migratable_cleared)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void num_poisoned_pages_inc(unsigned long pfn)
|
static inline void num_poisoned_pages_inc(unsigned long pfn)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
mm/cma.c
7
mm/cma.c
|
|
@ -188,10 +188,13 @@ cleanup:
|
||||||
|
|
||||||
/* Expose all pages to the buddy, they are useless for CMA. */
|
/* Expose all pages to the buddy, they are useless for CMA. */
|
||||||
if (!test_bit(CMA_RESERVE_PAGES_ON_ERROR, &cma->flags)) {
|
if (!test_bit(CMA_RESERVE_PAGES_ON_ERROR, &cma->flags)) {
|
||||||
for (r = 0; r < allocrange; r++) {
|
for (r = 0; r < cma->nranges; r++) {
|
||||||
|
unsigned long start_pfn;
|
||||||
|
|
||||||
cmr = &cma->ranges[r];
|
cmr = &cma->ranges[r];
|
||||||
|
start_pfn = r <= allocrange ? early_pfn[r] : cmr->early_pfn;
|
||||||
end_pfn = cmr->base_pfn + cmr->count;
|
end_pfn = cmr->base_pfn + cmr->count;
|
||||||
for (pfn = early_pfn[r]; pfn < end_pfn; pfn++)
|
for (pfn = start_pfn; pfn < end_pfn; pfn++)
|
||||||
free_reserved_page(pfn_to_page(pfn));
|
free_reserved_page(pfn_to_page(pfn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,7 @@ static int __init cma_debugfs_init(void)
|
||||||
cma_debugfs_root = debugfs_create_dir("cma", NULL);
|
cma_debugfs_root = debugfs_create_dir("cma", NULL);
|
||||||
|
|
||||||
for (i = 0; i < cma_area_count; i++)
|
for (i = 0; i < cma_area_count; i++)
|
||||||
|
if (test_bit(CMA_ACTIVATED, &cma_areas[i].flags))
|
||||||
cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root);
|
cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,9 @@ struct folio *damon_get_folio(unsigned long pfn)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
folio = page_folio(page);
|
folio = page_folio(page);
|
||||||
if (!folio_test_lru(folio) || !folio_try_get(folio))
|
if (!folio_try_get(folio))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (unlikely(page_folio(page) != folio || !folio_test_lru(folio))) {
|
if (unlikely(page_folio(page) != folio) || !folio_test_lru(folio)) {
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
folio = NULL;
|
folio = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3015,9 +3015,9 @@ static void __split_huge_pud_locked(struct vm_area_struct *vma, pud_t *pud,
|
||||||
if (!folio_test_referenced(folio) && pud_young(old_pud))
|
if (!folio_test_referenced(folio) && pud_young(old_pud))
|
||||||
folio_set_referenced(folio);
|
folio_set_referenced(folio);
|
||||||
folio_remove_rmap_pud(folio, page, vma);
|
folio_remove_rmap_pud(folio, page, vma);
|
||||||
folio_put(folio);
|
|
||||||
add_mm_counter(vma->vm_mm, mm_counter_file(folio),
|
add_mm_counter(vma->vm_mm, mm_counter_file(folio),
|
||||||
-HPAGE_PUD_NR);
|
-HPAGE_PUD_NR);
|
||||||
|
folio_put(folio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __split_huge_pud(struct vm_area_struct *vma, pud_t *pud,
|
void __split_huge_pud(struct vm_area_struct *vma, pud_t *pud,
|
||||||
|
|
@ -3133,7 +3133,9 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
|
||||||
if (!folio_test_referenced(folio) && pmd_young(old_pmd))
|
if (!folio_test_referenced(folio) && pmd_young(old_pmd))
|
||||||
folio_set_referenced(folio);
|
folio_set_referenced(folio);
|
||||||
folio_remove_rmap_pmd(folio, page, vma);
|
folio_remove_rmap_pmd(folio, page, vma);
|
||||||
|
add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR);
|
add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
69
mm/hugetlb.c
69
mm/hugetlb.c
|
|
@ -118,6 +118,9 @@ static int hugetlb_acct_memory(struct hstate *h, long delta);
|
||||||
static void hugetlb_vma_lock_free(struct vm_area_struct *vma);
|
static void hugetlb_vma_lock_free(struct vm_area_struct *vma);
|
||||||
static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma);
|
static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma);
|
||||||
static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma);
|
static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma);
|
||||||
|
static int __huge_pmd_unshare(struct mmu_gather *tlb,
|
||||||
|
struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
|
||||||
|
bool check_locks);
|
||||||
static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
|
static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
|
||||||
unsigned long start, unsigned long end, bool take_locks);
|
unsigned long start, unsigned long end, bool take_locks);
|
||||||
static struct resv_map *vma_resv_map(struct vm_area_struct *vma);
|
static struct resv_map *vma_resv_map(struct vm_area_struct *vma);
|
||||||
|
|
@ -4974,6 +4977,7 @@ again:
|
||||||
addr, dst_vma);
|
addr, dst_vma);
|
||||||
folio_put(pte_folio);
|
folio_put(pte_folio);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
restore_reserve_on_error(h, dst_vma, addr, new_folio);
|
||||||
folio_put(new_folio);
|
folio_put(new_folio);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -6270,6 +6274,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte,
|
||||||
folio_put(*foliop);
|
folio_put(*foliop);
|
||||||
*foliop = NULL;
|
*foliop = NULL;
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
restore_reserve_on_error(h, dst_vma, dst_addr, folio);
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -6891,6 +6896,31 @@ out:
|
||||||
return pte;
|
return pte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __huge_pmd_unshare(struct mmu_gather *tlb,
|
||||||
|
struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
|
||||||
|
bool check_locks)
|
||||||
|
{
|
||||||
|
unsigned long sz = huge_page_size(hstate_vma(vma));
|
||||||
|
struct mm_struct *mm = vma->vm_mm;
|
||||||
|
pgd_t *pgd = pgd_offset(mm, addr);
|
||||||
|
p4d_t *p4d = p4d_offset(pgd, addr);
|
||||||
|
pud_t *pud = pud_offset(p4d, addr);
|
||||||
|
|
||||||
|
if (sz != PMD_SIZE)
|
||||||
|
return 0;
|
||||||
|
if (!ptdesc_pmd_is_shared(virt_to_ptdesc(ptep)))
|
||||||
|
return 0;
|
||||||
|
i_mmap_assert_write_locked(vma->vm_file->f_mapping);
|
||||||
|
if (check_locks)
|
||||||
|
hugetlb_vma_assert_locked(vma);
|
||||||
|
pud_clear(pud);
|
||||||
|
|
||||||
|
tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr);
|
||||||
|
|
||||||
|
mm_dec_nr_pmds(mm);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* huge_pmd_unshare - Unmap a pmd table if it is shared by multiple users
|
* huge_pmd_unshare - Unmap a pmd table if it is shared by multiple users
|
||||||
* @tlb: the current mmu_gather.
|
* @tlb: the current mmu_gather.
|
||||||
|
|
@ -6910,24 +6940,7 @@ out:
|
||||||
int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
||||||
unsigned long addr, pte_t *ptep)
|
unsigned long addr, pte_t *ptep)
|
||||||
{
|
{
|
||||||
unsigned long sz = huge_page_size(hstate_vma(vma));
|
return __huge_pmd_unshare(tlb, vma, addr, ptep, /*check_locks=*/true);
|
||||||
struct mm_struct *mm = vma->vm_mm;
|
|
||||||
pgd_t *pgd = pgd_offset(mm, addr);
|
|
||||||
p4d_t *p4d = p4d_offset(pgd, addr);
|
|
||||||
pud_t *pud = pud_offset(p4d, addr);
|
|
||||||
|
|
||||||
if (sz != PMD_SIZE)
|
|
||||||
return 0;
|
|
||||||
if (!ptdesc_pmd_is_shared(virt_to_ptdesc(ptep)))
|
|
||||||
return 0;
|
|
||||||
i_mmap_assert_write_locked(vma->vm_file->f_mapping);
|
|
||||||
hugetlb_vma_assert_locked(vma);
|
|
||||||
pud_clear(pud);
|
|
||||||
|
|
||||||
tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr);
|
|
||||||
|
|
||||||
mm_dec_nr_pmds(mm);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -6961,6 +6974,13 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __huge_pmd_unshare(struct mmu_gather *tlb,
|
||||||
|
struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
|
||||||
|
bool check_locks)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
||||||
unsigned long addr, pte_t *ptep)
|
unsigned long addr, pte_t *ptep)
|
||||||
{
|
{
|
||||||
|
|
@ -7141,17 +7161,6 @@ int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
|
||||||
bool *migratable_cleared)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
spin_lock_irq(&hugetlb_lock);
|
|
||||||
ret = __get_huge_page_for_hwpoison(pfn, flags, migratable_cleared);
|
|
||||||
spin_unlock_irq(&hugetlb_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* folio_putback_hugetlb - unisolate a hugetlb folio
|
* folio_putback_hugetlb - unisolate a hugetlb folio
|
||||||
* @folio: the isolated hugetlb folio
|
* @folio: the isolated hugetlb folio
|
||||||
|
|
@ -7269,7 +7278,7 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
|
||||||
if (!ptep)
|
if (!ptep)
|
||||||
continue;
|
continue;
|
||||||
ptl = huge_pte_lock(h, mm, ptep);
|
ptl = huge_pte_lock(h, mm, ptep);
|
||||||
huge_pmd_unshare(&tlb, vma, address, ptep);
|
__huge_pmd_unshare(&tlb, vma, address, ptep, take_locks);
|
||||||
spin_unlock(ptl);
|
spin_unlock(ptl);
|
||||||
}
|
}
|
||||||
huge_pmd_unshare_flush(&tlb, vma);
|
huge_pmd_unshare_flush(&tlb, vma);
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,8 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
|
||||||
|
|
||||||
/* Remapping the head page requires r/w */
|
/* Remapping the head page requires r/w */
|
||||||
if (unlikely(walk->nr_walked == 0 && walk->vmemmap_head)) {
|
if (unlikely(walk->nr_walked == 0 && walk->vmemmap_head)) {
|
||||||
|
VM_WARN_ON_ONCE(!PageHead((const struct page *)addr));
|
||||||
|
|
||||||
list_del(&walk->vmemmap_head->lru);
|
list_del(&walk->vmemmap_head->lru);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -218,6 +220,8 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
|
||||||
|
|
||||||
entry = mk_pte(walk->vmemmap_head, PAGE_KERNEL);
|
entry = mk_pte(walk->vmemmap_head, PAGE_KERNEL);
|
||||||
} else {
|
} else {
|
||||||
|
VM_WARN_ON_ONCE(!PageTail((const struct page *)addr));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remap the tail pages as read-only to catch illegal write
|
* Remap the tail pages as read-only to catch illegal write
|
||||||
* operation to the tail pages.
|
* operation to the tail pages.
|
||||||
|
|
@ -232,33 +236,28 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
|
||||||
static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,
|
static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,
|
||||||
struct vmemmap_remap_walk *walk)
|
struct vmemmap_remap_walk *walk)
|
||||||
{
|
{
|
||||||
struct page *page;
|
struct page *src = pte_page(ptep_get(pte)), *dst;
|
||||||
struct page *from, *to;
|
|
||||||
|
|
||||||
page = list_first_entry(walk->vmemmap_pages, struct page, lru);
|
|
||||||
list_del(&page->lru);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize tail pages in the newly allocated vmemmap page.
|
* When rolling back vmemmap_remap_free(), keep the copied head page
|
||||||
*
|
* mapping and restore only PTEs currently pointing at the shared tail
|
||||||
* There is folio-scope metadata that is encoded in the first few
|
* page.
|
||||||
* tail pages.
|
|
||||||
*
|
|
||||||
* Use the value last tail page in the page with the head page
|
|
||||||
* to initialize the rest of tail pages.
|
|
||||||
*/
|
*/
|
||||||
from = compound_head((struct page *)addr) +
|
if (walk->vmemmap_tail && walk->vmemmap_tail != src)
|
||||||
PAGE_SIZE / sizeof(struct page) - 1;
|
return;
|
||||||
to = page_to_virt(page);
|
|
||||||
for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++, to++)
|
VM_WARN_ON_ONCE(PageHead((const struct page *)addr));
|
||||||
*to = *from;
|
|
||||||
|
dst = list_first_entry(walk->vmemmap_pages, struct page, lru);
|
||||||
|
list_del(&dst->lru);
|
||||||
|
copy_page(page_to_virt(dst), page_to_virt(src));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Makes sure that preceding stores to the page contents become visible
|
* Makes sure that preceding stores to the page contents become visible
|
||||||
* before the set_pte_at() write.
|
* before the set_pte_at() write.
|
||||||
*/
|
*/
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
set_pte_at(&init_mm, addr, pte, mk_pte(page, PAGE_KERNEL));
|
set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -324,6 +323,7 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
|
||||||
*/
|
*/
|
||||||
walk = (struct vmemmap_remap_walk) {
|
walk = (struct vmemmap_remap_walk) {
|
||||||
.remap_pte = vmemmap_restore_pte,
|
.remap_pte = vmemmap_restore_pte,
|
||||||
|
.vmemmap_tail = vmemmap_tail,
|
||||||
.vmemmap_pages = vmemmap_pages,
|
.vmemmap_pages = vmemmap_pages,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2011,6 +2011,7 @@ struct memcg_stock_pcp {
|
||||||
|
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
uint8_t drain_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
|
static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
|
||||||
|
|
@ -2194,7 +2195,9 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
|
||||||
if (!success) {
|
if (!success) {
|
||||||
i = empty_slot;
|
i = empty_slot;
|
||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
i = get_random_u32_below(NR_MEMCG_STOCK);
|
i = stock->drain_idx++;
|
||||||
|
if (stock->drain_idx == NR_MEMCG_STOCK)
|
||||||
|
stock->drain_idx = 0;
|
||||||
drain_stock(stock, i);
|
drain_stock(stock, i);
|
||||||
}
|
}
|
||||||
css_get(&memcg->css);
|
css_get(&memcg->css);
|
||||||
|
|
|
||||||
|
|
@ -1966,20 +1966,19 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)
|
||||||
folio_free_raw_hwp(folio, true);
|
folio_free_raw_hwp(folio, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
||||||
* Called from hugetlb code with hugetlb_lock held.
|
|
||||||
*/
|
|
||||||
int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
|
||||||
bool *migratable_cleared)
|
bool *migratable_cleared)
|
||||||
{
|
{
|
||||||
struct page *page = pfn_to_page(pfn);
|
struct page *page = pfn_to_page(pfn);
|
||||||
struct folio *folio = page_folio(page);
|
struct folio *folio;
|
||||||
bool count_increased = false;
|
bool count_increased = false;
|
||||||
int ret, rc;
|
int ret, rc;
|
||||||
|
|
||||||
|
spin_lock_irq(&hugetlb_lock);
|
||||||
|
folio = page_folio(page);
|
||||||
if (!folio_test_hugetlb(folio)) {
|
if (!folio_test_hugetlb(folio)) {
|
||||||
ret = MF_HUGETLB_NON_HUGEPAGE;
|
ret = MF_HUGETLB_NON_HUGEPAGE;
|
||||||
goto out;
|
goto out_unlock;
|
||||||
} else if (flags & MF_COUNT_INCREASED) {
|
} else if (flags & MF_COUNT_INCREASED) {
|
||||||
ret = MF_HUGETLB_IN_USED;
|
ret = MF_HUGETLB_IN_USED;
|
||||||
count_increased = true;
|
count_increased = true;
|
||||||
|
|
@ -1995,13 +1994,13 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
||||||
} else {
|
} else {
|
||||||
ret = MF_HUGETLB_RETRY;
|
ret = MF_HUGETLB_RETRY;
|
||||||
if (!(flags & MF_NO_RETRY))
|
if (!(flags & MF_NO_RETRY))
|
||||||
goto out;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = hugetlb_update_hwpoison(folio, page);
|
rc = hugetlb_update_hwpoison(folio, page);
|
||||||
if (rc >= MF_HUGETLB_FOLIO_PRE_POISONED) {
|
if (rc >= MF_HUGETLB_FOLIO_PRE_POISONED) {
|
||||||
ret = rc;
|
ret = rc;
|
||||||
goto out;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -2013,8 +2012,10 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
|
||||||
*migratable_cleared = true;
|
*migratable_cleared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock_irq(&hugetlb_lock);
|
||||||
return ret;
|
return ret;
|
||||||
out:
|
out_unlock:
|
||||||
|
spin_unlock_irq(&hugetlb_lock);
|
||||||
if (count_increased)
|
if (count_increased)
|
||||||
folio_put(folio);
|
folio_put(folio);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
#include <linux/userfaultfd_k.h>
|
#include <linux/userfaultfd_k.h>
|
||||||
#include <linux/mmu_notifier.h>
|
#include <linux/mmu_notifier.h>
|
||||||
#include <linux/hugetlb.h>
|
#include <linux/hugetlb.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
#include <asm/tlb.h>
|
#include <asm/tlb.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
@ -66,7 +68,7 @@ static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma)
|
||||||
{
|
{
|
||||||
if (vma_is_anonymous(vma))
|
if (vma_is_anonymous(vma))
|
||||||
return &anon_uffd_ops;
|
return &anon_uffd_ops;
|
||||||
return vma->vm_ops ? vma->vm_ops->uffd_ops : NULL;
|
return vma->vm_ops->uffd_ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline
|
static __always_inline
|
||||||
|
|
@ -443,16 +445,80 @@ static int mfill_copy_folio_locked(struct folio *folio, unsigned long src_addr)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mfill_copy_folio_retry(struct mfill_state *state,
|
#define MFILL_RETRY_STATE_VMA_FLAGS \
|
||||||
|
append_vma_flags(__VMA_UFFD_FLAGS, VMA_SHARED_BIT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VMA state saved before dropping the locks in mfill_copy_folio_retry().
|
||||||
|
* Used to detect VMA replacement or incompatible changes after reacquiring the
|
||||||
|
* locks.
|
||||||
|
*/
|
||||||
|
struct mfill_retry_state {
|
||||||
|
const struct vm_uffd_ops *ops;
|
||||||
|
struct file *file;
|
||||||
|
vma_flags_t flags;
|
||||||
|
pgoff_t pgoff;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void mfill_retry_state_save(struct mfill_retry_state *s,
|
||||||
|
struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
s->flags = vma_flags_and_mask(&vma->flags, MFILL_RETRY_STATE_VMA_FLAGS);
|
||||||
|
s->ops = vma_uffd_ops(vma);
|
||||||
|
s->pgoff = vma->vm_pgoff;
|
||||||
|
|
||||||
|
if (vma->vm_file)
|
||||||
|
s->file = get_file(vma->vm_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mfill_retry_state_changed(struct mfill_retry_state *state,
|
||||||
|
struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
vma_flags_t flags = vma_flags_and_mask(&vma->flags,
|
||||||
|
MFILL_RETRY_STATE_VMA_FLAGS);
|
||||||
|
|
||||||
|
/* Have any UFFD flags (missing, WP, minor) changed? */
|
||||||
|
if (!vma_flags_same_pair(&state->flags, &flags))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* VMA type or effective uffd_ops changed while the lock was dropped */
|
||||||
|
if (state->ops != vma_uffd_ops(vma))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* VMA was anonymous before; changed only if it no longer is */
|
||||||
|
if (!state->file)
|
||||||
|
return !vma_is_anonymous(vma);
|
||||||
|
|
||||||
|
/* VMA was file backed, but file, inode or offset has changed */
|
||||||
|
if (!vma->vm_file || vma->vm_file->f_inode != state->file->f_inode ||
|
||||||
|
state->file != vma->vm_file || vma->vm_pgoff != state->pgoff)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mfill_retry_state_put(struct mfill_retry_state *s)
|
||||||
|
{
|
||||||
|
if (s->file)
|
||||||
|
fput(s->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_FREE(retry_put, struct mfill_retry_state *,
|
||||||
|
if (_T) mfill_retry_state_put(_T));
|
||||||
|
|
||||||
|
static int mfill_copy_folio_retry(struct mfill_state *mfill_state,
|
||||||
struct folio *folio)
|
struct folio *folio)
|
||||||
{
|
{
|
||||||
const struct vm_uffd_ops *orig_ops = vma_uffd_ops(state->vma);
|
struct mfill_retry_state retry_state = { 0 };
|
||||||
unsigned long src_addr = state->src_addr;
|
struct mfill_retry_state *for_free __free(retry_put) = &retry_state;
|
||||||
|
unsigned long src_addr = mfill_state->src_addr;
|
||||||
void *kaddr;
|
void *kaddr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
mfill_retry_state_save(&retry_state, mfill_state->vma);
|
||||||
|
|
||||||
/* retry copying with mm_lock dropped */
|
/* retry copying with mm_lock dropped */
|
||||||
mfill_put_vma(state);
|
mfill_put_vma(mfill_state);
|
||||||
|
|
||||||
kaddr = kmap_local_folio(folio, 0);
|
kaddr = kmap_local_folio(folio, 0);
|
||||||
err = copy_from_user(kaddr, (const void __user *) src_addr, PAGE_SIZE);
|
err = copy_from_user(kaddr, (const void __user *) src_addr, PAGE_SIZE);
|
||||||
|
|
@ -463,19 +529,14 @@ static int mfill_copy_folio_retry(struct mfill_state *state,
|
||||||
flush_dcache_folio(folio);
|
flush_dcache_folio(folio);
|
||||||
|
|
||||||
/* reget VMA and PMD, they could change underneath us */
|
/* reget VMA and PMD, they could change underneath us */
|
||||||
err = mfill_get_vma(state);
|
err = mfill_get_vma(mfill_state);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/*
|
if (mfill_retry_state_changed(&retry_state, mfill_state->vma))
|
||||||
* The VMA type may have changed while the lock was dropped
|
|
||||||
* (e.g. replaced with a hugetlb mapping), making the caller's
|
|
||||||
* ops pointer stale.
|
|
||||||
*/
|
|
||||||
if (vma_uffd_ops(state->vma) != orig_ops)
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
err = mfill_establish_pmd(state);
|
err = mfill_establish_pmd(mfill_state);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -491,6 +552,11 @@ static int __mfill_atomic_pte(struct mfill_state *state,
|
||||||
struct folio *folio;
|
struct folio *folio;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!ops) {
|
||||||
|
VM_WARN_ONCE(1, "UFFDIO_COPY for unsupported VMA");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
folio = ops->alloc_folio(state->vma, state->dst_addr);
|
folio = ops->alloc_folio(state->vma, state->dst_addr);
|
||||||
if (!folio)
|
if (!folio)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue