5fe17b6460
GKI (arm64) relevant 45 out of 232 changes, affecting 66 files +537/-357a74979dce9mm/slab/kvfree_rcu: Switch to WQ_MEM_RECLAIM wq [1 file, +10/-4]4e95072462mm: fix kernel BUG when userfaultfd_move encounters swapcache [1 file, +66/-9]99012b24causerfaultfd: fix PTE unmapping stack-allocated PTE copies [1 file, +10/-10]a5396ee0f5netfilter: nf_conncount: garbage collection is not skipped when jiffies wrap around [1 file, +2/-2]0befa32ac3netfilter: nf_tables: make destruction work queue pernet [3 files, +21/-15]0677a4f3c0Bluetooth: hci_event: Fix enabling passive scanning [1 file, +22/-15]55b098a2beRevert "Bluetooth: hci_core: Fix sleeping function called from invalid context" [6 files, +57/-97]db1e0c0856netfilter: nf_conncount: Fully initialize struct nf_conncount_tuple in insert_tree() [1 file, +2/-0]5c3ca9cb48net_sched: Prevent creation of classes with TC_H_ROOT [1 file, +6/-0]a4d42b5901gre: Fix IPv6 link-local address generation. [1 file, +9/-6]ae5716b463hrtimers: Mark is_migration_base() with __always_inline [1 file, +12/-10]ccdec7ea9apowercap: call put_device() on an error path in powercap_register_control_type() [1 file, +1/-2]aa189c3941futex: Pass in task to futex_queue() [5 files, +15/-9]1207e5d1a2scsi: core: Use GFP_NOIO to avoid circular locking dependency [1 file, +1/-1]3be0408451scsi: ufs: core: Fix error return with query response [1 file, +6/-1]705f251518HID: hid-steam: Fix issues with disabling both gamepad mode and lizard mode [1 file, +3/-3]024d7e006aHID: hid-apple: Apple Magic Keyboard a3203 USB-C support [2 files, +6/-0]3ceaafa26fHID: apple: fix up the F6 key on the Omoton KB066 keyboard [1 file, +3/-0]30a40b5925sched: Clarify wake_up_q()'s write to task->wake_q.next [1 file, +3/-2]a91922e9eathermal/cpufreq_cooling: Remove structure member documentation [1 file, +0/-2]aa3b0ea474arm64: amu: Delay allocating cpumask for AMU FIE support [1 file, +10/-12]f8094625a5Bluetooth: L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd [1 file, +34/-5]4ed43c26b7PCI: pci_ids: add INTEL_HDA_PTL_H [1 file, +1/-0]b349a3d1b1io-wq: backoff when retrying worker creation [1 file, +18/-5]fd903dd301nvme-pci: quirk Acer FA100 for non-uniqueue identifiers [1 file, +2/-0]bfe7f298eenvme: only allow entering LIVE from CONNECTING state [1 file, +0/-2]df2ae00d96fuse: don't truncate cached, mutated symlink [3 files, +22/-6]d1ceef54b2net: Handle napi_schedule() calls from non-interrupt [1 file, +1/-1]7184e99610block: fix 'kmem_cache of name 'bio-108' already exists' [1 file, +1/-1]1936b189faInput: xpad - add 8BitDo SN30 Pro, Hyperkin X91 and Gamesir G7 SE controllers [1 file, +8/-5]e2304bbf87Input: xpad - add multiple supported devices [1 file, +19/-1]d91dd818f1Input: xpad - add support for ZOTAC Gaming Zone [1 file, +2/-0]f7ccf4eb4bInput: xpad - add support for TECNO Pocket Go [1 file, +2/-0]0ff93d8956Input: xpad - rename QH controller to Legion Go S [1 file, +2/-2]87ede08ef2USB: serial: ftdi_sio: add support for Altera USB Blaster 3 [2 files, +27/-0]962912aaf8drm/atomic: Filter out redundant DPMS calls [2 files, +8/-0]454825019dnetmem: prevent TX of unreadable skbs [1 file, +3/-0]f6bbea2ffearm64: mm: Populate vmemmap at the page level if not section aligned [1 file, +4/-1]608bbf7ff5Fix mmu notifiers for range-based invalidates [1 file, +12/-10]a46a9371f8ASoC: ops: Consistently treat platform_max as control value [2 files, +11/-9]77213a424anvme: move error logging from nvme_end_req() to __nvme_end_req() [1 file, +6/-6]f415148282mm/hugetlb: wait for hugetlb folios to be freed [3 files, +23/-0]f87271d21dBluetooth: L2CAP: Fix corrupted list in hci_chan_del [3 files, +58/-98]8f324d9930HID: apple: disable Fn key handling on the Omoton KB066 [1 file, +7/-4]62b9ad7e52fs/netfs/read_collect: add to next->prev_donated [1 file, +1/-1] Changes in 6.12.20 mm/slab/kvfree_rcu: Switch to WQ_MEM_RECLAIM wq mm: fix kernel BUG when userfaultfd_move encounters swapcache userfaultfd: fix PTE unmapping stack-allocated PTE copies fbdev: hyperv_fb: iounmap() the correct memory when removing a device pinctrl: bcm281xx: Fix incorrect regmap max_registers value pinctrl: nuvoton: npcm8xx: Add NULL check in npcm8xx_gpio_fw netfilter: nft_ct: Use __refcount_inc() for per-CPU nft_ct_pcpu_template. ice: do not configure destination override for switchdev ice: fix memory leak in aRFS after reset ice: Fix switchdev slow-path in LAG netfilter: nf_conncount: garbage collection is not skipped when jiffies wrap around netfilter: nf_tables: make destruction work queue pernet sched: address a potential NULL pointer dereference in the GRED scheduler. wifi: iwlwifi: mvm: fix PNVM timeout for non-MSI-X platforms wifi: mac80211: don't queue sdata::work for a non-running sdata wifi: cfg80211: cancel wiphy_work before freeing wiphy Bluetooth: hci_event: Fix enabling passive scanning Revert "Bluetooth: hci_core: Fix sleeping function called from invalid context" net/mlx5: Fill out devlink dev info only for PFs net: dsa: mv88e6xxx: Verify after ATU Load ops net: mctp i3c: Copy headers if cloned net: mctp i2c: Copy headers if cloned netpoll: hold rcu read lock in __netpoll_send_skb() drm/hyperv: Fix address space leak when Hyper-V DRM device is removed fbdev: hyperv_fb: Fix hang in kdump kernel when on Hyper-V Gen 2 VMs fbdev: hyperv_fb: Simplify hvfb_putmem fbdev: hyperv_fb: Allow graceful removal of framebuffer Drivers: hv: vmbus: Don't release fb_mmio resource in vmbus_free_mmio() net/mlx5: handle errors in mlx5_chains_create_table() eth: bnxt: fix truesize for mb-xdp-pass case eth: bnxt: return fail if interface is down in bnxt_queue_mem_alloc() eth: bnxt: do not use BNXT_VNIC_NTUPLE unconditionally in queue restart logic eth: bnxt: do not update checksum in bnxt_xdp_build_skb() eth: bnxt: fix kernel panic in the bnxt_get_queue_stats{rx | tx} eth: bnxt: use page pool for head frags bnxt_en: refactor tpa_info alloc/free into helpers bnxt_en: handle tpa_info in queue API implementation eth: bnxt: fix memory leak in queue reset net: switchdev: Convert blocking notification chain to a raw one net: mctp: unshare packets when reassembling bonding: fix incorrect MAC address setting to receive NS messages selftests: bonding: fix incorrect mac address rtase: Fix improper release of ring list entries in rtase_sw_reset netfilter: nf_conncount: Fully initialize struct nf_conncount_tuple in insert_tree() ipvs: prevent integer overflow in do_ip_vs_get_ctl() net_sched: Prevent creation of classes with TC_H_ROOT netfilter: nft_exthdr: fix offset with ipv4_find_option() gre: Fix IPv6 link-local address generation. net: openvswitch: remove misbehaving actions length check Revert "openvswitch: switch to per-action label counting in conntrack" net/mlx5: HWS, Rightsize bwc matcher priority net/mlx5: Fix incorrect IRQ pool usage when releasing IRQs net/mlx5: Lag, Check shared fdb before creating MultiPort E-Switch net/mlx5: Bridge, fix the crash caused by LAG state check net/mlx5e: Prevent bridge link show failure for non-eswitch-allowed devices nvme-fc: go straight to connecting state when initializing nvme-fc: do not ignore connectivity loss during connecting hrtimers: Mark is_migration_base() with __always_inline powercap: call put_device() on an error path in powercap_register_control_type() btrfs: avoid starting new transaction when cleaning qgroup during subvolume drop futex: Pass in task to futex_queue() iscsi_ibft: Fix UBSAN shift-out-of-bounds warning in ibft_attr_show_nic() sched/debug: Provide slice length for fair tasks platform/x86/intel: pmc: fix ltr decode in pmc_core_ltr_show() drm/amd/display: Fix out-of-bound accesses scsi: core: Use GFP_NOIO to avoid circular locking dependency scsi: ufs: core: Fix error return with query response scsi: qla1280: Fix kernel oops when debug level > 2 ACPI: resource: IRQ override for Eluktronics MECH-17 smb: client: fix noisy when tree connecting to DFS interlink targets alpha/elf: Fix misc/setarch test of util-linux by removing 32bit support vboxsf: fix building with GCC 15 selftests: always check mask returned by statmount(2) sched_ext: selftests/dsp_local_on: Fix sporadic failures HID: intel-ish-hid: fix the length of MNG_SYNC_FW_CLOCK in doorbell HID: intel-ish-hid: Send clock sync message immediately after reset HID: ignore non-functional sensor in HP 5MP Camera HID: hid-steam: Fix issues with disabling both gamepad mode and lizard mode usb: phy: generic: Use proper helper for property detection HID: intel-ish-hid: ipc: Add Panther Lake PCI device IDs HID: topre: Fix n-key rollover on Realforce R3S TKL boards selftests/cgroup: use bash in test_cpuset_v1_hp.sh HID: hid-apple: Apple Magic Keyboard a3203 USB-C support HID: apple: fix up the F6 key on the Omoton KB066 keyboard btrfs: fix two misuses of folio_shift() objtool: Ignore dangling jump table entries sched: Clarify wake_up_q()'s write to task->wake_q.next platform/x86: thinkpad_acpi: Fix invalid fan speed on ThinkPad X120e platform/x86: thinkpad_acpi: Support for V9 DYTC platform profiles platform/x86: int3472: Use correct type for "polarity", call it gpio_flags platform/x86: int3472: Call "reset" GPIO "enable" for INT347E s390/cio: Fix CHPID "configure" attribute caching thermal/cpufreq_cooling: Remove structure member documentation LoongArch: Fix kernel_page_present() for KPRANGE/XKPRANGE LoongArch: KVM: Set host with kernel mode when switch to VM mode arm64: amu: Delay allocating cpumask for AMU FIE support Xen/swiotlb: mark xen_swiotlb_fixup() __init Bluetooth: L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd drm/tests: hdmi: Remove redundant assignments drm/tests: hdmi: Reorder DRM entities variables assignment drm/tests: hdmi: Fix recursive locking selftests/bpf: Fix invalid flag of recv() ASoC: Intel: sof_sdw: Add lookup of quirk using PCI subsystem ID ASoC: Intel: sof_sdw: Add quirk for Asus Zenbook S14 ASoC: Intel: soc-acpi-intel-mtl-match: declare adr as ull ASoC: simple-card-utils.c: add missing dlc->of_node ALSA: hda/realtek: Limit mic boost on Positivo ARN50 ASoC: rsnd: indicate unsupported clock rate ASoC: rsnd: don't indicate warning on rsnd_kctrl_accept_runtime() ASoC: rsnd: adjust convert rate limitation ASoC: arizona/madera: use fsleep() in up/down DAPM event delays. ASoC: SOF: Intel: hda: add softdep pre to snd-hda-codec-hdmi module PCI: pci_ids: add INTEL_HDA_PTL_H ALSA: hda: intel-dsp-config: Add PTL-H support ASoC: SOF: Intel: pci-ptl: Add support for PTL-H ALSA: hda: hda-intel: add Panther Lake-H support ASoC: SOF: amd: Add post_fw_run_delay ACP quirk ASoC: SOF: amd: Handle IPC replies before FW_BOOT_COMPLETE net: wwan: mhi_wwan_mbim: Silence sequence number glitch errors io-wq: backoff when retrying worker creation nvme-pci: quirk Acer FA100 for non-uniqueue identifiers nvmet-rdma: recheck queue state is LIVE in state lock in recv done apple-nvme: Release power domains when probe fails cifs: Treat unhandled directory name surrogate reparse points as mount directory nodes sctp: Fix undefined behavior in left shift operation nvme: only allow entering LIVE from CONNECTING state phy: ti: gmii-sel: Do not use syscon helper to build regmap ASoC: tas2770: Fix volume scale ASoC: tas2764: Fix power control mask ASoC: tas2764: Set the SDOUT polarity correctly fuse: don't truncate cached, mutated symlink ASoC: dapm-graph: set fill colour of turned on nodes ASoC: SOF: Intel: don't check number of sdw links when set dmic_fixup drm/vkms: Round fixp2int conversion in lerp_u16 perf/x86/intel: Use better start period for frequency mode x86/of: Don't use DTB for SMP setup if ACPI is enabled x86/irq: Define trace events conditionally perf/x86/rapl: Add support for Intel Arrow Lake U mptcp: safety check before fallback drm/nouveau: Do not override forced connector status net: Handle napi_schedule() calls from non-interrupt block: fix 'kmem_cache of name 'bio-108' already exists' vhost: return task creation error instead of NULL cifs: Validate content of WSL reparse point buffers cifs: Throw -EOPNOTSUPP error on unsupported reparse point type from parse_reparse_point() Input: goodix-berlin - fix vddio regulator references Input: ads7846 - fix gpiod allocation Input: iqs7222 - preserve system status register Input: xpad - add 8BitDo SN30 Pro, Hyperkin X91 and Gamesir G7 SE controllers Input: xpad - add multiple supported devices Input: xpad - add support for ZOTAC Gaming Zone Input: xpad - add support for TECNO Pocket Go Input: xpad - rename QH controller to Legion Go S Input: i8042 - swap old quirk combination with new quirk for NHxxRZQ Input: i8042 - add required quirks for missing old boardnames Input: i8042 - swap old quirk combination with new quirk for several devices Input: i8042 - swap old quirk combination with new quirk for more devices USB: serial: ftdi_sio: add support for Altera USB Blaster 3 USB: serial: option: add Telit Cinterion FE990B compositions USB: serial: option: fix Telit Cinterion FE990A name USB: serial: option: match on interface class for Telit FN990B rust: lockdep: Remove support for dynamically allocated LockClassKeys rust: remove leftover mentions of the `alloc` crate rust: alloc: satisfy POSIX alignment requirement rust: Disallow BTF generation with Rust + LTO rust: init: fix `Zeroable` implementation for `Option<NonNull<T>>` and `Option<KBox<T>>` x86/microcode/AMD: Fix out-of-bounds on systems with CPU-less NUMA nodes spi: microchip-core: prevent RX overflows when transmit size > FIFO size drm/i915/cdclk: Do cdclk post plane programming later drm/panic: use `div_ceil` to clean Clippy warning drm/panic: fix overindented list items in documentation drm/atomic: Filter out redundant DPMS calls drm/dp_mst: Fix locking when skipping CSN before topology probing drm/amdgpu: NULL-check BO's backing store when determining GFX12 PTE flags drm/amd/amdkfd: Evict all queues even HWS remove queue failed drm/amdgpu/display: Allow DCC for video formats on GFX12 drm/amd/display: Disable unneeded hpd interrupts during dm_init drm/amd/display: fix default brightness drm/amd/display: fix missing .is_two_pixels_per_container drm/amd/display: Restore correct backlight brightness after a GPU reset drm/amd/display: Assign normalized_pix_clk when color depth = 14 drm/amd/display: Fix slab-use-after-free on hdcp_work ksmbd: fix use-after-free in ksmbd_free_work_struct ksmbd: prevent connection release during oplock break notification clk: samsung: update PLL locktime for PLL142XX used on FSD platform clk: samsung: gs101: fix synchronous external abort in samsung_clk_save() ASoC: Intel: sof_sdw: Fix unlikely uninitialized variable use in create_sdw_dailinks() ASoC: amd: yc: Support mic on another Lenovo ThinkPad E16 Gen 2 model netmem: prevent TX of unreadable skbs dm-flakey: Fix memory corruption in optional corrupt_bio_byte feature arm64: mm: Populate vmemmap at the page level if not section aligned Fix mmu notifiers for range-based invalidates qlcnic: fix memory leak issues in qlcnic_sriov_common.c smb: client: fix regression with guest option net: phy: nxp-c45-tja11xx: add TJA112X PHY configuration errata net: phy: nxp-c45-tja11xx: add TJA112XB SGMII PCS restart errata sched_ext: Validate prev_cpu in scx_bpf_select_cpu_dfl() ASoC: ops: Consistently treat platform_max as control value rust: error: add missing newline to pr_warn! calls drm/gma500: Add NULL check for pci_gfx_root in mid_get_vbt_data() ASoC: cs42l43: Fix maximum ADC Volume rust: init: add missing newline to pr_info! calls ASoC: rt722-sdca: add missing readable registers drm/xe: cancel pending job timer before freeing scheduler drm/xe: Release guc ids before cancelling work drm/xe/userptr: Fix an incorrect assert drm/xe/pm: Temporarily disable D3Cold on BMG nvme: move error logging from nvme_end_req() to __nvme_end_req() ASoC: codecs: wm0010: Fix error handling path in wm0010_spi_probe() drm/i915: Increase I915_PARAM_MMAP_GTT_VERSION version to indicate support for partial mmaps scripts: generate_rust_analyzer: add missing macros deps scripts: generate_rust_analyzer: add missing include_dirs scripts: generate_rust_analyzer: add uapi crate block: change blk_mq_add_to_batch() third argument type to bool cifs: Fix integer overflow while processing acregmax mount option cifs: Fix integer overflow while processing acdirmax mount option cifs: Fix integer overflow while processing actimeo mount option cifs: Fix integer overflow while processing closetimeo mount option x86/vmware: Parse MP tables for SEV-SNP enabled guests under VMware hypervisors i2c: ali1535: Fix an error handling path in ali1535_probe() i2c: ali15x3: Fix an error handling path in ali15x3_probe() i2c: sis630: Fix an error handling path in sis630_probe() mm/hugetlb: wait for hugetlb folios to be freed smb3: add support for IAKerb smb: client: Fix match_session bug preventing session reuse sched_ext: selftests/dsp_local_on: Fix selftest on UP systems tools/sched_ext: Add helper to check task migration state Bluetooth: L2CAP: Fix corrupted list in hci_chan_del nvme-fc: rely on state transitions to handle connectivity loss HID: apple: disable Fn key handling on the Omoton KB066 fs/netfs/read_collect: add to next->prev_donated Linux 6.12.20 Change-Id: I5a7652336baaa64b019b6306f78ee5f96aa829aa Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
656 lines
20 KiB
C
656 lines
20 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/mm/page_isolation.c
|
|
*/
|
|
|
|
#include <linux/mm.h>
|
|
#include <linux/page-isolation.h>
|
|
#include <linux/pageblock-flags.h>
|
|
#include <linux/memory.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/page_owner.h>
|
|
#include <linux/page_pinner.h>
|
|
#include <linux/migrate.h>
|
|
#include "internal.h"
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/page_isolation.h>
|
|
|
|
/*
|
|
* This function checks whether the range [start_pfn, end_pfn) includes
|
|
* unmovable pages or not. The range must fall into a single pageblock and
|
|
* consequently belong to a single zone.
|
|
*
|
|
* PageLRU check without isolation or lru_lock could race so that
|
|
* MIGRATE_MOVABLE block might include unmovable pages. And __PageMovable
|
|
* check without lock_page also may miss some movable non-lru pages at
|
|
* race condition. So you can't expect this function should be exact.
|
|
*
|
|
* Returns a page without holding a reference. If the caller wants to
|
|
* dereference that page (e.g., dumping), it has to make sure that it
|
|
* cannot get removed (e.g., via memory unplug) concurrently.
|
|
*
|
|
*/
|
|
static struct page *has_unmovable_pages(unsigned long start_pfn, unsigned long end_pfn,
|
|
int migratetype, int flags)
|
|
{
|
|
struct page *page = pfn_to_page(start_pfn);
|
|
struct zone *zone = page_zone(page);
|
|
unsigned long pfn;
|
|
|
|
VM_BUG_ON(pageblock_start_pfn(start_pfn) !=
|
|
pageblock_start_pfn(end_pfn - 1));
|
|
|
|
if (is_migrate_cma_page(page)) {
|
|
/*
|
|
* CMA allocations (alloc_contig_range) really need to mark
|
|
* isolate CMA pageblocks even when they are not movable in fact
|
|
* so consider them movable here.
|
|
*/
|
|
if (is_migrate_cma(migratetype))
|
|
return NULL;
|
|
|
|
return page;
|
|
}
|
|
|
|
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
|
|
page = pfn_to_page(pfn);
|
|
|
|
/*
|
|
* Both, bootmem allocations and memory holes are marked
|
|
* PG_reserved and are unmovable. We can even have unmovable
|
|
* allocations inside ZONE_MOVABLE, for example when
|
|
* specifying "movablecore".
|
|
*/
|
|
if (PageReserved(page))
|
|
return page;
|
|
|
|
/*
|
|
* If the zone is movable and we have ruled out all reserved
|
|
* pages then it should be reasonably safe to assume the rest
|
|
* is movable.
|
|
*/
|
|
if (zone_idx(zone) == ZONE_MOVABLE)
|
|
continue;
|
|
|
|
/*
|
|
* Hugepages are not in LRU lists, but they're movable.
|
|
* THPs are on the LRU, but need to be counted as #small pages.
|
|
* We need not scan over tail pages because we don't
|
|
* handle each tail page individually in migration.
|
|
*/
|
|
if (PageHuge(page) || PageTransCompound(page)) {
|
|
struct folio *folio = page_folio(page);
|
|
unsigned int skip_pages;
|
|
|
|
if (PageHuge(page)) {
|
|
if (!hugepage_migration_supported(folio_hstate(folio)))
|
|
return page;
|
|
} else if (!folio_test_lru(folio) && !__folio_test_movable(folio)) {
|
|
return page;
|
|
}
|
|
|
|
skip_pages = folio_nr_pages(folio) - folio_page_idx(folio, page);
|
|
pfn += skip_pages - 1;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* We can't use page_count without pin a page
|
|
* because another CPU can free compound page.
|
|
* This check already skips compound tails of THP
|
|
* because their page->_refcount is zero at all time.
|
|
*/
|
|
if (!page_ref_count(page)) {
|
|
if (PageBuddy(page))
|
|
pfn += (1 << buddy_order(page)) - 1;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* The HWPoisoned page may be not in buddy system, and
|
|
* page_count() is not 0.
|
|
*/
|
|
if ((flags & MEMORY_OFFLINE) && PageHWPoison(page))
|
|
continue;
|
|
|
|
/*
|
|
* We treat all PageOffline() pages as movable when offlining
|
|
* to give drivers a chance to decrement their reference count
|
|
* in MEM_GOING_OFFLINE in order to indicate that these pages
|
|
* can be offlined as there are no direct references anymore.
|
|
* For actually unmovable PageOffline() where the driver does
|
|
* not support this, we will fail later when trying to actually
|
|
* move these pages that still have a reference count > 0.
|
|
* (false negatives in this function only)
|
|
*/
|
|
if ((flags & MEMORY_OFFLINE) && PageOffline(page))
|
|
continue;
|
|
|
|
if (__PageMovable(page) || PageLRU(page))
|
|
continue;
|
|
|
|
/*
|
|
* If there are RECLAIMABLE pages, we need to check
|
|
* it. But now, memory offline itself doesn't call
|
|
* shrink_node_slabs() and it still to be fixed.
|
|
*/
|
|
return page;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* This function set pageblock migratetype to isolate if no unmovable page is
|
|
* present in [start_pfn, end_pfn). The pageblock must intersect with
|
|
* [start_pfn, end_pfn).
|
|
*/
|
|
static int set_migratetype_isolate(struct page *page, int migratetype, int isol_flags,
|
|
unsigned long start_pfn, unsigned long end_pfn)
|
|
{
|
|
struct zone *zone = page_zone(page);
|
|
struct page *unmovable;
|
|
unsigned long flags;
|
|
unsigned long check_unmovable_start, check_unmovable_end;
|
|
|
|
if (PageUnaccepted(page))
|
|
accept_page(page);
|
|
|
|
spin_lock_irqsave(&zone->lock, flags);
|
|
|
|
/*
|
|
* We assume the caller intended to SET migrate type to isolate.
|
|
* If it is already set, then someone else must have raced and
|
|
* set it before us.
|
|
*/
|
|
if (is_migrate_isolate_page(page)) {
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/*
|
|
* FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
|
|
* We just check MOVABLE pages.
|
|
*
|
|
* Pass the intersection of [start_pfn, end_pfn) and the page's pageblock
|
|
* to avoid redundant checks.
|
|
*/
|
|
check_unmovable_start = max(page_to_pfn(page), start_pfn);
|
|
check_unmovable_end = min(pageblock_end_pfn(page_to_pfn(page)),
|
|
end_pfn);
|
|
|
|
unmovable = has_unmovable_pages(check_unmovable_start, check_unmovable_end,
|
|
migratetype, isol_flags);
|
|
if (!unmovable) {
|
|
if (!move_freepages_block_isolate(zone, page, MIGRATE_ISOLATE)) {
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
return -EBUSY;
|
|
}
|
|
zone->nr_isolate_pageblock++;
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
return 0;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
if (isol_flags & REPORT_FAILURE) {
|
|
/*
|
|
* printk() with zone->lock held will likely trigger a
|
|
* lockdep splat, so defer it here.
|
|
*/
|
|
dump_page(unmovable, "unmovable page");
|
|
}
|
|
|
|
return -EBUSY;
|
|
}
|
|
|
|
static void unset_migratetype_isolate(struct page *page, int migratetype)
|
|
{
|
|
struct zone *zone;
|
|
unsigned long flags;
|
|
bool isolated_page = false;
|
|
unsigned int order;
|
|
struct page *buddy;
|
|
|
|
zone = page_zone(page);
|
|
spin_lock_irqsave(&zone->lock, flags);
|
|
if (!is_migrate_isolate_page(page))
|
|
goto out;
|
|
|
|
/*
|
|
* Because freepage with more than pageblock_order on isolated
|
|
* pageblock is restricted to merge due to freepage counting problem,
|
|
* it is possible that there is free buddy page.
|
|
* move_freepages_block() doesn't care of merge so we need other
|
|
* approach in order to merge them. Isolation and free will make
|
|
* these pages to be merged.
|
|
*/
|
|
if (PageBuddy(page)) {
|
|
order = buddy_order(page);
|
|
if (order >= pageblock_order && order < MAX_PAGE_ORDER) {
|
|
buddy = find_buddy_page_pfn(page, page_to_pfn(page),
|
|
order, NULL);
|
|
if (buddy && !is_migrate_isolate_page(buddy)) {
|
|
isolated_page = !!__isolate_free_page(page, order);
|
|
/*
|
|
* Isolating a free page in an isolated pageblock
|
|
* is expected to always work as watermarks don't
|
|
* apply here.
|
|
*/
|
|
VM_WARN_ON(!isolated_page);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we isolate freepage with more than pageblock_order, there
|
|
* should be no freepage in the range, so we could avoid costly
|
|
* pageblock scanning for freepage moving.
|
|
*
|
|
* We didn't actually touch any of the isolated pages, so place them
|
|
* to the tail of the freelist. This is an optimization for memory
|
|
* onlining - just onlined memory won't immediately be considered for
|
|
* allocation.
|
|
*/
|
|
if (!isolated_page) {
|
|
/*
|
|
* Isolating this block already succeeded, so this
|
|
* should not fail on zone boundaries.
|
|
*/
|
|
WARN_ON_ONCE(!move_freepages_block_isolate(zone, page, migratetype));
|
|
} else {
|
|
set_pageblock_migratetype(page, migratetype);
|
|
__putback_isolated_page(page, order, migratetype);
|
|
}
|
|
zone->nr_isolate_pageblock--;
|
|
out:
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
}
|
|
|
|
static inline struct page *
|
|
__first_valid_page(unsigned long pfn, unsigned long nr_pages)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nr_pages; i++) {
|
|
struct page *page;
|
|
|
|
page = pfn_to_online_page(pfn + i);
|
|
if (!page)
|
|
continue;
|
|
return page;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* isolate_single_pageblock() -- tries to isolate a pageblock that might be
|
|
* within a free or in-use page.
|
|
* @boundary_pfn: pageblock-aligned pfn that a page might cross
|
|
* @flags: isolation flags
|
|
* @gfp_flags: GFP flags used for migrating pages
|
|
* @isolate_before: isolate the pageblock before the boundary_pfn
|
|
* @skip_isolation: the flag to skip the pageblock isolation in second
|
|
* isolate_single_pageblock()
|
|
* @migratetype: migrate type to set in error recovery.
|
|
*
|
|
* Free and in-use pages can be as big as MAX_PAGE_ORDER and contain more than one
|
|
* pageblock. When not all pageblocks within a page are isolated at the same
|
|
* time, free page accounting can go wrong. For example, in the case of
|
|
* MAX_PAGE_ORDER = pageblock_order + 1, a MAX_PAGE_ORDER page has two
|
|
* pagelbocks.
|
|
* [ MAX_PAGE_ORDER ]
|
|
* [ pageblock0 | pageblock1 ]
|
|
* When either pageblock is isolated, if it is a free page, the page is not
|
|
* split into separate migratetype lists, which is supposed to; if it is an
|
|
* in-use page and freed later, __free_one_page() does not split the free page
|
|
* either. The function handles this by splitting the free page or migrating
|
|
* the in-use page then splitting the free page.
|
|
*/
|
|
static int isolate_single_pageblock(unsigned long boundary_pfn, int flags,
|
|
gfp_t gfp_flags, bool isolate_before, bool skip_isolation,
|
|
int migratetype)
|
|
{
|
|
unsigned long start_pfn;
|
|
unsigned long isolate_pageblock;
|
|
unsigned long pfn;
|
|
struct zone *zone;
|
|
int ret;
|
|
|
|
VM_BUG_ON(!pageblock_aligned(boundary_pfn));
|
|
|
|
if (isolate_before)
|
|
isolate_pageblock = boundary_pfn - pageblock_nr_pages;
|
|
else
|
|
isolate_pageblock = boundary_pfn;
|
|
|
|
/*
|
|
* scan at the beginning of MAX_ORDER_NR_PAGES aligned range to avoid
|
|
* only isolating a subset of pageblocks from a bigger than pageblock
|
|
* free or in-use page. Also make sure all to-be-isolated pageblocks
|
|
* are within the same zone.
|
|
*/
|
|
zone = page_zone(pfn_to_page(isolate_pageblock));
|
|
start_pfn = max(ALIGN_DOWN(isolate_pageblock, MAX_ORDER_NR_PAGES),
|
|
zone->zone_start_pfn);
|
|
|
|
if (skip_isolation) {
|
|
int mt __maybe_unused = get_pageblock_migratetype(pfn_to_page(isolate_pageblock));
|
|
|
|
VM_BUG_ON(!is_migrate_isolate(mt));
|
|
} else {
|
|
ret = set_migratetype_isolate(pfn_to_page(isolate_pageblock), migratetype,
|
|
flags, isolate_pageblock, isolate_pageblock + pageblock_nr_pages);
|
|
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Bail out early when the to-be-isolated pageblock does not form
|
|
* a free or in-use page across boundary_pfn:
|
|
*
|
|
* 1. isolate before boundary_pfn: the page after is not online
|
|
* 2. isolate after boundary_pfn: the page before is not online
|
|
*
|
|
* This also ensures correctness. Without it, when isolate after
|
|
* boundary_pfn and [start_pfn, boundary_pfn) are not online,
|
|
* __first_valid_page() will return unexpected NULL in the for loop
|
|
* below.
|
|
*/
|
|
if (isolate_before) {
|
|
if (!pfn_to_online_page(boundary_pfn))
|
|
return 0;
|
|
} else {
|
|
if (!pfn_to_online_page(boundary_pfn - 1))
|
|
return 0;
|
|
}
|
|
|
|
for (pfn = start_pfn; pfn < boundary_pfn;) {
|
|
struct page *page = __first_valid_page(pfn, boundary_pfn - pfn);
|
|
|
|
VM_BUG_ON(!page);
|
|
pfn = page_to_pfn(page);
|
|
|
|
if (PageUnaccepted(page)) {
|
|
pfn += MAX_ORDER_NR_PAGES;
|
|
continue;
|
|
}
|
|
|
|
if (PageBuddy(page)) {
|
|
int order = buddy_order(page);
|
|
|
|
/* move_freepages_block_isolate() handled this */
|
|
VM_WARN_ON_ONCE(pfn + (1 << order) > boundary_pfn);
|
|
|
|
pfn += 1UL << order;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If a compound page is straddling our block, attempt
|
|
* to migrate it out of the way.
|
|
*
|
|
* We don't have to worry about this creating a large
|
|
* free page that straddles into our block: gigantic
|
|
* pages are freed as order-0 chunks, and LRU pages
|
|
* (currently) do not exceed pageblock_order.
|
|
*
|
|
* The block of interest has already been marked
|
|
* MIGRATE_ISOLATE above, so when migration is done it
|
|
* will free its pages onto the correct freelists.
|
|
*/
|
|
if (PageCompound(page)) {
|
|
struct page *head = compound_head(page);
|
|
unsigned long head_pfn = page_to_pfn(head);
|
|
unsigned long nr_pages = compound_nr(head);
|
|
|
|
if (head_pfn + nr_pages <= boundary_pfn ||
|
|
PageHuge(page)) {
|
|
pfn = head_pfn + nr_pages;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* These pages are movable too, but they're
|
|
* not expected to exceed pageblock_order.
|
|
*
|
|
* Let us know when they do, so we can add
|
|
* proper free and split handling for them.
|
|
*/
|
|
VM_WARN_ON_ONCE_PAGE(PageLRU(page), page);
|
|
VM_WARN_ON_ONCE_PAGE(__PageMovable(page), page);
|
|
|
|
goto failed;
|
|
}
|
|
|
|
pfn++;
|
|
}
|
|
return 0;
|
|
failed:
|
|
/* restore the original migratetype */
|
|
if (!skip_isolation)
|
|
unset_migratetype_isolate(pfn_to_page(isolate_pageblock), migratetype);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/**
|
|
* start_isolate_page_range() - mark page range MIGRATE_ISOLATE
|
|
* @start_pfn: The first PFN of the range to be isolated.
|
|
* @end_pfn: The last PFN of the range to be isolated.
|
|
* @migratetype: Migrate type to set in error recovery.
|
|
* @flags: The following flags are allowed (they can be combined in
|
|
* a bit mask)
|
|
* MEMORY_OFFLINE - isolate to offline (!allocate) memory
|
|
* e.g., skip over PageHWPoison() pages
|
|
* and PageOffline() pages.
|
|
* REPORT_FAILURE - report details about the failure to
|
|
* isolate the range
|
|
* @gfp_flags: GFP flags used for migrating pages that sit across the
|
|
* range boundaries.
|
|
*
|
|
* Making page-allocation-type to be MIGRATE_ISOLATE means free pages in
|
|
* the range will never be allocated. Any free pages and pages freed in the
|
|
* future will not be allocated again. If specified range includes migrate types
|
|
* other than MOVABLE or CMA, this will fail with -EBUSY. For isolating all
|
|
* pages in the range finally, the caller have to free all pages in the range.
|
|
* test_page_isolated() can be used for test it.
|
|
*
|
|
* The function first tries to isolate the pageblocks at the beginning and end
|
|
* of the range, since there might be pages across the range boundaries.
|
|
* Afterwards, it isolates the rest of the range.
|
|
*
|
|
* There is no high level synchronization mechanism that prevents two threads
|
|
* from trying to isolate overlapping ranges. If this happens, one thread
|
|
* will notice pageblocks in the overlapping range already set to isolate.
|
|
* This happens in set_migratetype_isolate, and set_migratetype_isolate
|
|
* returns an error. We then clean up by restoring the migration type on
|
|
* pageblocks we may have modified and return -EBUSY to caller. This
|
|
* prevents two threads from simultaneously working on overlapping ranges.
|
|
*
|
|
* Please note that there is no strong synchronization with the page allocator
|
|
* either. Pages might be freed while their page blocks are marked ISOLATED.
|
|
* A call to drain_all_pages() after isolation can flush most of them. However
|
|
* in some cases pages might still end up on pcp lists and that would allow
|
|
* for their allocation even when they are in fact isolated already. Depending
|
|
* on how strong of a guarantee the caller needs, zone_pcp_disable/enable()
|
|
* might be used to flush and disable pcplist before isolation and enable after
|
|
* unisolation.
|
|
*
|
|
* Return: 0 on success and -EBUSY if any part of range cannot be isolated.
|
|
*/
|
|
int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
|
|
int migratetype, int flags, gfp_t gfp_flags)
|
|
{
|
|
unsigned long pfn;
|
|
struct page *page;
|
|
/* isolation is done at page block granularity */
|
|
unsigned long isolate_start = pageblock_start_pfn(start_pfn);
|
|
unsigned long isolate_end = pageblock_align(end_pfn);
|
|
int ret;
|
|
bool skip_isolation = false;
|
|
|
|
/* isolate [isolate_start, isolate_start + pageblock_nr_pages) pageblock */
|
|
ret = isolate_single_pageblock(isolate_start, flags, gfp_flags, false,
|
|
skip_isolation, migratetype);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (isolate_start == isolate_end - pageblock_nr_pages)
|
|
skip_isolation = true;
|
|
|
|
/* isolate [isolate_end - pageblock_nr_pages, isolate_end) pageblock */
|
|
ret = isolate_single_pageblock(isolate_end, flags, gfp_flags, true,
|
|
skip_isolation, migratetype);
|
|
if (ret) {
|
|
unset_migratetype_isolate(pfn_to_page(isolate_start), migratetype);
|
|
return ret;
|
|
}
|
|
|
|
/* skip isolated pageblocks at the beginning and end */
|
|
for (pfn = isolate_start + pageblock_nr_pages;
|
|
pfn < isolate_end - pageblock_nr_pages;
|
|
pfn += pageblock_nr_pages) {
|
|
page = __first_valid_page(pfn, pageblock_nr_pages);
|
|
if (page && set_migratetype_isolate(page, migratetype, flags,
|
|
start_pfn, end_pfn)) {
|
|
undo_isolate_page_range(isolate_start, pfn, migratetype);
|
|
unset_migratetype_isolate(
|
|
pfn_to_page(isolate_end - pageblock_nr_pages),
|
|
migratetype);
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* undo_isolate_page_range - undo effects of start_isolate_page_range()
|
|
* @start_pfn: The first PFN of the isolated range
|
|
* @end_pfn: The last PFN of the isolated range
|
|
* @migratetype: New migrate type to set on the range
|
|
*
|
|
* This finds every MIGRATE_ISOLATE page block in the given range
|
|
* and switches it to @migratetype.
|
|
*/
|
|
void undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
|
|
int migratetype)
|
|
{
|
|
unsigned long pfn;
|
|
struct page *page;
|
|
unsigned long isolate_start = pageblock_start_pfn(start_pfn);
|
|
unsigned long isolate_end = pageblock_align(end_pfn);
|
|
|
|
for (pfn = isolate_start;
|
|
pfn < isolate_end;
|
|
pfn += pageblock_nr_pages) {
|
|
page = __first_valid_page(pfn, pageblock_nr_pages);
|
|
if (!page || !is_migrate_isolate_page(page))
|
|
continue;
|
|
unset_migratetype_isolate(page, migratetype);
|
|
}
|
|
}
|
|
/*
|
|
* Test all pages in the range is free(means isolated) or not.
|
|
* all pages in [start_pfn...end_pfn) must be in the same zone.
|
|
* zone->lock must be held before call this.
|
|
*
|
|
* Returns the last tested pfn.
|
|
*/
|
|
static unsigned long
|
|
__test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn,
|
|
int flags)
|
|
{
|
|
struct page *page;
|
|
|
|
while (pfn < end_pfn) {
|
|
page = pfn_to_page(pfn);
|
|
if (PageBuddy(page))
|
|
/*
|
|
* If the page is on a free list, it has to be on
|
|
* the correct MIGRATE_ISOLATE freelist. There is no
|
|
* simple way to verify that as VM_BUG_ON(), though.
|
|
*/
|
|
pfn += 1 << buddy_order(page);
|
|
else if ((flags & MEMORY_OFFLINE) && PageHWPoison(page))
|
|
/* A HWPoisoned page cannot be also PageBuddy */
|
|
pfn++;
|
|
else if ((flags & MEMORY_OFFLINE) && PageOffline(page) &&
|
|
!page_count(page))
|
|
/*
|
|
* The responsible driver agreed to skip PageOffline()
|
|
* pages when offlining memory by dropping its
|
|
* reference in MEM_GOING_OFFLINE.
|
|
*/
|
|
pfn++;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return pfn;
|
|
}
|
|
|
|
/**
|
|
* test_pages_isolated - check if pageblocks in range are isolated
|
|
* @start_pfn: The first PFN of the isolated range
|
|
* @end_pfn: The first PFN *after* the isolated range
|
|
* @isol_flags: Testing mode flags
|
|
*
|
|
* This tests if all in the specified range are free.
|
|
*
|
|
* If %MEMORY_OFFLINE is specified in @flags, it will consider
|
|
* poisoned and offlined pages free as well.
|
|
*
|
|
* Caller must ensure the requested range doesn't span zones.
|
|
*
|
|
* Returns 0 if true, -EBUSY if one or more pages are in use.
|
|
*/
|
|
int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
|
|
int isol_flags)
|
|
{
|
|
unsigned long pfn, flags;
|
|
struct page *page;
|
|
struct zone *zone;
|
|
int ret;
|
|
|
|
/*
|
|
* Due to the deferred freeing of hugetlb folios, the hugepage folios may
|
|
* not immediately release to the buddy system. This can cause PageBuddy()
|
|
* to fail in __test_page_isolated_in_pageblock(). To ensure that the
|
|
* hugetlb folios are properly released back to the buddy system, we
|
|
* invoke the wait_for_freed_hugetlb_folios() function to wait for the
|
|
* release to complete.
|
|
*/
|
|
wait_for_freed_hugetlb_folios();
|
|
|
|
/*
|
|
* Note: pageblock_nr_pages != MAX_PAGE_ORDER. Then, chunks of free
|
|
* pages are not aligned to pageblock_nr_pages.
|
|
* Then we just check migratetype first.
|
|
*/
|
|
for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
|
|
page = __first_valid_page(pfn, pageblock_nr_pages);
|
|
if (page && !is_migrate_isolate_page(page))
|
|
break;
|
|
}
|
|
page = __first_valid_page(start_pfn, end_pfn - start_pfn);
|
|
if ((pfn < end_pfn) || !page) {
|
|
ret = -EBUSY;
|
|
goto out;
|
|
}
|
|
|
|
/* Check all pages are free or marked as ISOLATED */
|
|
zone = page_zone(page);
|
|
spin_lock_irqsave(&zone->lock, flags);
|
|
pfn = __test_page_isolated_in_pageblock(start_pfn, end_pfn, isol_flags);
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
|
|
ret = pfn < end_pfn ? -EBUSY : 0;
|
|
|
|
out:
|
|
trace_test_pages_isolated(start_pfn, end_pfn, pfn);
|
|
if (pfn < end_pfn)
|
|
page_pinner_failure_detect(pfn_to_page(pfn));
|
|
|
|
return ret;
|
|
}
|