mirror of
https://github.com/torvalds/linux.git
synced 2026-09-11 20:13:02 +02:00
18 hotfixes. 13 are cc:stable. 15 are for MM.
All are singletons - please see the changelogs for details. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCapoUuQAKCRDdBJ7gKXxA jgscAP9iRyonROgpsNKC9H8EsAL7QhZNxjwc5PWs0bN6J50LOwD/Um6G7b1P8cxs j7kGpxbQYI0RWxxLUBLTQiPbDrvn6wY= =TJzY -----END PGP SIGNATURE----- Merge tag 'mm-hotfixes-stable-2026-09-03-17-45' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Pull misc fixes from Andrew Morton: "18 hotfixes. 13 are cc:stable. 15 are for MM. All are singletons - please see the changelogs for details. There are no fixes (yet) for all the stuff we added in the most recent merge window. Hopefully a good sign" * tag 'mm-hotfixes-stable-2026-09-03-17-45' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: mm/secretmem: properly account locked pages mm/mremap: reset unfaulted VMA page offset for MREMAP_DONTUNMAP MAINTAINERS: add Kiryl as a THP reviewer MAINTAINERS: cover all of RAID MAINTAINERS: mailmap: update entries for Thorsten Blum MAINTAINERS: remove Lorenzo as THP co-maintainer Revert "once: don't use a work queue to reset sleepable static key" mm/hugetlb: fix missing migratable flag on same-node hugetlb migration mm/mempolicy: fix sleeping allocation in alloc_pages_bulk_weighted_interleave() mm/huge_memory: transfer the pmd dirty bit to the folio on zap MAINTAINERS: add Lance Yang as a hung task detector co-maintainer userfaultfd: reset err to be 0 when move_pages_ptes succeeded mm: fix incorrect vm_flags usage when checking allowable orders for tmpfs mm/hugetlb: keep max_huge_pages when dissolving surplus folios mm/migrate_device: avoid out-of-bounds writes for compound folios mm/hugetlb_cgroup: call page_counter_set_max() outside VM_BUG_ON() memcg: make the v1 soft limit knob inert mm/hugetlb_cma: fix null nodemask dereference in hugetlb_cma_alloc_frozen_folio
This commit is contained in:
commit
bc35965f69
3
.mailmap
3
.mailmap
|
|
@ -899,7 +899,8 @@ Thomas Graf <tgraf@suug.ch>
|
|||
Thomas Gleixner <tglx@kernel.org> <tglx@linutronix.de>
|
||||
Thomas Körper <socketcan@esd.eu> <thomas.koerper@esd.eu>
|
||||
Thomas Pedersen <twp@codeaurora.org>
|
||||
Thorsten Blum <thorsten.blum@linux.dev> <thorsten.blum@toblux.com>
|
||||
Thorsten Blum <blum@kernel.org> <thorsten.blum@toblux.com>
|
||||
Thorsten Blum <blum@kernel.org> <thorsten.blum@linux.dev>
|
||||
Tiezhu Yang <yangtiezhu@loongson.cn> <kernelpatch@126.com>
|
||||
Tingwei Zhang <quic_tingwei@quicinc.com> <tingwei@codeaurora.org>
|
||||
Tirupathi Reddy <quic_tirupath@quicinc.com> <tirupath@codeaurora.org>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ Features:
|
|||
- pages are linked to per-memcg LRU exclusively, and there is no global LRU.
|
||||
- optionally, memory+swap usage can be accounted and limited.
|
||||
- hierarchical accounting
|
||||
- soft limit
|
||||
- moving (recharging) account at moving a task is selectable.
|
||||
- usage threshold notifier
|
||||
- memory pressure notifier
|
||||
|
|
@ -76,10 +75,9 @@ Brief summary of control files.
|
|||
memory.memsw.failcnt show the number of memory+Swap hits limits
|
||||
memory.max_usage_in_bytes show max memory usage recorded
|
||||
memory.memsw.max_usage_in_bytes show max memory+Swap usage recorded
|
||||
memory.soft_limit_in_bytes set/show soft limit of memory usage
|
||||
This knob is not available on CONFIG_PREEMPT_RT systems.
|
||||
This knob is deprecated and shouldn't be
|
||||
used.
|
||||
memory.soft_limit_in_bytes This knob is deprecated and has no effect.
|
||||
Writes are ignored and reads always
|
||||
return the maximum value.
|
||||
memory.stat show various statistics
|
||||
memory.use_hierarchy set/show hierarchical account enabled
|
||||
This knob is deprecated and shouldn't be
|
||||
|
|
@ -340,9 +338,6 @@ memory.kmem.usage_in_bytes, or in a separate counter when it makes sense.
|
|||
The main "kmem" counter is fed into the main counter, so kmem charges will
|
||||
also be visible from the user counter.
|
||||
|
||||
Currently no soft limit is implemented for kernel memory. It is future work
|
||||
to trigger slab reclaim when those limits are reached.
|
||||
|
||||
2.7.1 Current Kernel Memory resources accounted
|
||||
-----------------------------------------------
|
||||
|
||||
|
|
@ -710,42 +705,10 @@ For compatibility reasons writing 1 to memory.use_hierarchy will always pass::
|
|||
|
||||
THIS IS DEPRECATED!
|
||||
|
||||
Soft limits allow for greater sharing of memory. The idea behind soft limits
|
||||
is to allow control groups to use as much of the memory as needed, provided
|
||||
Writing to memory.soft_limit_in_bytes has no effect and reading it will
|
||||
always return the maximum value.
|
||||
|
||||
a. There is no memory contention
|
||||
b. They do not exceed their hard limit
|
||||
|
||||
When the system detects memory contention or low memory, control groups
|
||||
are pushed back to their soft limits. If the soft limit of each control
|
||||
group is very high, they are pushed back as much as possible to make
|
||||
sure that one control group does not starve the others of memory.
|
||||
|
||||
Please note that soft limits is a best-effort feature; it comes with
|
||||
no guarantees, but it does its best to make sure that when memory is
|
||||
heavily contended for, memory is allocated based on the soft limit
|
||||
hints/setup. Currently soft limit based reclaim is set up such that
|
||||
it gets invoked from balance_pgdat (kswapd).
|
||||
|
||||
7.1 Interface
|
||||
-------------
|
||||
|
||||
Soft limits can be setup by using the following commands (in this example we
|
||||
assume a soft limit of 256 MiB)::
|
||||
|
||||
# echo 256M > memory.soft_limit_in_bytes
|
||||
|
||||
If we want to change this to 1G, we can at any time use::
|
||||
|
||||
# echo 1G > memory.soft_limit_in_bytes
|
||||
|
||||
.. note::
|
||||
Soft limits take effect over a long period of time, since they involve
|
||||
reclaiming memory for balancing between memory cgroups
|
||||
|
||||
.. note::
|
||||
It is recommended to set the soft limit always below the hard limit,
|
||||
otherwise the hard limit will take precedence.
|
||||
Use memory.low and memory.min in cgroup v2 instead.
|
||||
|
||||
.. _cgroup-v1-memory-move-charges:
|
||||
|
||||
|
|
|
|||
10
MAINTAINERS
10
MAINTAINERS
|
|
@ -12221,7 +12221,7 @@ F: drivers/tty/hvc/
|
|||
|
||||
HUNG TASK DETECTOR
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
R: Lance Yang <lance.yang@linux.dev>
|
||||
M: Lance Yang <lance.yang@linux.dev>
|
||||
R: Masami Hiramatsu <mhiramat@kernel.org>
|
||||
R: Petr Mladek <pmladek@suse.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
|
|
@ -17410,7 +17410,6 @@ F: mm/swapfile.c
|
|||
MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE)
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: David Hildenbrand <david@kernel.org>
|
||||
M: Lorenzo Stoakes <ljs@kernel.org>
|
||||
R: Zi Yan <ziy@nvidia.com>
|
||||
R: Baolin Wang <baolin.wang@linux.alibaba.com>
|
||||
R: Liam R. Howlett <liam@infradead.org>
|
||||
|
|
@ -17420,6 +17419,7 @@ R: Dev Jain <dev.jain@arm.com>
|
|||
R: Barry Song <baohua@kernel.org>
|
||||
R: Lance Yang <lance.yang@linux.dev>
|
||||
R: Usama Arif <usama.arif@linux.dev>
|
||||
R: Kiryl Shutsemau <kas@kernel.org>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
W: http://www.linux-mm.org
|
||||
|
|
@ -17692,7 +17692,7 @@ F: Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml
|
|||
F: drivers/spi/spi-at91-usart.c
|
||||
|
||||
MICROCHIP ATSHA204A DRIVER
|
||||
M: Thorsten Blum <thorsten.blum@linux.dev>
|
||||
M: Thorsten Blum <blum@kernel.org>
|
||||
L: linux-crypto@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/crypto/atmel-sha204a.c
|
||||
|
|
@ -17716,7 +17716,7 @@ F: Documentation/devicetree/bindings/media/microchip,csi2dc.yaml
|
|||
F: drivers/media/platform/microchip/microchip-csi2dc.c
|
||||
|
||||
MICROCHIP ECC DRIVER
|
||||
M: Thorsten Blum <thorsten.blum@linux.dev>
|
||||
M: Thorsten Blum <blum@kernel.org>
|
||||
L: linux-crypto@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/crypto/atmel-ecc.c
|
||||
|
|
@ -25403,7 +25403,7 @@ F: drivers/md/md*
|
|||
F: drivers/md/raid*
|
||||
F: include/linux/raid/
|
||||
F: include/uapi/linux/raid/
|
||||
F: lib/raid/raid6/
|
||||
F: lib/raid/
|
||||
|
||||
SOLIDRUN CLEARFOG SUPPORT
|
||||
M: Russell King <linux@armlinux.org.uk>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ struct user_struct {
|
|||
|
||||
#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) || \
|
||||
defined(CONFIG_NET) || defined(CONFIG_IO_URING) || \
|
||||
defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD)
|
||||
defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD) || \
|
||||
defined(CONFIG_SECRETMEM)
|
||||
atomic_long_t locked_vm;
|
||||
#endif
|
||||
#ifdef CONFIG_WATCH_QUEUE
|
||||
|
|
|
|||
|
|
@ -93,6 +93,6 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key,
|
|||
{
|
||||
*done = true;
|
||||
mutex_unlock(&once_mutex);
|
||||
static_branch_disable(once_key);
|
||||
once_disable_jump(once_key, mod);
|
||||
}
|
||||
EXPORT_SYMBOL(__do_once_sleepable_done);
|
||||
|
|
|
|||
|
|
@ -2449,6 +2449,8 @@ static void zap_huge_pmd_folio(struct mm_struct *mm, struct vm_area_struct *vma,
|
|||
add_mm_counter(mm, mm_counter_file(folio),
|
||||
-HPAGE_PMD_NR);
|
||||
|
||||
if (is_present && pmd_dirty(pmdval))
|
||||
folio_mark_dirty(folio);
|
||||
if (is_present && pmd_young(pmdval) &&
|
||||
likely(vma_has_recency(vma)))
|
||||
folio_mark_accessed(folio);
|
||||
|
|
|
|||
20
mm/hugetlb.c
20
mm/hugetlb.c
|
|
@ -1992,7 +1992,8 @@ int dissolve_free_hugetlb_folio(struct folio *folio)
|
|||
if (h->surplus_huge_pages_node[folio_nid(folio)])
|
||||
adjust_surplus = true;
|
||||
remove_hugetlb_folio(h, folio, adjust_surplus);
|
||||
h->max_huge_pages--;
|
||||
if (!adjust_surplus)
|
||||
h->max_huge_pages--;
|
||||
spin_unlock_irq(&hugetlb_lock);
|
||||
|
||||
/*
|
||||
|
|
@ -2012,7 +2013,8 @@ int dissolve_free_hugetlb_folio(struct folio *folio)
|
|||
if (rc) {
|
||||
spin_lock_irq(&hugetlb_lock);
|
||||
add_hugetlb_folio(h, folio, adjust_surplus);
|
||||
h->max_huge_pages++;
|
||||
if (!adjust_surplus)
|
||||
h->max_huge_pages++;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -7330,14 +7332,14 @@ void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,
|
|||
* There is no need to transfer the per-node surplus state
|
||||
* when we do not cross the node.
|
||||
*/
|
||||
if (new_nid == old_nid)
|
||||
return;
|
||||
spin_lock_irq(&hugetlb_lock);
|
||||
if (h->surplus_huge_pages_node[old_nid]) {
|
||||
h->surplus_huge_pages_node[old_nid]--;
|
||||
h->surplus_huge_pages_node[new_nid]++;
|
||||
if (new_nid != old_nid) {
|
||||
spin_lock_irq(&hugetlb_lock);
|
||||
if (h->surplus_huge_pages_node[old_nid]) {
|
||||
h->surplus_huge_pages_node[old_nid]--;
|
||||
h->surplus_huge_pages_node[new_nid]++;
|
||||
}
|
||||
spin_unlock_irq(&hugetlb_lock);
|
||||
}
|
||||
spin_unlock_irq(&hugetlb_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup,
|
|||
struct page_counter *fault, *fault_parent = NULL;
|
||||
struct page_counter *rsvd, *rsvd_parent = NULL;
|
||||
unsigned long limit;
|
||||
int ret;
|
||||
|
||||
if (parent_h_cgroup) {
|
||||
fault_parent = hugetlb_cgroup_counter_from_cgroup(
|
||||
|
|
@ -118,8 +119,10 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup,
|
|||
limit = round_down(PAGE_COUNTER_MAX,
|
||||
pages_per_huge_page(&hstates[idx]));
|
||||
|
||||
VM_BUG_ON(page_counter_set_max(fault, limit));
|
||||
VM_BUG_ON(page_counter_set_max(rsvd, limit));
|
||||
ret = page_counter_set_max(fault, limit);
|
||||
VM_WARN_ON_ONCE(ret);
|
||||
ret = page_counter_set_max(rsvd, limit);
|
||||
VM_WARN_ON_ONCE(ret);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/cpuset.h>
|
||||
#include <linux/mm_inline.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
|
|
@ -55,15 +56,25 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
|
|||
int node;
|
||||
struct folio *folio;
|
||||
struct page *page = NULL;
|
||||
const nodemask_t *nmask;
|
||||
unsigned int cpuset_mems_cookie;
|
||||
|
||||
if (!hugetlb_cma_size)
|
||||
return NULL;
|
||||
|
||||
if (hugetlb_cma[nid])
|
||||
retry_cpuset:
|
||||
if (!nodemask) {
|
||||
cpuset_mems_cookie = read_mems_allowed_begin();
|
||||
nmask = &cpuset_current_mems_allowed;
|
||||
} else {
|
||||
nmask = nodemask;
|
||||
}
|
||||
|
||||
if (hugetlb_cma[nid] && node_isset(nid, *nmask))
|
||||
page = cma_alloc_frozen_compound(hugetlb_cma[nid], order);
|
||||
|
||||
if (!page && !(gfp_mask & __GFP_THISNODE)) {
|
||||
for_each_node_mask(node, *nodemask) {
|
||||
for_each_node_mask(node, *nmask) {
|
||||
if (node == nid || !hugetlb_cma[node])
|
||||
continue;
|
||||
|
||||
|
|
@ -73,8 +84,12 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
|
|||
}
|
||||
}
|
||||
|
||||
if (!page)
|
||||
if (!page) {
|
||||
if (!nodemask &&
|
||||
unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
|
||||
goto retry_cpuset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
folio = page_folio(page);
|
||||
folio_set_hugetlb_cma(folio);
|
||||
|
|
|
|||
|
|
@ -454,12 +454,6 @@ int hugepage_madvise(struct vm_area_struct *vma,
|
|||
case MADV_HUGEPAGE:
|
||||
*vm_flags &= ~VM_NOHUGEPAGE;
|
||||
*vm_flags |= VM_HUGEPAGE;
|
||||
/*
|
||||
* If the vma become good for khugepaged to scan,
|
||||
* register it here without waiting a page fault that
|
||||
* may not happen any time soon.
|
||||
*/
|
||||
khugepaged_enter_vma(vma, *vm_flags);
|
||||
break;
|
||||
case MADV_NOHUGEPAGE:
|
||||
*vm_flags &= ~VM_HUGEPAGE;
|
||||
|
|
|
|||
|
|
@ -178,6 +178,14 @@ static int madvise_update_vma(vm_flags_t new_flags,
|
|||
/* vm_flags is protected by the mmap_lock held in write mode. */
|
||||
vma_start_write(vma);
|
||||
vma->flags = new_vma_flags;
|
||||
/*
|
||||
* If the vma become good for khugepaged to scan,
|
||||
* register it here without waiting a page fault that
|
||||
* may not happen any time soon.
|
||||
*/
|
||||
if (vma_flags_test(&new_vma_flags, VMA_HUGEPAGE_BIT))
|
||||
khugepaged_enter_vma(vma, vma_flags_to_legacy(new_vma_flags));
|
||||
|
||||
if (set_new_anon_name)
|
||||
return replace_anon_vma_name(vma, anon_name);
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ enum {
|
|||
RES_LIMIT,
|
||||
RES_MAX_USAGE,
|
||||
RES_FAILCNT,
|
||||
RES_SOFT_LIMIT,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
|
|
@ -1888,6 +1887,30 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static u64 mem_cgroup_soft_limit_read(struct cgroup_subsys_state *css,
|
||||
struct cftype *cft)
|
||||
{
|
||||
return (u64)PAGE_COUNTER_MAX * PAGE_SIZE;
|
||||
}
|
||||
|
||||
static ssize_t mem_cgroup_soft_limit_write(struct kernfs_open_file *of,
|
||||
char *buf, size_t nbytes, loff_t off)
|
||||
{
|
||||
unsigned long nr_pages;
|
||||
int ret;
|
||||
|
||||
ret = page_counter_memparse(strstrip(buf), "-1", &nr_pages);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. "
|
||||
"Writing any value to this file has no effect. "
|
||||
"Please report your usecase to linux-mm@kvack.org if you "
|
||||
"depend on this functionality.\n");
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,
|
||||
struct cftype *cft)
|
||||
{
|
||||
|
|
@ -1924,8 +1947,6 @@ static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,
|
|||
return (u64)counter->watermark * PAGE_SIZE;
|
||||
case RES_FAILCNT:
|
||||
return counter->failcnt;
|
||||
case RES_SOFT_LIMIT:
|
||||
return (u64)READ_ONCE(memcg->soft_limit) * PAGE_SIZE;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
|
@ -2020,17 +2041,6 @@ static ssize_t mem_cgroup_write(struct kernfs_open_file *of,
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case RES_SOFT_LIMIT:
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
|
||||
ret = -EOPNOTSUPP;
|
||||
} else {
|
||||
pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. "
|
||||
"Please report your usecase to linux-mm@kvack.org if you "
|
||||
"depend on this functionality.\n");
|
||||
WRITE_ONCE(memcg->soft_limit, nr_pages);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret ?: nbytes;
|
||||
}
|
||||
|
|
@ -2384,9 +2394,8 @@ struct cftype mem_cgroup_legacy_files[] = {
|
|||
},
|
||||
{
|
||||
.name = "soft_limit_in_bytes",
|
||||
.private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT),
|
||||
.write = mem_cgroup_write,
|
||||
.read_u64 = mem_cgroup_read_u64,
|
||||
.write = mem_cgroup_soft_limit_write,
|
||||
.read_u64 = mem_cgroup_soft_limit_read,
|
||||
},
|
||||
{
|
||||
.name = "failcnt",
|
||||
|
|
|
|||
|
|
@ -2679,7 +2679,7 @@ static unsigned long alloc_pages_bulk_weighted_interleave(gfp_t gfp,
|
|||
prev_node = node;
|
||||
|
||||
/* create a local copy of node weights to operate on outside rcu */
|
||||
weights = kzalloc(nr_node_ids, GFP_KERNEL);
|
||||
weights = kmalloc(nr_node_ids, gfp & GFP_RECLAIM_MASK);
|
||||
if (!weights)
|
||||
return total_allocated;
|
||||
|
||||
|
|
|
|||
|
|
@ -1423,6 +1423,15 @@ int migrate_device_range(unsigned long *src_pfns, unsigned long start,
|
|||
|
||||
src_pfns[i] = migrate_device_pfn_lock(pfn);
|
||||
nr = folio_nr_pages(folio);
|
||||
if (nr > npages - i) {
|
||||
if (src_pfns[i] & MIGRATE_PFN_MIGRATE) {
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
}
|
||||
memset(&src_pfns[i], 0,
|
||||
(npages - i) * sizeof(*src_pfns));
|
||||
break;
|
||||
}
|
||||
if (nr > 1) {
|
||||
src_pfns[i] |= MIGRATE_PFN_COMPOUND;
|
||||
for (j = 1; j < nr; j++)
|
||||
|
|
@ -1457,6 +1466,15 @@ int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages)
|
|||
|
||||
src_pfns[i] = migrate_device_pfn_lock(src_pfns[i]);
|
||||
nr = folio_nr_pages(folio);
|
||||
if (nr > npages - i) {
|
||||
if (src_pfns[i] & MIGRATE_PFN_MIGRATE) {
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
}
|
||||
memset(&src_pfns[i], 0,
|
||||
(npages - i) * sizeof(*src_pfns));
|
||||
break;
|
||||
}
|
||||
if (nr > 1) {
|
||||
src_pfns[i] |= MIGRATE_PFN_COMPOUND;
|
||||
for (j = 1; j < nr; j++)
|
||||
|
|
|
|||
22
mm/mremap.c
22
mm/mremap.c
|
|
@ -1331,18 +1331,30 @@ static void dontunmap_complete(struct vma_remap_struct *vrm,
|
|||
{
|
||||
unsigned long start = vrm->addr;
|
||||
unsigned long end = vrm->addr + vrm->old_len;
|
||||
unsigned long old_start = vrm->vma->vm_start;
|
||||
unsigned long old_end = vrm->vma->vm_end;
|
||||
struct vm_area_struct *vma = vrm->vma;
|
||||
unsigned long old_start = vma->vm_start;
|
||||
unsigned long old_end = vma->vm_end;
|
||||
|
||||
/* We always clear VMA_LOCKED[ONFAULT]_BIT on the old VMA. */
|
||||
vma_clear_flags_mask(vrm->vma, VMA_LOCKED_MASK);
|
||||
vma_clear_flags_mask(vma, VMA_LOCKED_MASK);
|
||||
|
||||
/*
|
||||
* anon_vma links of the old vma is no longer needed after its page
|
||||
* table has been moved.
|
||||
*/
|
||||
if (new_vma != vrm->vma && start == old_start && end == old_end)
|
||||
unlink_anon_vmas(vrm->vma);
|
||||
if (new_vma != vma && start == old_start && end == old_end) {
|
||||
const pgoff_t pgoff_unfaulted = vma->vm_start >> PAGE_SHIFT;
|
||||
|
||||
unlink_anon_vmas(vma);
|
||||
/*
|
||||
* The VMA is now unfaulted and it is an invariant that
|
||||
* unfaulted anonymous VMAs have page offset equal to
|
||||
* vma->vm_start >> PAGE_SHIFT.
|
||||
*/
|
||||
vma_set_anon_pgoff(vma, pgoff_unfaulted);
|
||||
if (vma_is_anonymous(vma) && !vma->vm_file)
|
||||
vma_set_pgoff(vma, pgoff_unfaulted);
|
||||
}
|
||||
|
||||
/* Because we won't unmap we don't need to touch locked_vm. */
|
||||
}
|
||||
|
|
|
|||
116
mm/secretmem.c
116
mm/secretmem.c
|
|
@ -18,6 +18,8 @@
|
|||
#include <linux/secretmem.h>
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/sched/user.h>
|
||||
#include <linux/cred.h>
|
||||
|
||||
#include <uapi/linux/magic.h>
|
||||
|
||||
|
|
@ -47,10 +49,69 @@ bool secretmem_active(void)
|
|||
return !!atomic_read(&secretmem_users);
|
||||
}
|
||||
|
||||
struct secretmem_inode_state {
|
||||
struct user_struct *user;
|
||||
atomic_long_t nr_pages_accounted;
|
||||
};
|
||||
|
||||
static bool __secretmem_account_pages(struct user_struct *user,
|
||||
unsigned long nr_pages)
|
||||
{
|
||||
unsigned long page_limit, cur_pages, new_pages;
|
||||
|
||||
if (!nr_pages)
|
||||
return true;
|
||||
|
||||
page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
|
||||
|
||||
cur_pages = atomic_long_read(&user->locked_vm);
|
||||
do {
|
||||
new_pages = cur_pages + nr_pages;
|
||||
if (new_pages > page_limit)
|
||||
return false;
|
||||
} while (!atomic_long_try_cmpxchg(&user->locked_vm,
|
||||
&cur_pages, new_pages));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool secretmem_account_folio(struct secretmem_inode_state *state,
|
||||
const struct folio *folio)
|
||||
{
|
||||
const unsigned long nr_pages = folio_nr_pages(folio);
|
||||
|
||||
if (!__secretmem_account_pages(state->user, nr_pages))
|
||||
return false;
|
||||
|
||||
atomic_long_add(nr_pages, &state->nr_pages_accounted);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __secretmem_unaccount_pages(struct secretmem_inode_state *state,
|
||||
unsigned long nr_pages)
|
||||
{
|
||||
atomic_long_sub(nr_pages, &state->user->locked_vm);
|
||||
atomic_long_sub(nr_pages, &state->nr_pages_accounted);
|
||||
}
|
||||
|
||||
static void secretmem_unaccount_folio(struct secretmem_inode_state *state,
|
||||
struct folio *folio)
|
||||
{
|
||||
__secretmem_unaccount_pages(state, folio_nr_pages(folio));
|
||||
}
|
||||
|
||||
static void secretmem_unaccount_all_folios(struct secretmem_inode_state *state)
|
||||
{
|
||||
const unsigned long nr_pages_accounted =
|
||||
atomic_long_read(&state->nr_pages_accounted);
|
||||
|
||||
__secretmem_unaccount_pages(state, nr_pages_accounted);
|
||||
}
|
||||
|
||||
static vm_fault_t secretmem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct address_space *mapping = vmf->vma->vm_file->f_mapping;
|
||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
struct secretmem_inode_state *state = inode->i_private;
|
||||
pgoff_t offset = vmf->pgoff;
|
||||
gfp_t gfp = vmf->gfp_mask;
|
||||
unsigned long addr;
|
||||
|
|
@ -72,8 +133,15 @@ static vm_fault_t secretmem_fault(struct vm_fault *vmf)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (!secretmem_account_folio(state, folio)) {
|
||||
folio_put(folio);
|
||||
ret = VM_FAULT_SIGBUS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = set_direct_map_invalid_noflush(folio_page(folio, 0));
|
||||
if (err) {
|
||||
secretmem_unaccount_folio(state, folio);
|
||||
folio_put(folio);
|
||||
ret = vmf_error(err);
|
||||
goto out;
|
||||
|
|
@ -82,6 +150,7 @@ static vm_fault_t secretmem_fault(struct vm_fault *vmf)
|
|||
__folio_mark_uptodate(folio);
|
||||
err = filemap_add_folio(mapping, folio, offset, gfp);
|
||||
if (unlikely(err)) {
|
||||
secretmem_unaccount_folio(state, folio);
|
||||
/*
|
||||
* If a split of large page was required, it
|
||||
* already happened when we marked the page invalid
|
||||
|
|
@ -112,22 +181,30 @@ static const struct vm_operations_struct secretmem_vm_ops = {
|
|||
.fault = secretmem_fault,
|
||||
};
|
||||
|
||||
static void secretmem_destroy_inode_priv(struct inode *inode)
|
||||
{
|
||||
struct secretmem_inode_state *state = inode->i_private;
|
||||
|
||||
secretmem_unaccount_all_folios(state);
|
||||
free_uid(state->user);
|
||||
kfree(state);
|
||||
inode->i_private = NULL;
|
||||
}
|
||||
|
||||
static int secretmem_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
atomic_dec(&secretmem_users);
|
||||
secretmem_destroy_inode_priv(inode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int secretmem_mmap_prepare(struct vm_area_desc *desc)
|
||||
{
|
||||
const unsigned long len = vma_desc_size(desc);
|
||||
|
||||
if (!vma_desc_test_any(desc, VMA_SHARED_BIT, VMA_MAYSHARE_BIT))
|
||||
return -EINVAL;
|
||||
|
||||
vma_desc_set_flags(desc, VMA_LOCKED_BIT, VMA_DONTDUMP_BIT);
|
||||
if (!mlock_future_ok(desc->mm, /*is_vma_locked=*/ true, len))
|
||||
return -EAGAIN;
|
||||
vma_desc_set_flags(desc, VMA_DONTDUMP_BIT);
|
||||
desc->vm_ops = &secretmem_vm_ops;
|
||||
|
||||
return 0;
|
||||
|
|
@ -187,20 +264,40 @@ static const struct inode_operations secretmem_iops = {
|
|||
|
||||
static struct vfsmount *secretmem_mnt;
|
||||
|
||||
static int secretmem_init_inode_priv(struct inode *inode)
|
||||
{
|
||||
struct secretmem_inode_state *state;
|
||||
|
||||
state = kzalloc_obj(*state);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
state->user = get_uid(current_user());
|
||||
inode->i_private = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file *secretmem_file_create(unsigned long flags)
|
||||
{
|
||||
struct file *file;
|
||||
struct inode *inode;
|
||||
const char *anon_name = "[secretmem]";
|
||||
int err;
|
||||
|
||||
inode = anon_inode_make_secure_inode(secretmem_mnt->mnt_sb, anon_name, NULL);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
|
||||
err = secretmem_init_inode_priv(inode);
|
||||
if (err)
|
||||
goto err_free_inode;
|
||||
|
||||
file = alloc_file_pseudo(inode, secretmem_mnt, "secretmem",
|
||||
O_RDWR | O_LARGEFILE, &secretmem_fops);
|
||||
if (IS_ERR(file))
|
||||
goto err_free_inode;
|
||||
if (IS_ERR(file)) {
|
||||
err = PTR_ERR(file);
|
||||
goto err_free_priv;
|
||||
}
|
||||
|
||||
mapping_set_gfp_mask(inode->i_mapping, GFP_USER);
|
||||
mapping_set_unevictable(inode->i_mapping);
|
||||
|
|
@ -215,10 +312,11 @@ static struct file *secretmem_file_create(unsigned long flags)
|
|||
atomic_inc(&secretmem_users);
|
||||
|
||||
return file;
|
||||
|
||||
err_free_priv:
|
||||
secretmem_destroy_inode_priv(inode);
|
||||
err_free_inode:
|
||||
iput(inode);
|
||||
return file;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(memfd_secret, unsigned int, flags)
|
||||
|
|
|
|||
|
|
@ -2171,8 +2171,10 @@ static ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start,
|
|||
}
|
||||
|
||||
if (err) {
|
||||
if (err == -EAGAIN)
|
||||
if (err == -EAGAIN) {
|
||||
err = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,33 +57,6 @@ static void test_file_apis(int fd)
|
|||
pass("file IO is blocked as expected\n");
|
||||
}
|
||||
|
||||
static void test_mlock_limit(int fd)
|
||||
{
|
||||
size_t len;
|
||||
char *mem;
|
||||
|
||||
len = mlock_limit_cur;
|
||||
if (len % page_size != 0)
|
||||
len = (len/page_size) * page_size;
|
||||
|
||||
mem = mmap(NULL, len, prot, mode, fd, 0);
|
||||
if (mem == MAP_FAILED) {
|
||||
fail("unable to mmap secret memory\n");
|
||||
return;
|
||||
}
|
||||
munmap(mem, len);
|
||||
|
||||
len = mlock_limit_max * 2;
|
||||
mem = mmap(NULL, len, prot, mode, fd, 0);
|
||||
if (mem != MAP_FAILED) {
|
||||
fail("unexpected mlock limit violation\n");
|
||||
munmap(mem, len);
|
||||
return;
|
||||
}
|
||||
|
||||
pass("mlock limit is respected\n");
|
||||
}
|
||||
|
||||
static void test_vmsplice(int fd, const char *desc)
|
||||
{
|
||||
ssize_t transferred;
|
||||
|
|
@ -297,7 +270,7 @@ static void prepare(void)
|
|||
strerror(errno));
|
||||
}
|
||||
|
||||
#define NUM_TESTS 6
|
||||
#define NUM_TESTS 5
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
|
@ -319,7 +292,6 @@ int main(int argc, char *argv[])
|
|||
if (ftruncate(fd, page_size))
|
||||
ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno));
|
||||
|
||||
test_mlock_limit(fd);
|
||||
test_file_apis(fd);
|
||||
/*
|
||||
* We have to run the first vmsplice test before any secretmem page was
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user