Files
ack-tegra/mm/page_isolation.c
Greg Kroah-Hartman 5fe17b6460 Merge 6.12.20 into android16-6.12
GKI (arm64) relevant 45 out of 232 changes, affecting 66 files +537/-357
  a74979dce9 mm/slab/kvfree_rcu: Switch to WQ_MEM_RECLAIM wq [1 file, +10/-4]
  4e95072462 mm: fix kernel BUG when userfaultfd_move encounters swapcache [1 file, +66/-9]
  99012b24ca userfaultfd: fix PTE unmapping stack-allocated PTE copies [1 file, +10/-10]
  a5396ee0f5 netfilter: nf_conncount: garbage collection is not skipped when jiffies wrap around [1 file, +2/-2]
  0befa32ac3 netfilter: nf_tables: make destruction work queue pernet [3 files, +21/-15]
  0677a4f3c0 Bluetooth: hci_event: Fix enabling passive scanning [1 file, +22/-15]
  55b098a2be Revert "Bluetooth: hci_core: Fix sleeping function called from invalid context" [6 files, +57/-97]
  db1e0c0856 netfilter: nf_conncount: Fully initialize struct nf_conncount_tuple in insert_tree() [1 file, +2/-0]
  5c3ca9cb48 net_sched: Prevent creation of classes with TC_H_ROOT [1 file, +6/-0]
  a4d42b5901 gre: Fix IPv6 link-local address generation. [1 file, +9/-6]
  ae5716b463 hrtimers: Mark is_migration_base() with __always_inline [1 file, +12/-10]
  ccdec7ea9a powercap: call put_device() on an error path in powercap_register_control_type() [1 file, +1/-2]
  aa189c3941 futex: Pass in task to futex_queue() [5 files, +15/-9]
  1207e5d1a2 scsi: core: Use GFP_NOIO to avoid circular locking dependency [1 file, +1/-1]
  3be0408451 scsi: ufs: core: Fix error return with query response [1 file, +6/-1]
  705f251518 HID: hid-steam: Fix issues with disabling both gamepad mode and lizard mode [1 file, +3/-3]
  024d7e006a HID: hid-apple: Apple Magic Keyboard a3203 USB-C support [2 files, +6/-0]
  3ceaafa26f HID: apple: fix up the F6 key on the Omoton KB066 keyboard [1 file, +3/-0]
  30a40b5925 sched: Clarify wake_up_q()'s write to task->wake_q.next [1 file, +3/-2]
  a91922e9ea thermal/cpufreq_cooling: Remove structure member documentation [1 file, +0/-2]
  aa3b0ea474 arm64: amu: Delay allocating cpumask for AMU FIE support [1 file, +10/-12]
  f8094625a5 Bluetooth: L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd [1 file, +34/-5]
  4ed43c26b7 PCI: pci_ids: add INTEL_HDA_PTL_H [1 file, +1/-0]
  b349a3d1b1 io-wq: backoff when retrying worker creation [1 file, +18/-5]
  fd903dd301 nvme-pci: quirk Acer FA100 for non-uniqueue identifiers [1 file, +2/-0]
  bfe7f298ee nvme: only allow entering LIVE from CONNECTING state [1 file, +0/-2]
  df2ae00d96 fuse: don't truncate cached, mutated symlink [3 files, +22/-6]
  d1ceef54b2 net: Handle napi_schedule() calls from non-interrupt [1 file, +1/-1]
  7184e99610 block: fix 'kmem_cache of name 'bio-108' already exists' [1 file, +1/-1]
  1936b189fa Input: xpad - add 8BitDo SN30 Pro, Hyperkin X91 and Gamesir G7 SE controllers [1 file, +8/-5]
  e2304bbf87 Input: xpad - add multiple supported devices [1 file, +19/-1]
  d91dd818f1 Input: xpad - add support for ZOTAC Gaming Zone [1 file, +2/-0]
  f7ccf4eb4b Input: xpad - add support for TECNO Pocket Go [1 file, +2/-0]
  0ff93d8956 Input: xpad - rename QH controller to Legion Go S [1 file, +2/-2]
  87ede08ef2 USB: serial: ftdi_sio: add support for Altera USB Blaster 3 [2 files, +27/-0]
  962912aaf8 drm/atomic: Filter out redundant DPMS calls [2 files, +8/-0]
  454825019d netmem: prevent TX of unreadable skbs [1 file, +3/-0]
  f6bbea2ffe arm64: mm: Populate vmemmap at the page level if not section aligned [1 file, +4/-1]
  608bbf7ff5 Fix mmu notifiers for range-based invalidates [1 file, +12/-10]
  a46a9371f8 ASoC: ops: Consistently treat platform_max as control value [2 files, +11/-9]
  77213a424a nvme: move error logging from nvme_end_req() to __nvme_end_req() [1 file, +6/-6]
  f415148282 mm/hugetlb: wait for hugetlb folios to be freed [3 files, +23/-0]
  f87271d21d Bluetooth: L2CAP: Fix corrupted list in hci_chan_del [3 files, +58/-98]
  8f324d9930 HID: apple: disable Fn key handling on the Omoton KB066 [1 file, +7/-4]
  62b9ad7e52 fs/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>
2025-04-22 10:18:37 -07:00

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;
}