15 hotfixes. 6 are cc:stable. 14 are for MM.

Singletons, with one doubleton - please see the changelogs for details.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCaa9ZkgAKCRDdBJ7gKXxA
 jpfeAQDAj+Sn3bRdJWCUVNgWPdNLrmZnZRw5VdipioAnfmZHZQEAqBXnm7ehn1BA
 JgQvx6zTlsyqy4FhW+Q6uLmBpKmMTQw=
 =kbLH
 -----END PGP SIGNATURE-----

Merge tag 'mm-hotfixes-stable-2026-03-09-16-36' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull misc fixes from Andrew Morton:
 "15 hotfixes. 6 are cc:stable. 14 are for MM.

  Singletons, with one doubleton - please see the changelogs for details"

* tag 'mm-hotfixes-stable-2026-03-09-16-36' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm:
  MAINTAINERS, mailmap: update email address for Lorenzo Stoakes
  mm/mmu_notifier: clean up mmu_notifier.h kernel-doc
  uaccess: correct kernel-doc parameter format
  mm/huge_memory: fix a folio_split() race condition with folio_try_get()
  MAINTAINERS: add co-maintainer and reviewer for SLAB ALLOCATOR
  MAINTAINERS: add RELAY entry
  memcg: fix slab accounting in refill_obj_stock() trylock path
  mm/hugetlb.c: use __pa() instead of virt_to_phys() in early bootmem alloc code
  zram: rename writeback_compressed device attr
  tools/testing: fix testing/vma and testing/radix-tree build
  Revert "ptdesc: remove references to folios from __pagetable_ctor() and pagetable_dtor()"
  mm/cma: move put_page_testzero() out of VM_WARN_ON in cma_release()
  mm/damon/core: clear walk_control on inactive context in damos_walk()
  mm: memfd_luo: always dirty all folios
  mm: memfd_luo: always make all folios uptodate
This commit is contained in:
Linus Torvalds 2026-03-10 12:47:56 -07:00
commit b4f0dd314b
18 changed files with 163 additions and 73 deletions

View File

@ -498,7 +498,8 @@ Lior David <quic_liord@quicinc.com> <liord@codeaurora.org>
Loic Poulain <loic.poulain@oss.qualcomm.com> <loic.poulain@linaro.org>
Loic Poulain <loic.poulain@oss.qualcomm.com> <loic.poulain@intel.com>
Lorenzo Pieralisi <lpieralisi@kernel.org> <lorenzo.pieralisi@arm.com>
Lorenzo Stoakes <lorenzo.stoakes@oracle.com> <lstoakes@gmail.com>
Lorenzo Stoakes <ljs@kernel.org> <lstoakes@gmail.com>
Lorenzo Stoakes <ljs@kernel.org> <lorenzo.stoakes@oracle.com>
Luca Ceresoli <luca.ceresoli@bootlin.com> <luca@lucaceresoli.net>
Luca Weiss <luca@lucaweiss.eu> <luca@z3ntu.xyz>
Lucas De Marchi <demarchi@kernel.org> <lucas.demarchi@intel.com>

View File

@ -151,11 +151,11 @@ Description:
The algorithm_params file is write-only and is used to setup
compression algorithm parameters.
What: /sys/block/zram<id>/writeback_compressed
What: /sys/block/zram<id>/compressed_writeback
Date: Decemeber 2025
Contact: Richard Chang <richardycc@google.com>
Description:
The writeback_compressed device atrribute toggles compressed
The compressed_writeback device atrribute toggles compressed
writeback feature.
What: /sys/block/zram<id>/writeback_batch_size

View File

@ -216,7 +216,7 @@ writeback_limit WO specifies the maximum amount of write IO zram
writeback_limit_enable RW show and set writeback_limit feature
writeback_batch_size RW show and set maximum number of in-flight
writeback operations
writeback_compressed RW show and set compressed writeback feature
compressed_writeback RW show and set compressed writeback feature
comp_algorithm RW show and change the compression algorithm
algorithm_params WO setup compression algorithm parameters
compact WO trigger memory compaction
@ -439,11 +439,11 @@ budget in next setting is user's job.
By default zram stores written back pages in decompressed (raw) form, which
means that writeback operation involves decompression of the page before
writing it to the backing device. This behavior can be changed by enabling
`writeback_compressed` feature, which causes zram to write compressed pages
`compressed_writeback` feature, which causes zram to write compressed pages
to the backing device, thus avoiding decompression overhead. To enable
this feature, execute::
$ echo yes > /sys/block/zramX/writeback_compressed
$ echo yes > /sys/block/zramX/compressed_writeback
Note that this feature should be configured before the `zramX` device is
initialized.

View File

@ -16643,7 +16643,7 @@ F: mm/balloon.c
MEMORY MANAGEMENT - CORE
M: Andrew Morton <akpm@linux-foundation.org>
M: David Hildenbrand <david@kernel.org>
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Lorenzo Stoakes <ljs@kernel.org>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
R: Vlastimil Babka <vbabka@kernel.org>
R: Mike Rapoport <rppt@kernel.org>
@ -16773,7 +16773,7 @@ F: mm/workingset.c
MEMORY MANAGEMENT - MISC
M: Andrew Morton <akpm@linux-foundation.org>
M: David Hildenbrand <david@kernel.org>
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Lorenzo Stoakes <ljs@kernel.org>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
R: Vlastimil Babka <vbabka@kernel.org>
R: Mike Rapoport <rppt@kernel.org>
@ -16864,7 +16864,7 @@ R: David Hildenbrand <david@kernel.org>
R: Michal Hocko <mhocko@kernel.org>
R: Qi Zheng <zhengqi.arch@bytedance.com>
R: Shakeel Butt <shakeel.butt@linux.dev>
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Lorenzo Stoakes <ljs@kernel.org>
L: linux-mm@kvack.org
S: Maintained
F: mm/vmscan.c
@ -16873,7 +16873,7 @@ F: mm/workingset.c
MEMORY MANAGEMENT - RMAP (REVERSE MAPPING)
M: Andrew Morton <akpm@linux-foundation.org>
M: David Hildenbrand <david@kernel.org>
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
M: Lorenzo Stoakes <ljs@kernel.org>
R: Rik van Riel <riel@surriel.com>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
R: Vlastimil Babka <vbabka@kernel.org>
@ -16918,7 +16918,7 @@ 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 <lorenzo.stoakes@oracle.com>
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.Howlett@oracle.com>
@ -16958,7 +16958,7 @@ F: tools/testing/selftests/mm/uffd-*.[ch]
MEMORY MANAGEMENT - RUST
M: Alice Ryhl <aliceryhl@google.com>
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Lorenzo Stoakes <ljs@kernel.org>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
L: linux-mm@kvack.org
L: rust-for-linux@vger.kernel.org
@ -16974,7 +16974,7 @@ F: rust/kernel/page.rs
MEMORY MAPPING
M: Andrew Morton <akpm@linux-foundation.org>
M: Liam R. Howlett <Liam.Howlett@oracle.com>
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
M: Lorenzo Stoakes <ljs@kernel.org>
R: Vlastimil Babka <vbabka@kernel.org>
R: Jann Horn <jannh@google.com>
R: Pedro Falcato <pfalcato@suse.de>
@ -17004,7 +17004,7 @@ MEMORY MAPPING - LOCKING
M: Andrew Morton <akpm@linux-foundation.org>
M: Suren Baghdasaryan <surenb@google.com>
M: Liam R. Howlett <Liam.Howlett@oracle.com>
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
M: Lorenzo Stoakes <ljs@kernel.org>
R: Vlastimil Babka <vbabka@kernel.org>
R: Shakeel Butt <shakeel.butt@linux.dev>
L: linux-mm@kvack.org
@ -17019,7 +17019,7 @@ F: mm/mmap_lock.c
MEMORY MAPPING - MADVISE (MEMORY ADVICE)
M: Andrew Morton <akpm@linux-foundation.org>
M: Liam R. Howlett <Liam.Howlett@oracle.com>
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
M: Lorenzo Stoakes <ljs@kernel.org>
M: David Hildenbrand <david@kernel.org>
R: Vlastimil Babka <vbabka@kernel.org>
R: Jann Horn <jannh@google.com>
@ -22267,6 +22267,16 @@ L: linux-wireless@vger.kernel.org
S: Orphan
F: drivers/net/wireless/rsi/
RELAY
M: Andrew Morton <akpm@linux-foundation.org>
M: Jens Axboe <axboe@kernel.dk>
M: Jason Xing <kernelxing@tencent.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/filesystems/relay.rst
F: include/linux/relay.h
F: kernel/relay.c
REGISTER MAP ABSTRACTION
M: Mark Brown <broonie@kernel.org>
L: linux-kernel@vger.kernel.org
@ -23156,7 +23166,7 @@ K: \b(?i:rust)\b
RUST [ALLOC]
M: Danilo Krummrich <dakr@kernel.org>
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Lorenzo Stoakes <ljs@kernel.org>
R: Vlastimil Babka <vbabka@kernel.org>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
R: Uladzislau Rezki <urezki@gmail.com>
@ -24333,11 +24343,12 @@ F: drivers/nvmem/layouts/sl28vpd.c
SLAB ALLOCATOR
M: Vlastimil Babka <vbabka@kernel.org>
M: Harry Yoo <harry.yoo@oracle.com>
M: Andrew Morton <akpm@linux-foundation.org>
R: Hao Li <hao.li@linux.dev>
R: Christoph Lameter <cl@gentwo.org>
R: David Rientjes <rientjes@google.com>
R: Roman Gushchin <roman.gushchin@linux.dev>
R: Harry Yoo <harry.yoo@oracle.com>
L: linux-mm@kvack.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/vbabka/slab.git

View File

@ -549,7 +549,7 @@ static ssize_t bd_stat_show(struct device *dev, struct device_attribute *attr,
return ret;
}
static ssize_t writeback_compressed_store(struct device *dev,
static ssize_t compressed_writeback_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
@ -564,12 +564,12 @@ static ssize_t writeback_compressed_store(struct device *dev,
return -EBUSY;
}
zram->wb_compressed = val;
zram->compressed_wb = val;
return len;
}
static ssize_t writeback_compressed_show(struct device *dev,
static ssize_t compressed_writeback_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
@ -577,7 +577,7 @@ static ssize_t writeback_compressed_show(struct device *dev,
struct zram *zram = dev_to_zram(dev);
guard(rwsem_read)(&zram->dev_lock);
val = zram->wb_compressed;
val = zram->compressed_wb;
return sysfs_emit(buf, "%d\n", val);
}
@ -946,7 +946,7 @@ static int zram_writeback_complete(struct zram *zram, struct zram_wb_req *req)
goto out;
}
if (zram->wb_compressed) {
if (zram->compressed_wb) {
/*
* ZRAM_WB slots get freed, we need to preserve data required
* for read decompression.
@ -960,7 +960,7 @@ static int zram_writeback_complete(struct zram *zram, struct zram_wb_req *req)
set_slot_flag(zram, index, ZRAM_WB);
set_slot_handle(zram, index, req->blk_idx);
if (zram->wb_compressed) {
if (zram->compressed_wb) {
if (huge)
set_slot_flag(zram, index, ZRAM_HUGE);
set_slot_size(zram, index, size);
@ -1100,7 +1100,7 @@ static int zram_writeback_slots(struct zram *zram,
*/
if (!test_slot_flag(zram, index, ZRAM_PP_SLOT))
goto next;
if (zram->wb_compressed)
if (zram->compressed_wb)
err = read_from_zspool_raw(zram, req->page, index);
else
err = read_from_zspool(zram, req->page, index);
@ -1429,7 +1429,7 @@ static void zram_async_read_endio(struct bio *bio)
*
* Keep the existing behavior for now.
*/
if (zram->wb_compressed == false) {
if (zram->compressed_wb == false) {
/* No decompression needed, complete the parent IO */
bio_endio(req->parent);
bio_put(bio);
@ -1508,7 +1508,7 @@ static int read_from_bdev_sync(struct zram *zram, struct page *page, u32 index,
flush_work(&req.work);
destroy_work_on_stack(&req.work);
if (req.error || zram->wb_compressed == false)
if (req.error || zram->compressed_wb == false)
return req.error;
return decompress_bdev_page(zram, page, index);
@ -3007,7 +3007,7 @@ static DEVICE_ATTR_WO(writeback);
static DEVICE_ATTR_RW(writeback_limit);
static DEVICE_ATTR_RW(writeback_limit_enable);
static DEVICE_ATTR_RW(writeback_batch_size);
static DEVICE_ATTR_RW(writeback_compressed);
static DEVICE_ATTR_RW(compressed_writeback);
#endif
#ifdef CONFIG_ZRAM_MULTI_COMP
static DEVICE_ATTR_RW(recomp_algorithm);
@ -3031,7 +3031,7 @@ static struct attribute *zram_disk_attrs[] = {
&dev_attr_writeback_limit.attr,
&dev_attr_writeback_limit_enable.attr,
&dev_attr_writeback_batch_size.attr,
&dev_attr_writeback_compressed.attr,
&dev_attr_compressed_writeback.attr,
#endif
&dev_attr_io_stat.attr,
&dev_attr_mm_stat.attr,
@ -3091,7 +3091,7 @@ static int zram_add(void)
init_rwsem(&zram->dev_lock);
#ifdef CONFIG_ZRAM_WRITEBACK
zram->wb_batch_size = 32;
zram->wb_compressed = false;
zram->compressed_wb = false;
#endif
/* gendisk structure */

View File

@ -133,7 +133,7 @@ struct zram {
#ifdef CONFIG_ZRAM_WRITEBACK
struct file *backing_dev;
bool wb_limit_enable;
bool wb_compressed;
bool compressed_wb;
u32 wb_batch_size;
u64 bd_wb_limit;
struct block_device *bdev;

View File

@ -3514,26 +3514,21 @@ static inline bool ptlock_init(struct ptdesc *ptdesc) { return true; }
static inline void ptlock_free(struct ptdesc *ptdesc) {}
#endif /* defined(CONFIG_SPLIT_PTE_PTLOCKS) */
static inline unsigned long ptdesc_nr_pages(const struct ptdesc *ptdesc)
{
return compound_nr(ptdesc_page(ptdesc));
}
static inline void __pagetable_ctor(struct ptdesc *ptdesc)
{
pg_data_t *pgdat = NODE_DATA(memdesc_nid(ptdesc->pt_flags));
struct folio *folio = ptdesc_folio(ptdesc);
__SetPageTable(ptdesc_page(ptdesc));
mod_node_page_state(pgdat, NR_PAGETABLE, ptdesc_nr_pages(ptdesc));
__folio_set_pgtable(folio);
lruvec_stat_add_folio(folio, NR_PAGETABLE);
}
static inline void pagetable_dtor(struct ptdesc *ptdesc)
{
pg_data_t *pgdat = NODE_DATA(memdesc_nid(ptdesc->pt_flags));
struct folio *folio = ptdesc_folio(ptdesc);
ptlock_free(ptdesc);
__ClearPageTable(ptdesc_page(ptdesc));
mod_node_page_state(pgdat, NR_PAGETABLE, -ptdesc_nr_pages(ptdesc));
__folio_clear_pgtable(folio);
lruvec_stat_sub_folio(folio, NR_PAGETABLE);
}
static inline void pagetable_dtor_free(struct ptdesc *ptdesc)

View File

@ -234,7 +234,7 @@ struct mmu_notifier {
};
/**
* struct mmu_interval_notifier_ops
* struct mmu_interval_notifier_ops - callback for range notification
* @invalidate: Upon return the caller must stop using any SPTEs within this
* range. This function can sleep. Return false only if sleeping
* was required but mmu_notifier_range_blockable(range) is false.
@ -309,8 +309,8 @@ void mmu_interval_notifier_remove(struct mmu_interval_notifier *interval_sub);
/**
* mmu_interval_set_seq - Save the invalidation sequence
* @interval_sub - The subscription passed to invalidate
* @cur_seq - The cur_seq passed to the invalidate() callback
* @interval_sub: The subscription passed to invalidate
* @cur_seq: The cur_seq passed to the invalidate() callback
*
* This must be called unconditionally from the invalidate callback of a
* struct mmu_interval_notifier_ops under the same lock that is used to call
@ -329,8 +329,8 @@ mmu_interval_set_seq(struct mmu_interval_notifier *interval_sub,
/**
* mmu_interval_read_retry - End a read side critical section against a VA range
* interval_sub: The subscription
* seq: The return of the paired mmu_interval_read_begin()
* @interval_sub: The subscription
* @seq: The return of the paired mmu_interval_read_begin()
*
* This MUST be called under a user provided lock that is also held
* unconditionally by op->invalidate() when it calls mmu_interval_set_seq().
@ -338,7 +338,7 @@ mmu_interval_set_seq(struct mmu_interval_notifier *interval_sub,
* Each call should be paired with a single mmu_interval_read_begin() and
* should be used to conclude the read side.
*
* Returns true if an invalidation collided with this critical section, and
* Returns: true if an invalidation collided with this critical section, and
* the caller should retry.
*/
static inline bool
@ -350,20 +350,21 @@ mmu_interval_read_retry(struct mmu_interval_notifier *interval_sub,
/**
* mmu_interval_check_retry - Test if a collision has occurred
* interval_sub: The subscription
* seq: The return of the matching mmu_interval_read_begin()
* @interval_sub: The subscription
* @seq: The return of the matching mmu_interval_read_begin()
*
* This can be used in the critical section between mmu_interval_read_begin()
* and mmu_interval_read_retry(). A return of true indicates an invalidation
* has collided with this critical region and a future
* mmu_interval_read_retry() will return true.
*
* False is not reliable and only suggests a collision may not have
* occurred. It can be called many times and does not have to hold the user
* provided lock.
* and mmu_interval_read_retry().
*
* This call can be used as part of loops and other expensive operations to
* expedite a retry.
* It can be called many times and does not have to hold the user
* provided lock.
*
* Returns: true indicates an invalidation has collided with this critical
* region and a future mmu_interval_read_retry() will return true.
* False is not reliable and only suggests a collision may not have
* occurred.
*/
static inline bool
mmu_interval_check_retry(struct mmu_interval_notifier *interval_sub,

View File

@ -792,7 +792,7 @@ for (bool done = false; !done; done = true) \
/**
* scoped_user_rw_access_size - Start a scoped user read/write access with given size
* @uptr Pointer to the user space address to read from and write to
* @uptr: Pointer to the user space address to read from and write to
* @size: Size of the access starting from @uptr
* @elbl: Error label to goto when the access region is rejected
*
@ -803,7 +803,7 @@ for (bool done = false; !done; done = true) \
/**
* scoped_user_rw_access - Start a scoped user read/write access
* @uptr Pointer to the user space address to read from and write to
* @uptr: Pointer to the user space address to read from and write to
* @elbl: Error label to goto when the access region is rejected
*
* The size of the access starting from @uptr is determined via sizeof(*@uptr)).

View File

@ -1013,6 +1013,7 @@ bool cma_release(struct cma *cma, const struct page *pages,
unsigned long count)
{
struct cma_memrange *cmr;
unsigned long ret = 0;
unsigned long i, pfn;
cmr = find_cma_memrange(cma, pages, count);
@ -1021,7 +1022,9 @@ bool cma_release(struct cma *cma, const struct page *pages,
pfn = page_to_pfn(pages);
for (i = 0; i < count; i++, pfn++)
VM_WARN_ON(!put_page_testzero(pfn_to_page(pfn)));
ret += !put_page_testzero(pfn_to_page(pfn));
WARN(ret, "%lu pages are still in use!\n", ret);
__cma_release_frozen(cma, cmr, pages, count);

View File

@ -1562,8 +1562,13 @@ int damos_walk(struct damon_ctx *ctx, struct damos_walk_control *control)
}
ctx->walk_control = control;
mutex_unlock(&ctx->walk_control_lock);
if (!damon_is_running(ctx))
if (!damon_is_running(ctx)) {
mutex_lock(&ctx->walk_control_lock);
if (ctx->walk_control == control)
ctx->walk_control = NULL;
mutex_unlock(&ctx->walk_control_lock);
return -EINVAL;
}
wait_for_completion(&control->completion);
if (control->canceled)
return -ECANCELED;

View File

@ -3631,6 +3631,7 @@ static int __split_unmapped_folio(struct folio *folio, int new_order,
const bool is_anon = folio_test_anon(folio);
int old_order = folio_order(folio);
int start_order = split_type == SPLIT_TYPE_UNIFORM ? new_order : old_order - 1;
struct folio *old_folio = folio;
int split_order;
/*
@ -3651,12 +3652,16 @@ static int __split_unmapped_folio(struct folio *folio, int new_order,
* uniform split has xas_split_alloc() called before
* irq is disabled to allocate enough memory, whereas
* non-uniform split can handle ENOMEM.
* Use the to-be-split folio, so that a parallel
* folio_try_get() waits on it until xarray is updated
* with after-split folios and the original one is
* unfrozen.
*/
if (split_type == SPLIT_TYPE_UNIFORM)
xas_split(xas, folio, old_order);
else {
if (split_type == SPLIT_TYPE_UNIFORM) {
xas_split(xas, old_folio, old_order);
} else {
xas_set_order(xas, folio->index, split_order);
xas_try_split(xas, folio, old_order);
xas_try_split(xas, old_folio, old_order);
if (xas_error(xas))
return xas_error(xas);
}

View File

@ -3101,7 +3101,7 @@ static __init void *alloc_bootmem(struct hstate *h, int nid, bool node_exact)
* extract the actual node first.
*/
if (m)
listnode = early_pfn_to_nid(PHYS_PFN(virt_to_phys(m)));
listnode = early_pfn_to_nid(PHYS_PFN(__pa(m)));
}
if (m) {
@ -3160,7 +3160,7 @@ found:
* The head struct page is used to get folio information by the HugeTLB
* subsystem like zone id and node id.
*/
memblock_reserved_mark_noinit(virt_to_phys((void *)m + PAGE_SIZE),
memblock_reserved_mark_noinit(__pa((void *)m + PAGE_SIZE),
huge_page_size(h) - PAGE_SIZE);
return 1;

View File

@ -3086,7 +3086,7 @@ static void refill_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes,
if (!local_trylock(&obj_stock.lock)) {
if (pgdat)
mod_objcg_mlstate(objcg, pgdat, idx, nr_bytes);
mod_objcg_mlstate(objcg, pgdat, idx, nr_acct);
nr_pages = nr_bytes >> PAGE_SHIFT;
nr_bytes = nr_bytes & (PAGE_SIZE - 1);
atomic_add(nr_bytes, &objcg->nr_charged_bytes);

View File

@ -146,19 +146,56 @@ static int memfd_luo_preserve_folios(struct file *file,
for (i = 0; i < nr_folios; i++) {
struct memfd_luo_folio_ser *pfolio = &folios_ser[i];
struct folio *folio = folios[i];
unsigned int flags = 0;
err = kho_preserve_folio(folio);
if (err)
goto err_unpreserve;
if (folio_test_dirty(folio))
flags |= MEMFD_LUO_FOLIO_DIRTY;
if (folio_test_uptodate(folio))
flags |= MEMFD_LUO_FOLIO_UPTODATE;
folio_lock(folio);
/*
* A dirty folio is one which has been written to. A clean folio
* is its opposite. Since a clean folio does not carry user
* data, it can be freed by page reclaim under memory pressure.
*
* Saving the dirty flag at prepare() time doesn't work since it
* can change later. Saving it at freeze() also won't work
* because the dirty bit is normally synced at unmap and there
* might still be a mapping of the file at freeze().
*
* To see why this is a problem, say a folio is clean at
* preserve, but gets dirtied later. The pfolio flags will mark
* it as clean. After retrieve, the next kernel might try to
* reclaim this folio under memory pressure, losing user data.
*
* Unconditionally mark it dirty to avoid this problem. This
* comes at the cost of making clean folios un-reclaimable after
* live update.
*/
folio_mark_dirty(folio);
/*
* If the folio is not uptodate, it was fallocated but never
* used. Saving this flag at prepare() doesn't work since it
* might change later when someone uses the folio.
*
* Since we have taken the performance penalty of allocating,
* zeroing, and pinning all the folios in the holes, take a bit
* more and zero all non-uptodate folios too.
*
* NOTE: For someone looking to improve preserve performance,
* this is a good place to look.
*/
if (!folio_test_uptodate(folio)) {
folio_zero_range(folio, 0, folio_size(folio));
flush_dcache_folio(folio);
folio_mark_uptodate(folio);
}
folio_unlock(folio);
pfolio->pfn = folio_pfn(folio);
pfolio->flags = flags;
pfolio->flags = MEMFD_LUO_FOLIO_DIRTY | MEMFD_LUO_FOLIO_UPTODATE;
pfolio->index = folio->index;
}

View File

@ -5,6 +5,10 @@
#include <linux/types.h>
#include <linux/gfp_types.h>
/* Helper macro to avoid gfp flags if they are the default one */
#define __default_gfp(a,...) a
#define default_gfp(...) __default_gfp(__VA_ARGS__ __VA_OPT__(,) GFP_KERNEL)
static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags)
{
return !!(gfp_flags & __GFP_DIRECT_RECLAIM);

View File

@ -68,6 +68,25 @@
__builtin_mul_overflow(__a, __b, __d); \
})
/**
* size_mul() - Calculate size_t multiplication with saturation at SIZE_MAX
* @factor1: first factor
* @factor2: second factor
*
* Returns: calculate @factor1 * @factor2, both promoted to size_t,
* with any overflow causing the return value to be SIZE_MAX. The
* lvalue must be size_t to avoid implicit type conversion.
*/
static inline size_t __must_check size_mul(size_t factor1, size_t factor2)
{
size_t bytes;
if (check_mul_overflow(factor1, factor2, &bytes))
return SIZE_MAX;
return bytes;
}
/**
* array_size() - Calculate size of 2-dimensional array.
*

View File

@ -202,4 +202,13 @@ static inline unsigned int kmem_cache_sheaf_size(struct slab_sheaf *sheaf)
return sheaf->size;
}
#define __alloc_objs(KMALLOC, GFP, TYPE, COUNT) \
({ \
const size_t __obj_size = size_mul(sizeof(TYPE), COUNT); \
(TYPE *)KMALLOC(__obj_size, GFP); \
})
#define kzalloc_obj(P, ...) \
__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1)
#endif /* _TOOLS_SLAB_H */