83fdf72a16
* remotes/origin/tmp-4716ccc : UPSTREAM: media: rc: Fix use-after-free bugs caused by ene_tx_irqsim() ANDROID: incremental fs: Evict inodes before freeing mount data UPSTREAM: ext4: fix kernel BUG in 'ext4_write_inline_data_end()' UPSTREAM: hid: bigben_probe(): validate report count UPSTREAM: HID: bigben: use spinlock to safely schedule workers BACKPORT: of: base: Skip CPU nodes with "fail"/"fail-..." status UPSTREAM: HID: bigben_worker() remove unneeded check on report_field UPSTREAM: HID: bigben: use spinlock to protect concurrent accesses UPSTREAM: hwrng: virtio - add an internal buffer UPSTREAM: ext4: fix another off-by-one fsmap error on 1k block filesystems UPSTREAM: ext4: refuse to create ea block when umounted UPSTREAM: ext4: optimize ea_inode block expansion UPSTREAM: ext4: allocate extended attribute value in vmalloc area BACKPORT: FROMGIT: cgroup: Use separate src/dst nodes when preloading css_sets for migration ANDROID: fs/f2fs: fixup __f2fs_cluster_blocks with F2FS_FS_COMPRESSION UPSTREAM: usb: gadget: f_fs: Ensure ep0req is dequeued before free_request UPSTREAM: KVM: VMX: Execute IBPB on emulated VM-exit when guest has IBRS Revert "iommu: Add gfp parameter to iommu_ops::map" Revert "iommu/amd: Pass gfp flags to iommu_map_page() in amd_iommu_map()" Revert "RDMA/usnic: use iommu_map_atomic() under spin_lock()" Linux 5.4.233 bpf: add missing header file include Revert "net/sched: taprio: make qdisc_leaf() see the per-netdev-queue pfifo child qdiscs" ext4: Fix function prototype mismatch for ext4_feat_ktype wifi: mwifiex: Add missing compatible string for SD8787 uaccess: Add speculation barrier to copy_from_user() mac80211: mesh: embedd mesh_paths and mpp_paths into ieee80211_if_mesh drm/i915/gvt: fix double free bug in split_2MB_gtt_entry alarmtimer: Prevent starvation by small intervals and SIG_IGN powerpc: dts: t208x: Disable 10G on MAC1 and MAC2 can: kvaser_usb: hydra: help gcc-13 to figure out cmd_len KVM: VMX: Execute IBPB on emulated VM-exit when guest has IBRS KVM: x86: Fail emulation during EMULTYPE_SKIP on any exception random: always mix cycle counter in add_latent_entropy() powerpc: dts: t208x: Mark MAC1 and MAC2 as 10G wifi: rtl8xxxu: gen2: Turn on the rate control drm/etnaviv: don't truncate physical page address drm: etnaviv: fix common struct sg_table related issues scatterlist: add generic wrappers for iterating over sgtable objects dma-mapping: add generic helpers for mapping sgtable objects Linux 5.4.232 iommu/amd: Pass gfp flags to iommu_map_page() in amd_iommu_map() net: sched: sch: Fix off by one in htb_activate_prios() ASoC: SOF: Intel: hda-dai: fix possible stream_tag leak nilfs2: fix underflow in second superblock position calculations kvm: initialize all of the kvm_debugregs structure before sending it to userspace i40e: Add checking for null for nlmsg_find_attr() ipv6: Fix tcp socket connection with DSCP. ipv6: Fix datagram socket connection with DSCP. ixgbe: add double of VLAN header when computing the max MTU net: mpls: fix stale pointer if allocation fails during device rename net: stmmac: Restrict warning on disabling DMA store and fwd mode bnxt_en: Fix mqprio and XDP ring checking logic net: stmmac: fix order of dwmac5 FlexPPS parametrization sequence net/usb: kalmia: Don't pass act_len in usb_bulk_msg error path dccp/tcp: Avoid negative sk_forward_alloc by ipv6_pinfo.pktoptions. sctp: sctp_sock_filter(): avoid list_entry() on possibly empty list net: bgmac: fix BCM5358 support by setting correct flags i40e: add double of VLAN header when computing the max MTU ixgbe: allow to increase MTU to 3K with XDP enabled revert "squashfs: harden sanity check in squashfs_read_xattr_id_table" net: Fix unwanted sign extension in netdev_stats_to_stats64() Revert "mm: Always release pages to the buddy allocator in memblock_free_late()." hugetlb: check for undefined shift on 32 bit architectures sched/psi: Fix use-after-free in ep_remove_wait_queue() ALSA: hda/realtek - fixed wrong gpio assigned ALSA: hda/conexant: add a new hda codec SN6180 mmc: mmc_spi: fix error handling in mmc_spi_probe() mmc: sdio: fix possible resource leaks in some error paths ipv4: Fix incorrect route flushing when source address is deleted Revert "ipv4: Fix incorrect route flushing when source address is deleted" xfs: sync lazy sb accounting on quiesce of read-only mounts xfs: prevent UAF in xfs_log_item_in_current_chkpt xfs: fix the forward progress assertion in xfs_iwalk_run_callbacks xfs: ensure inobt record walks always make forward progress xfs: fix missing CoW blocks writeback conversion retry xfs: only relog deferred intent items if free space in the log gets low xfs: expose the log push threshold xfs: periodically relog deferred intent items xfs: change the order in which child and parent defer ops are finished xfs: fix an incore inode UAF in xfs_bui_recover xfs: clean up xfs_bui_item_recover iget/trans_alloc/ilock ordering xfs: clean up bmap intent item recovery checking xfs: xfs_defer_capture should absorb remaining transaction reservation xfs: xfs_defer_capture should absorb remaining block reservations xfs: proper replay of deferred ops queued during log recovery xfs: fix finobt btree block recovery ordering xfs: log new intent items created as part of finishing recovered intent items xfs: refactor xfs_defer_finish_noroll xfs: turn dfp_intent into a xfs_log_item xfs: merge the ->diff_items defer op into ->create_intent xfs: merge the ->log_item defer op into ->create_intent xfs: factor out a xfs_defer_create_intent helper xfs: remove the xfs_inode_log_item_t typedef xfs: remove the xfs_efd_log_item_t typedef xfs: remove the xfs_efi_log_item_t typedef netfilter: nft_tproxy: restrict to prerouting hook btrfs: free device in btrfs_close_devices for a single device filesystem aio: fix mremap after fork null-deref nvme-fc: fix a missing queue put in nvmet_fc_ls_create_association s390/decompressor: specify __decompress() buf len to avoid overflow net: sched: sch: Bounds check priority net: stmmac: do not stop RX_CLK in Rx LPI state for qcs404 SoC net/rose: Fix to not accept on connected socket tools/virtio: fix the vringh test for virtio ring changes ASoC: cs42l56: fix DT probe selftests/bpf: Verify copy_register_state() preserves parent/live fields migrate: hugetlb: check for hugetlb shared PMD in node migration bpf: Always return target ifindex in bpf_fib_lookup nvme-pci: Move enumeration by class to be last in the table arm64: dts: meson-axg: Make mmc host controller interrupts level-sensitive arm64: dts: meson-g12-common: Make mmc host controller interrupts level-sensitive arm64: dts: meson-gx: Make mmc host controller interrupts level-sensitive riscv: Fixup race condition on PG_dcache_clean in flush_icache_pte ceph: flush cap releases when the session is flushed usb: typec: altmodes/displayport: Fix probe pin assign check usb: core: add quirk for Alcor Link AK9563 smartcard reader net: USB: Fix wrong-direction WARNING in plusb.c pinctrl: intel: Restore the pins that used to be in Direct IRQ mode pinctrl: single: fix potential NULL dereference pinctrl: aspeed: Fix confusing types in return value ALSA: pci: lx6464es: fix a debug loop selftests: forwarding: lib: quote the sysctl values rds: rds_rm_zerocopy_callback() use list_first_entry() ice: Do not use WQ_MEM_RECLAIM flag for workqueue ionic: clean interrupt before enabling queue to avoid credit race net: phy: meson-gxl: use MMD access dummy stubs for GXL, internal PHY bonding: fix error checking in bond_debug_reregister() xfrm: fix bug with DSCP copy to v6 from v4 tunnel RDMA/usnic: use iommu_map_atomic() under spin_lock() iommu: Add gfp parameter to iommu_ops::map IB/IPoIB: Fix legacy IPoIB due to wrong number of queues IB/hfi1: Restore allocated resources on failed copyout can: j1939: do not wait 250 ms if the same addr was already claimed tracing: Fix poll() and select() do not work on per_cpu trace_pipe and trace_pipe_raw ALSA: emux: Avoid potential array out-of-bound in snd_emux_xg_control() btrfs: zlib: zero-initialize zlib workspace btrfs: limit device extents to the device size iio:adc:twl6030: Enable measurement of VAC wifi: brcmfmac: Check the count value of channel spec to prevent out-of-bounds reads f2fs: fix to do sanity check on i_extra_isize in is_alive() fbdev: smscufx: fix error handling code in ufx_usb_probe powerpc/imc-pmu: Revert nest_init_lock to being a mutex serial: 8250_dma: Fix DMA Rx rearm race serial: 8250_dma: Fix DMA Rx completion race xprtrdma: Fix regbuf data not freed in rpcrdma_req_create() mm: swap: properly update readahead statistics in unuse_pte_range() nvmem: core: fix cell removal on error Squashfs: fix handling and sanity checking of xattr_ids count mm/swapfile: add cond_resched() in get_swap_pages() fpga: stratix10-soc: Fix return value check in s10_ops_write_init() mm: hugetlb: proc: check for hugetlb shared PMD in /proc/PID/smaps riscv: disable generation of unwind tables parisc: Wire up PTRACE_GETREGS/PTRACE_SETREGS for compat case parisc: Fix return code of pdc_iodc_print() iio:adc:twl6030: Enable measurements of VUSB, VBAT and others iio: adc: berlin2-adc: Add missing of_node_put() in error path iio: hid: fix the retval in accel_3d_capture_sample efi: Accept version 2 of memory attributes table watchdog: diag288_wdt: fix __diag288() inline assembly watchdog: diag288_wdt: do not use stack buffers for hardware data fbcon: Check font dimension limits Input: i8042 - add Clevo PCX0DX to i8042 quirk table Input: i8042 - add TUXEDO devices to i8042 quirk tables Input: i8042 - merge quirk tables Input: i8042 - move __initconst to fix code styling warning vc_screen: move load of struct vc_data pointer in vcs_read() to avoid UAF usb: gadget: f_fs: Fix unbalanced spinlock in __ffs_ep0_queue_wait usb: dwc3: qcom: enable vbus override when in OTG dr-mode usb: dwc3: dwc3-qcom: Fix typo in the dwc3 vbus override API iio: adc: stm32-dfsdm: fill module aliases net/x25: Fix to not accept on connected socket i2c: rk3x: fix a bunch of kernel-doc warnings scsi: iscsi_tcp: Fix UAF during login when accessing the shost ipaddress scsi: target: core: Fix warning on RT kernels efi: fix potential NULL deref in efi_mem_reserve_persistent net: openvswitch: fix flow memory leak in ovs_flow_cmd_new virtio-net: Keep stop() to follow mirror sequence of open() selftests: net: udpgso_bench_tx: Cater for pending datagrams zerocopy benchmarking selftests: net: udpgso_bench: Fix racing bug between the rx/tx programs selftests: net: udpgso_bench_rx/tx: Stop when wrong CLI args are provided selftests: net: udpgso_bench_rx: Fix 'used uninitialized' compiler warning ata: libata: Fix sata_down_spd_limit() when no link speed is reported can: j1939: fix errant WARN_ON_ONCE in j1939_session_deactivate net: phy: meson-gxl: Add generic dummy stubs for MMD register access squashfs: harden sanity check in squashfs_read_xattr_id_table netfilter: br_netfilter: disable sabotage_in hook after first suppression netrom: Fix use-after-free caused by accept on already connected socket fix "direction" argument of iov_iter_kvec() fix iov_iter_bvec() "direction" argument WRITE is "data source", not destination... scsi: Revert "scsi: core: map PQ=1, PDT=other values to SCSI_SCAN_TARGET_PRESENT" arm64: dts: imx8mm: Fix pad control for UART1_DTE_RX ALSA: hda/via: Avoid potential array out-of-bound in add_secret_dac_path() ASoC: Intel: bytcr_rt5651: Drop reference count of ACPI device after use bus: sunxi-rsb: Fix error handling in sunxi_rsb_init() firewire: fix memory leak for payload of request subaction to IEC 61883-1 FCP region Linux 5.4.231 Revert "xprtrdma: Fix regbuf data not freed in rpcrdma_req_create()" usb: host: xhci-plat: add wakeup entry at sysfs Bluetooth: fix null ptr deref on hci_sync_conn_complete_evt ipv6: ensure sane device mtu in tunnels exit: Use READ_ONCE() for all oops/warn limit reads docs: Fix path paste-o for /sys/kernel/warn_count panic: Expose "warn_count" to sysfs panic: Introduce warn_limit panic: Consolidate open-coded panic_on_warn checks exit: Allow oops_limit to be disabled exit: Expose "oops_count" to sysfs exit: Put an upper limit on how often we can oops ia64: make IA64_MCA_RECOVERY bool instead of tristate csky: Fix function name in csky_alignment() and die() h8300: Fix build errors from do_exit() to make_task_dead() transition hexagon: Fix function name in die() objtool: Add a missing comma to avoid string concatenation exit: Add and use make_task_dead. mm: kasan: do not panic if both panic_on_warn and kasan_multishot set panic: unset panic_on_warn inside panic() sysctl: add a new register_sysctl_init() interface dmaengine: imx-sdma: Fix a possible memory leak in sdma_transfer_init blk-cgroup: fix missing pd_online_fn() while activating policy bpf: Skip task with pid=1 in send_signal_common() ARM: dts: imx: Fix pca9547 i2c-mux node name x86/asm: Fix an assembler warning with current binutils clk: Fix pointer casting to prevent oops in devm_clk_release() perf/x86/amd: fix potential integer overflow on shift of a int netfilter: conntrack: unify established states for SCTP paths x86/i8259: Mark legacy PIC interrupts with IRQ_LEVEL block: fix and cleanup bio_check_ro nfsd: Ensure knfsd shuts down when the "nfsd" pseudofs is unmounted Revert "Input: synaptics - switch touchpad on HP Laptop 15-da3001TU to RMI mode" net: mdio-mux-meson-g12a: force internal PHY off on mux switch net: xgene: Move shared header file into include/linux net/phy/mdio-i2c: Move header file to include/linux/mdio net/tg3: resolve deadlock in tg3_reset_task() during EEH thermal: intel: int340x: Add locking to int340x_thermal_get_trip_type() net: ravb: Fix possible hang if RIS2_QFF1 happen sctp: fail if no bound addresses can be used for a given scope net/sched: sch_taprio: do not schedule in taprio_reset() netrom: Fix use-after-free of a listening socket. netfilter: conntrack: fix vtag checks for ABORT/SHUTDOWN_COMPLETE ipv4: prevent potential spectre v1 gadget in fib_metrics_match() ipv4: prevent potential spectre v1 gadget in ip_metrics_convert() netlink: annotate data races around sk_state netlink: annotate data races around dst_portid and dst_group netlink: annotate data races around nlk->portid netfilter: nft_set_rbtree: skip elements in transaction from garbage collection net: fix UaF in netns ops registration error path netlink: prevent potential spectre v1 gadgets EDAC/qcom: Do not pass llcc_driv_data as edac_device_ctl_info's pvt_info EDAC/device: Respect any driver-supplied workqueue polling value ARM: 9280/1: mm: fix warning on phys_addr_t to void pointer assignment thermal: intel: int340x: Protect trip temperature from concurrent updates KVM: x86/vmx: Do not skip segment attributes if unusable bit is set cifs: Fix oops due to uncleared server->smbd_conn in reconnect ftrace/scripts: Update the instructions for ftrace-bisect.sh trace_events_hist: add check for return value of 'create_hist_field' tracing: Make sure trace_printk() can output as soon as it can be used module: Don't wait for GOING modules scsi: hpsa: Fix allocation size for scsi_host_alloc() Bluetooth: hci_sync: cancel cmd_timer if hci_open failed Revert "Revert "xhci: Set HCD flag to defer primary roothub registration"" fs: reiserfs: remove useless new_opts in reiserfs_remount netfilter: conntrack: do not renew entry stuck in tcp SYN_SENT state Revert "selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID" mmc: sdhci-esdhc-imx: correct the tuning start tap and step setting mmc: sdhci-esdhc-imx: disable the CMD CRC check for standard tuning mmc: sdhci-esdhc-imx: clear pending interrupt and halt cqhci lockref: stop doing cpu_relax in the cmpxchg loop platform/x86: asus-nb-wmi: Add alternate mapping for KEY_SCREENLOCK platform/x86: touchscreen_dmi: Add info for the CSL Panther Tab HD scsi: hisi_sas: Set a port invalid only if there are no devices attached when refreshing port id KVM: s390: interrupt: use READ_ONCE() before cmpxchg() spi: spidev: remove debug messages that access spidev->spi without locking ASoC: fsl-asoc-card: Fix naming of AC'97 CODEC widgets ASoC: fsl_ssi: Rename AC'97 streams to avoid collisions with AC'97 CODEC cpufreq: armada-37xx: stop using 0 as NULL pointer s390/debug: add _ASM_S390_ prefix to header guard drm: Add orientation quirk for Lenovo ideapad D330-10IGL ASoC: fsl_micfil: Correct the number of steps on SX controls cpufreq: Add Tegra234 to cpufreq-dt-platdev blocklist tcp: fix rate_app_limited to default to 1 net: dsa: microchip: ksz9477: port map correction in ALU table entry register driver core: Fix test_async_probe_init saves device in wrong array w1: fix WARNING after calling w1_process() w1: fix deadloop in __w1_remove_master_device() tcp: avoid the lookup process failing to get sk in ehash table dmaengine: xilinx_dma: call of_node_put() when breaking out of for_each_child_of_node() dmaengine: xilinx_dma: Fix devm_platform_ioremap_resource error handling dmaengine: xilinx_dma: use devm_platform_ioremap_resource() HID: betop: check shape of output reports net: macb: fix PTP TX timestamp failure due to packet padding dmaengine: Fix double increment of client_count in dma_chan_get() drm/panfrost: fix GENERIC_ATOMIC64 dependency net: mlx5: eliminate anonymous module_init & module_exit usb: gadget: f_fs: Ensure ep0req is dequeued before free_request usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait HID: revert CHERRY_MOUSE_000C quirk net: stmmac: fix invalid call to mdiobus_get_phy() HID: check empty report_list in bigben_probe() HID: check empty report_list in hid_validate_values() net: mdio: validate parameter addr in mdiobus_get_phy() net: usb: sr9700: Handle negative len l2tp: Don't sleep and disable BH under writer-side sk_callback_lock l2tp: Serialize access to sk_user_data with sk_callback_lock net: fix a concurrency bug in l2tp_tunnel_register() net/sched: sch_taprio: fix possible use-after-free wifi: rndis_wlan: Prevent buffer overflow in rndis_query_oid gpio: mxc: Always set GPIOs used as interrupt source to INPUT mode net: wan: Add checks for NULL for utdm in undo_uhdlc_init and unmap_si_regs net: nfc: Fix use-after-free in local_cleanup() phy: rockchip-inno-usb2: Fix missing clk_disable_unprepare() in rockchip_usb2phy_power_on() bpf: Fix pointer-leak due to insufficient speculative store bypass mitigation amd-xgbe: Delay AN timeout during KR training amd-xgbe: TX Flow Ctrl Registers are h/w ver dependent affs: initialize fsdata in affs_truncate() IB/hfi1: Fix expected receive setup error exit issues IB/hfi1: Reserve user expected TIDs IB/hfi1: Reject a zero-length user expected buffer RDMA/core: Fix ib block iterator counter overflow tomoyo: fix broken dependency on *.conf.default EDAC/highbank: Fix memory leak in highbank_mc_probe() HID: intel_ish-hid: Add check for ishtp_dma_tx_map ARM: imx: add missing of_node_put() ARM: imx35: Retrieve the IIM base address from devicetree ARM: imx31: Retrieve the IIM base address from devicetree ARM: imx27: Retrieve the SYSCTRL base address from devicetree ARM: dts: imx6qdl-gw560x: Remove incorrect 'uart-has-rtscts' memory: mvebu-devbus: Fix missing clk_disable_unprepare in mvebu_devbus_probe() memory: atmel-sdramc: Fix missing clk_disable_unprepare in atmel_ramc_probe() clk: Provide new devm_clk helpers for prepared and enabled clocks clk: generalize devm_clk_get() a bit Linux 5.4.230 mm/khugepaged: fix collapse_pte_mapped_thp() to allow anon_vma x86/fpu: Use _Alignof to avoid undefined behavior in TYPE_ALIGN drm/amd/display: Fix COLOR_SPACE_YCBCR2020_TYPE matrix drm/amd/display: Fix set scaling doesn's work drm/i915: re-disable RC6p on Sandy Bridge gsmi: fix null-deref in gsmi_get_variable serial: atmel: fix incorrect baudrate setup dmaengine: tegra210-adma: fix global intr clear serial: pch_uart: Pass correct sg to dma_unmap_sg() dt-bindings: phy: g12a-usb3-pcie-phy: fix compatible string documentation usb-storage: apply IGNORE_UAS only for HIKSEMI MD202 on RTL9210 usb: gadget: f_ncm: fix potential NULL ptr deref in ncm_bitrate() usb: gadget: g_webcam: Send color matching descriptor per frame usb: typec: altmodes/displayport: Fix pin assignment calculation usb: typec: altmodes/displayport: Add pin assignment helper usb: host: ehci-fsl: Fix module alias USB: serial: cp210x: add SCALANCE LPE-9000 device id USB: gadgetfs: Fix race between mounting and unmounting cifs: do not include page data when checking signature btrfs: fix race between quota rescan and disable leading to NULL pointer deref mmc: sunxi-mmc: Fix clock refcount imbalance during unbind comedi: adv_pci1760: Fix PWM instruction handling usb: core: hub: disable autosuspend for TI TUSB8041 misc: fastrpc: Fix use-after-free race condition for maps misc: fastrpc: Don't remove map on creater_process and device_release USB: misc: iowarrior: fix up header size for USB_DEVICE_ID_CODEMERCS_IOW100 USB: serial: option: add Quectel EM05CN modem USB: serial: option: add Quectel EM05CN (SG) modem USB: serial: option: add Quectel EC200U modem USB: serial: option: add Quectel EM05-G (RS) modem USB: serial: option: add Quectel EM05-G (CS) modem USB: serial: option: add Quectel EM05-G (GR) modem prlimit: do_prlimit needs to have a speculation check xhci: Detect lpm incapable xHC USB3 roothub ports from ACPI tables usb: acpi: add helper to check port lpm capability using acpi _DSM xhci: Add a flag to disable USB3 lpm on a xhci root port level. xhci: Add update_hub_device override for PCI xHCI hosts xhci: Fix null pointer dereference when host dies usb: xhci: Check endpoint is valid before dereferencing it xhci-pci: set the dma max_seg_size ALSA: hda/realtek - Turn on power early drm/i915/gt: Reset twice efi: fix userspace infinite retry read efivars after EFI runtime services page fault nilfs2: fix general protection fault in nilfs_btree_insert() Add exception protection processing for vd in axi_chan_handle_err function wifi: brcmfmac: fix regression for Broadcom PCIe wifi devices f2fs: let's avoid panic if extent_tree is not created RDMA/srp: Move large values to a new enum for gcc13 net/ethtool/ioctl: return -EOPNOTSUPP if we have no phy stats selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID pNFS/filelayout: Fix coalescing test for single DS Revert "net: add atomic_long_t to net_device_stats fields" Revert "PM/devfreq: governor: Add a private governor_data for governor" Linux 5.4.229 tipc: call tipc_lxc_xmit without holding node_read_lock ocfs2: fix freeing uninitialized resource on ocfs2_dlm_shutdown tipc: Add a missing case of TIPC_DIRECT_MSG type tty: serial: tegra: Handle RX transfer in PIO mode if DMA wasn't started tipc: fix use-after-free in tipc_disc_rcv() Revert "usb: ulpi: defer ulpi_register on ulpi_read_id timeout" mm: Always release pages to the buddy allocator in memblock_free_late(). efi: fix NULL-deref in init error path arm64: cmpxchg_double*: hazard against entire exchange variable arm64: atomics: remove LL/SC trampolines arm64: atomics: format whitespace consistently drm/virtio: Fix GEM handle creation UAF x86/resctrl: Fix task CLOSID/RMID update race x86/resctrl: Use task_curr() instead of task_struct->on_cpu to prevent unnecessary IPI iommu/mediatek-v1: Fix an error handling path in mtk_iommu_v1_probe() iommu/mediatek-v1: Add error handle for mtk_iommu_probe net/mlx5: Fix ptp max frequency adjustment range net/mlx5: Rename ptp clock info net/sched: act_mpls: Fix warning during failed attribute validation nfc: pn533: Wait for out_urb's completion in pn533_usb_send_frame() hvc/xen: lock console list traversal tipc: fix unexpected link reset due to discovery messages tipc: eliminate checking netns if node established tipc: improve throughput between nodes in netns regulator: da9211: Use irq handler when ready EDAC/device: Fix period calculation in edac_device_reset_delay_period() x86/boot: Avoid using Intel mnemonics in AT&T syntax asm powerpc/imc-pmu: Fix use of mutex in IRQs disabled section netfilter: ipset: Fix overflow before widen in the bitmap_ip_create() function. ext4: fix uninititialized value in 'ext4_evict_inode' ext4: fix use-after-free in ext4_orphan_cleanup ext4: lost matching-pair of trace in ext4_truncate ext4: fix bug_on in __es_tree_search caused by bad quota inode quota: Factor out setup of quota inode jbd2: use the correct print format usb: ulpi: defer ulpi_register on ulpi_read_id timeout wifi: wilc1000: sdio: fix module autoloading ipv6: raw: Deduct extension header length in rawv6_push_pending_frames ixgbe: fix pci device refcount leak platform/x86: sony-laptop: Don't turn off 0x153 keyboard backlight during probe drm/msm/adreno: Make adreno quirks not overwrite each other cifs: Fix uninitialized memory read for smb311 posix symlink create ALSA: hda/hdmi: Add a HP device 0x8715 to force connect list ALSA: pcm: Move rwsem lock inside snd_ctl_elem_read to prevent UAF net/ulp: prevent ULP without clone op from entering the LISTEN status s390/percpu: add READ_ONCE() to arch_this_cpu_to_op_simple() s390/kexec: fix ipl report address for kdump perf auxtrace: Fix address filter duplicate symbol selection docs: Fix the docs build with Sphinx 6.0 efi: tpm: Avoid READ_ONCE() for accessing the event log KVM: arm64: Fix S1PTW handling on RO memslots net: sched: disallow noqueue for qdisc classes driver core: Fix bus_type.match() error handling in __driver_attach() selftests: set the BUILD variable to absolute path selftests: Fix kselftest O=objdir build from cluttering top level objdir parisc: Align parisc MADV_XXX constants with all other architectures mbcache: Avoid nesting of cache->c_list_lock under bit locks hfs/hfsplus: avoid WARN_ON() for sanity check, use proper error handling hfs/hfsplus: use WARN_ON for sanity check ext4: don't allow journal inode to have encrypt flag riscv: uaccess: fix type of 0 variable on error in get_user() nfsd: fix handling of readdir in v4root vs. mount upcall timeout x86/bugs: Flush IBP in ib_prctl_set() ASoC: Intel: bytcr_rt5640: Add quirk for the Advantech MICA-071 tablet udf: Fix extension of the last extent in the file caif: fix memory leak in cfctrl_linkup_request() drm/i915: unpin on error in intel_vgpu_shadow_mm_pin() usb: rndis_host: Secure rndis_query check against int overflow drivers/net/bonding/bond_3ad: return when there's no aggregator perf tools: Fix resources leak in perf_data__open_dir() net: sched: cbq: dont intepret cls results when asked to drop net: sched: atm: dont intepret cls results when asked to drop RDMA/mlx5: Fix validation of max_rd_atomic caps for DC RDMA/uverbs: Silence shiftTooManyBitsSigned warning net: phy: xgmiitorgmii: Fix refcount leak in xgmiitorgmii_probe net: amd-xgbe: add missed tasklet_kill vhost: fix range used in translate_desc() nfc: Fix potential resource leaks qlcnic: prevent ->dcb use-after-free on qlcnic_dcb_enable() failure net: sched: fix memory leak in tcindex_set_parms net: hns3: add interrupts re-initialization while doing VF FLR nfsd: shut down the NFSv4 state objects before the filecache bpf: pull before calling skb_postpull_rcsum() SUNRPC: ensure the matching upcall is in-flight upon downcall ext4: fix deadlock due to mbcache entry corruption mbcache: automatically delete entries from cache on freeing ext4: fix race when reusing xattr blocks ext4: unindent codeblock in ext4_xattr_block_set() ext4: remove EA inode entry from mbcache on inode eviction mbcache: add functions to delete entry if unused mbcache: don't reclaim used entries ext4: use kmemdup() to replace kmalloc + memcpy fs: ext4: initialize fsdata in pagecache_write() ext4: use memcpy_to_page() in pagecache_write() mm/highmem: Lift memcpy_[to|from]_page to core ext4: correct inconsistent error msg in nojournal mode ext4: goto right label 'failed_mount3a' ravb: Fix "failed to switch device to config mode" message during unbind KVM: nVMX: Properly expose ENABLE_USR_WAIT_PAUSE control to L1 KVM: VMX: Fix the spelling of CPU_BASED_USE_TSC_OFFSETTING KVM: VMX: Rename NMI_PENDING to NMI_WINDOW KVM: VMX: Rename INTERRUPT_PENDING to INTERRUPT_WINDOW KVM: retpolines: x86: eliminate retpoline from vmx.c exit handlers KVM: x86: optimize more exit handlers in vmx.c perf probe: Fix to get the DW_AT_decl_file and DW_AT_call_file as unsinged data perf probe: Use dwarf_attr_integrate as generic DWARF attr accessor dm thin: resume even if in FAIL mode media: s5p-mfc: Fix in register read and write for H264 media: s5p-mfc: Clear workbit to handle error condition media: s5p-mfc: Fix to handle reference queue during finishing PM/devfreq: governor: Add a private governor_data for governor btrfs: replace strncpy() with strscpy() ext4: allocate extended attribute value in vmalloc area ext4: avoid unaccounted block allocation when expanding inode ext4: initialize quota before expanding inode in setproject ioctl ext4: fix inode leak in ext4_xattr_inode_create() on an error path ext4: avoid BUG_ON when creating xattrs ext4: fix error code return to user-space in ext4_get_branch() ext4: fix corruption when online resizing a 1K bigalloc fs ext4: fix delayed allocation bug in ext4_clu_mapped for bigalloc + inline ext4: init quota for 'old.inode' in 'ext4_rename' ext4: fix bug_on in __es_tree_search caused by bad boot loader inode ext4: fix reserved cluster accounting in __es_remove_extent() ext4: add helper to check quota inums ext4: add EXT4_IGET_BAD flag to prevent unexpected bad inode ext4: fix undefined behavior in bit shift for ext4_check_flag_values ext4: add inode table check in __ext4_get_inode_loc to aovid possible infinite loop drm/vmwgfx: Validate the box size for the snooped cursor drm/connector: send hotplug uevent on connector cleanup device_cgroup: Roll back to original exceptions after copy failure parisc: led: Fix potential null-ptr-deref in start_task() iommu/amd: Fix ivrs_acpihid cmdline parsing code crypto: n2 - add missing hash statesize PCI/sysfs: Fix double free in error path PCI: Fix pci_device_is_present() for VFs by checking PF ipmi: fix use after free in _ipmi_destroy_user() ima: Fix a potential NULL pointer access in ima_restore_measurement_list mtd: spi-nor: Check for zero erase size in spi_nor_find_best_erase_type() ipmi: fix long wait in unload when IPMI disconnect efi: Add iMac Pro 2017 to uefi skip cert quirk md/bitmap: Fix bitmap chunk size overflow issues cifs: fix missing display of three mount options cifs: fix confusing debug message media: dvb-core: Fix UAF due to refcount races at releasing media: dvb-core: Fix double free in dvb_register_device() ARM: 9256/1: NWFPE: avoid compiler-generated __aeabi_uldivmod tracing: Fix infinite loop in tracing_read_pipe on overflowed print_trace_line tracing/hist: Fix wrong return value in parse_action_params() x86/microcode/intel: Do not retry microcode reloading on the APs tracing/hist: Fix out-of-bound write on 'action_data.var_ref_idx' dm cache: set needs_check flag after aborting metadata dm cache: Fix UAF in destroy() dm clone: Fix UAF in clone_dtr() dm integrity: Fix UAF in dm_integrity_dtr() dm thin: Fix UAF in run_timer_softirq() dm thin: Use last transaction's pmd->root when commit failed dm thin: Fix ABBA deadlock between shrink_slab and dm_pool_abort_metadata dm cache: Fix ABBA deadlock between shrink_slab and dm_cache_metadata_abort binfmt: Fix error return code in load_elf_fdpic_binary() binfmt: Move install_exec_creds after setup_new_exec to match binfmt_elf cpufreq: Init completion before kobject_init_and_add() selftests: Use optional USERCFLAGS and USERLDFLAGS arm64: dts: qcom: sdm850-lenovo-yoga-c630: correct I2C12 pins drive strength ARM: ux500: do not directly dereference __iomem btrfs: fix resolving backrefs for inline extent followed by prealloc mmc: sdhci-sprd: Disable CLK_AUTO when the clock is less than 400K ktest.pl minconfig: Unset configs instead of just removing them kest.pl: Fix grub2 menu handling for rebooting soc: qcom: Select REMAP_MMIO for LLCC driver media: stv0288: use explicitly signed char net/af_packet: make sure to pull mac header net/af_packet: add VLAN support for AF_PACKET SOCK_RAW GSO SUNRPC: Don't leak netobj memory when gss_read_proxy_verf() fails tpm: tpm_tis: Add the missed acpi_put_table() to fix memory leak tpm: tpm_crb: Add the missed acpi_put_table() to fix memory leak mmc: vub300: fix warning - do not call blocking ops when !TASK_RUNNING f2fs: should put a page when checking the summary info mm, compaction: fix fast_isolate_around() to stay within boundaries md: fix a crash in mempool_free pnode: terminate at peers of source ALSA: line6: fix stack overflow in line6_midi_transmit ALSA: line6: correct midi status byte when receiving data from podxt ovl: Use ovl mounter's fsuid and fsgid in ovl_link() hfsplus: fix bug causing custom uid and gid being unable to be assigned with mount HID: plantronics: Additional PIDs for double volume key presses quirk HID: multitouch: fix Asus ExpertBook P2 P2451FA trackpoint powerpc/rtas: avoid scheduling in rtas_os_term() powerpc/rtas: avoid device tree lookups in rtas_os_term() objtool: Fix SEGFAULT nvme: fix the NVME_CMD_EFFECTS_CSE_MASK definition nvme: resync include/linux/nvme.h with nvmecli ata: ahci: Fix PCS quirk application for suspend nvme-pci: fix doorbell buffer value endianness cifs: fix oops during encryption media: dvbdev: fix refcnt bug media: dvbdev: fix build warning due to comments gcov: add support for checksum field regulator: core: fix deadlock on regulator enable iio: adc128s052: add proper .data members in adc128_of_match table iio: adc: ad_sigma_delta: do not use internal iio_dev lock reiserfs: Add missing calls to reiserfs_security_free() HID: wacom: Ensure bootloader PID is usable in hidraw mode usb: dwc3: core: defer probe on ulpi_read_id timeout ALSA: hda/hdmi: Add HP Device 0x8711 to force connect list ALSA: hda/realtek: Add quirk for Lenovo TianYi510Pro-14IOB pstore: Make sure CONFIG_PSTORE_PMSG selects CONFIG_RT_MUTEXES pstore: Switch pmsg_lock to an rt_mutex to avoid priority inversion ASoC: rt5670: Remove unbalanced pm_runtime_put() ASoC: rockchip: spdif: Add missing clk_disable_unprepare() in rk_spdif_runtime_resume() ASoC: wm8994: Fix potential deadlock ASoC: rockchip: pdm: Add missing clk_disable_unprepare() in rockchip_pdm_runtime_resume() ASoC: audio-graph-card: fix refcount leak of cpu_ep in __graph_for_each_link() ASoC: mediatek: mt8173-rt5650-rt5514: fix refcount leak in mt8173_rt5650_rt5514_dev_probe() ASoC: Intel: Skylake: Fix driver hang during shutdown ALSA: hda: add snd_hdac_stop_streams() helper ALSA/ASoC: hda: move/rename snd_hdac_ext_stop_streams to hdac_stream.c orangefs: Fix kmemleak in orangefs_{kernel,client}_debug_init() orangefs: Fix kmemleak in orangefs_prepare_debugfs_help_string() drm/sti: Fix return type of sti_{dvo,hda,hdmi}_connector_mode_valid() drm/fsl-dcu: Fix return type of fsl_dcu_drm_connector_mode_valid() hugetlbfs: fix null-ptr-deref in hugetlbfs_parse_param() clk: st: Fix memory leak in st_of_quadfs_setup() media: si470x: Fix use-after-free in si470x_int_in_callback() mmc: f-sdh30: Add quirks for broken timeout clock capability regulator: core: fix use_count leakage when handling boot-on blk-mq: fix possible memleak when register 'hctx' failed media: dvb-usb: fix memory leak in dvb_usb_adapter_init() media: dvbdev: adopts refcnt to avoid UAF media: dvb-frontends: fix leak of memory fw bpf: Prevent decl_tag from being referenced in func_proto arg ppp: associate skb with a device at tx mrp: introduce active flags to prevent UAF when applicant uninit net: add atomic_long_t to net_device_stats fields md/raid1: stop mdx_raid1 thread when raid1 array run failed drivers/md/md-bitmap: check the return value of md_bitmap_get_counter() drm/sti: Use drm_mode_copy() drm/rockchip: Use drm_mode_copy() s390/lcs: Fix return type of lcs_start_xmit() s390/netiucv: Fix return type of netiucv_tx() s390/ctcm: Fix return type of ctc{mp,}m_tx() igb: Do not free q_vector unless new one was allocated wifi: brcmfmac: Fix potential shift-out-of-bounds in brcmf_fw_alloc_request() hamradio: baycom_epp: Fix return type of baycom_send_packet() net: ethernet: ti: Fix return type of netcp_ndo_start_xmit() bpf: make sure skb->len != 0 when redirecting to a tunneling device ipmi: fix memleak when unload ipmi driver ASoC: codecs: rt298: Add quirk for KBL-R RVP platform wifi: ar5523: Fix use-after-free on ar5523_cmd() timed out wifi: ath9k: verify the expected usb_endpoints are present brcmfmac: return error when getting invalid max_flowrings from dongle drm/etnaviv: add missing quirks for GC300 hfs: fix OOB Read in __hfs_brec_find acct: fix potential integer overflow in encode_comp_t() nilfs2: fix shift-out-of-bounds/overflow in nilfs_sb2_bad_offset() ACPICA: Fix error code path in acpi_ds_call_control_method() fs: jfs: fix shift-out-of-bounds in dbDiscardAG udf: Avoid double brelse() in udf_rename() fs: jfs: fix shift-out-of-bounds in dbAllocAG binfmt_misc: fix shift-out-of-bounds in check_special_flags rcu: Fix __this_cpu_read() lockdep warning in rcu_force_quiescent_state() net: stream: purge sk_error_queue in sk_stream_kill_queues() myri10ge: Fix an error handling path in myri10ge_probe() rxrpc: Fix missing unlock in rxrpc_do_sendmsg() net_sched: reject TCF_EM_SIMPLE case for complex ematch module mailbox: zynq-ipi: fix error handling while device_register() fails skbuff: Account for tail adjustment during pull operations openvswitch: Fix flow lookup to use unmasked key rtc: mxc_v2: Add missing clk_disable_unprepare() r6040: Fix kmemleak in probe and remove nfc: pn533: Clear nfc_target before being used mISDN: hfcmulti: don't call dev_kfree_skb/kfree_skb() under spin_lock_irqsave() mISDN: hfcpci: don't call dev_kfree_skb/kfree_skb() under spin_lock_irqsave() mISDN: hfcsusb: don't call dev_kfree_skb/kfree_skb() under spin_lock_irqsave() nfsd: under NFSv4.1, fix double svc_xprt_put on rpc_create failure NFSD: Add tracepoints to NFSD's duplicate reply cache nfsd: Define the file access mode enum for tracing rtc: pic32: Move devm_rtc_allocate_device earlier in pic32_rtc_probe() rtc: st-lpc: Add missing clk_disable_unprepare in st_rtc_probe() remoteproc: qcom_q6v5_pas: Fix missing of_node_put() in adsp_alloc_memory_region() remoteproc: sysmon: fix memory leak in qcom_add_sysmon_subdev() pwm: sifive: Call pwm_sifive_update_clock() while mutex is held selftests/powerpc: Fix resource leaks powerpc/hv-gpci: Fix hv_gpci event list powerpc/83xx/mpc832x_rdb: call platform_device_put() in error case in of_fsl_spi_probe() powerpc/perf: callchain validate kernel stack pointer bounds powerpc/xive: add missing iounmap() in error path in xive_spapr_populate_irq_data() cxl: Fix refcount leak in cxl_calc_capp_routing powerpc/52xx: Fix a resource leak in an error handling path macintosh/macio-adb: check the return value of ioremap() macintosh: fix possible memory leak in macio_add_one_device() iommu/fsl_pamu: Fix resource leak in fsl_pamu_probe() iommu/amd: Fix pci device refcount leak in ppr_notifier() rtc: pcf85063: Fix reading alarm rtc: snvs: Allow a time difference on clock register read include/uapi/linux/swab: Fix potentially missing __always_inline RDMA/siw: Fix pointer cast warning power: supply: fix null pointer dereferencing in power_supply_get_battery_info HSI: omap_ssi_core: Fix error handling in ssi_init() perf symbol: correction while adjusting symbol perf trace: Handle failure when trace point folder is missed perf trace: Use macro RAW_SYSCALL_ARGS_NUM to replace number perf trace: Add a strtoul() method to 'struct syscall_arg_fmt' perf trace: Allow associating scnprintf routines with well known arg names perf trace: Add the syscall_arg_fmt pointer to syscall_arg perf trace: Factor out the initialization of syscal_arg_fmt->scnprintf perf trace: Separate 'struct syscall_fmt' definition from syscall_fmts variable perf trace: Return error if a system call doesn't exist power: supply: fix residue sysfs file in error handle route of __power_supply_register() HSI: omap_ssi_core: fix possible memory leak in ssi_probe() HSI: omap_ssi_core: fix unbalanced pm_runtime_disable() fbdev: uvesafb: Fixes an error handling path in uvesafb_probe() fbdev: vermilion: decrease reference count in error path fbdev: via: Fix error in via_core_init() fbdev: pm2fb: fix missing pci_disable_device() fbdev: ssd1307fb: Drop optional dependency samples: vfio-mdev: Fix missing pci_disable_device() in mdpy_fb_probe() tracing/hist: Fix issue of losting command info in error_log usb: storage: Add check for kcalloc i2c: ismt: Fix an out-of-bounds bug in ismt_access() vme: Fix error not catched in fake_init() staging: rtl8192e: Fix potential use-after-free in rtllib_rx_Monitor() staging: rtl8192u: Fix use after free in ieee80211_rx() i2c: pxa-pci: fix missing pci_disable_device() on error in ce4100_i2c_probe chardev: fix error handling in cdev_device_add() mcb: mcb-parse: fix error handing in chameleon_parse_gdd() drivers: mcb: fix resource leak in mcb_probe() usb: gadget: f_hid: fix refcount leak on error path usb: gadget: f_hid: fix f_hidg lifetime vs cdev usb: gadget: f_hid: optional SETUP/SET_REPORT mode usb: roles: fix of node refcount leak in usb_role_switch_is_parent() counter: stm32-lptimer-cnt: fix the check on arr and cmp registers update cxl: fix possible null-ptr-deref in cxl_pci_init_afu|adapter() cxl: fix possible null-ptr-deref in cxl_guest_init_afu|adapter() misc: sgi-gru: fix use-after-free error in gru_set_context_option, gru_fault and gru_handle_user_call_os misc: tifm: fix possible memory leak in tifm_7xx1_switch_media() misc: ocxl: fix possible name leak in ocxl_file_register_afu() test_firmware: fix memory leak in test_firmware_init() serial: sunsab: Fix error handling in sunsab_init() serial: altera_uart: fix locking in polling mode tty: serial: altera_uart_{r,t}x_chars() need only uart_port tty: serial: clean up stop-tx part in altera_uart_tx_chars() serial: pch: Fix PCI device refcount leak in pch_request_dma() serial: pl011: Do not clear RX FIFO & RX interrupt in unthrottle. serial: amba-pl011: avoid SBSA UART accessing DMACR register usb: typec: tcpci: fix of node refcount leak in tcpci_register_port() usb: typec: Check for ops->exit instead of ops->enter in altmode_exit staging: vme_user: Fix possible UAF in tsi148_dma_list_add usb: fotg210-udc: Fix ages old endianness issues uio: uio_dmem_genirq: Fix deadlock between irq config and handling uio: uio_dmem_genirq: Fix missing unlock in irq configuration vfio: platform: Do not pass return buffer to ACPI _RST method class: fix possible memory leak in __class_register() serial: tegra: Read DMA status before terminating tty: serial: tegra: Activate RX DMA transfer by request drivers: dio: fix possible memory leak in dio_init() IB/IPoIB: Fix queue count inconsistency for PKEY child interfaces hwrng: geode - Fix PCI device refcount leak hwrng: amd - Fix PCI device refcount leak crypto: img-hash - Fix variable dereferenced before check 'hdev->req' orangefs: Fix sysfs not cleanup when dev init failed RDMA/hfi1: Fix error return code in parse_platform_config() crypto: omap-sham - Use pm_runtime_resume_and_get() in omap_sham_probe() f2fs: avoid victim selection from previous victim section RDMA/nldev: Add checks for nla_nest_start() in fill_stat_counter_qps() scsi: snic: Fix possible UAF in snic_tgt_create() scsi: fcoe: Fix transport not deattached when fcoe_if_init() fails scsi: ipr: Fix WARNING in ipr_init() scsi: fcoe: Fix possible name leak when device_register() fails scsi: hpsa: Fix possible memory leak in hpsa_add_sas_device() scsi: hpsa: Fix error handling in hpsa_add_sas_host() scsi: mpt3sas: Fix possible resource leaks in mpt3sas_transport_port_add() crypto: tcrypt - Fix multibuffer skcipher speed test mem leak scsi: hpsa: Fix possible memory leak in hpsa_init_one() RDMA/rxe: Fix NULL-ptr-deref in rxe_qp_do_cleanup() when socket create failed crypto: ccree - Make cc_debugfs_global_fini() available for module init function RDMA/hfi: Decrease PCI device reference count in error path PCI: Check for alloc failure in pci_request_irq() crypto: ccree - Remove debugfs when platform_driver_register failed crypto: ccree - swap SHA384 and SHA512 larval hashes at build time scsi: scsi_debug: Fix a warning in resp_write_scat() RDMA/siw: Set defined status for work completion with undefined status RDMA/nldev: Return "-EAGAIN" if the cm_id isn't from expected port RDMA/siw: Fix immediate work request flush to completion queue f2fs: fix normal discard process RDMA/core: Fix order of nldev_exit call apparmor: Use pointer to struct aa_label for lbs_cred apparmor: Fix abi check to include v8 abi apparmor: fix lockdep warning when removing a namespace apparmor: fix a memleak in multi_transaction_new() stmmac: fix potential division by 0 Bluetooth: RFCOMM: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_core: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_bcsp: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_h5: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_ll: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_qca: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: btusb: don't call kfree_skb() under spin_lock_irqsave() ntb_netdev: Use dev_kfree_skb_any() in interrupt context net: lan9303: Fix read error execution path can: tcan4x5x: Remove invalid write in clear_interrupts net: amd-xgbe: Check only the minimum speed for active/passive cables net: amd-xgbe: Fix logic around active and passive cables net: amd: lance: don't call dev_kfree_skb() under spin_lock_irqsave() hamradio: don't call dev_kfree_skb() under spin_lock_irqsave() net: ethernet: dnet: don't call dev_kfree_skb() under spin_lock_irqsave() net: emaclite: don't call dev_kfree_skb() under spin_lock_irqsave() net: apple: bmac: don't call dev_kfree_skb() under spin_lock_irqsave() net: apple: mace: don't call dev_kfree_skb() under spin_lock_irqsave() net/tunnel: wait until all sk_user_data reader finish before releasing the sock net: farsync: Fix kmemleak when rmmods farsync ethernet: s2io: don't call dev_kfree_skb() under spin_lock_irqsave() of: overlay: fix null pointer dereferencing in find_dup_cset_node_entry() and find_dup_cset_prop() drivers: net: qlcnic: Fix potential memory leak in qlcnic_sriov_init() net: stmmac: selftests: fix potential memleak in stmmac_test_arpoffload() net: defxx: Fix missing err handling in dfx_init() net: vmw_vsock: vmci: Check memcpy_from_msg() clk: socfpga: Fix memory leak in socfpga_gate_init() clk: socfpga: use clk_hw_register for a5/c5 clk: socfpga: clk-pll: Remove unused variable 'rc' blktrace: Fix output non-blktrace event when blk_classic option enabled wifi: brcmfmac: Fix error return code in brcmf_sdio_download_firmware() wifi: rtl8xxxu: Add __packed to struct rtl8723bu_c2h spi: spi-gpio: Don't set MOSI as an input if not 3WIRE mode clk: samsung: Fix memory leak in _samsung_clk_register_pll() media: coda: Add check for kmalloc media: coda: Add check for dcoda_iram_alloc media: c8sectpfe: Add of_node_put() when breaking out of loop mmc: mmci: fix return value check of mmc_add_host() mmc: wbsd: fix return value check of mmc_add_host() mmc: via-sdmmc: fix return value check of mmc_add_host() mmc: meson-gx: fix return value check of mmc_add_host() mmc: omap_hsmmc: fix return value check of mmc_add_host() mmc: atmel-mci: fix return value check of mmc_add_host() mmc: wmt-sdmmc: fix return value check of mmc_add_host() mmc: vub300: fix return value check of mmc_add_host() mmc: toshsd: fix return value check of mmc_add_host() mmc: rtsx_usb_sdmmc: fix return value check of mmc_add_host() mmc: pxamci: fix return value check of mmc_add_host() mmc: mxcmmc: fix return value check of mmc_add_host() mmc: moxart: fix return value check of mmc_add_host() mmc: alcor: fix return value check of mmc_add_host() NFSv4.x: Fail client initialisation if state manager thread can't run SUNRPC: Fix missing release socket in rpc_sockname() xprtrdma: Fix regbuf data not freed in rpcrdma_req_create() ALSA: mts64: fix possible null-ptr-defer in snd_mts64_interrupt media: saa7164: fix missing pci_disable_device() bpf, sockmap: fix race in sock_map_free() regulator: core: fix resource leak in regulator_register() configfs: fix possible memory leak in configfs_create_dir() hsr: Avoid double remove of a node. clk: qcom: clk-krait: fix wrong div2 functions regulator: core: fix module refcount leak in set_supply() wifi: cfg80211: Fix not unregister reg_pdev when load_builtin_regdb_keys() fails spi: spidev: mask SPI_CS_HIGH in SPI_IOC_RD_MODE bonding: uninitialized variable in bond_miimon_inspect() bpf, sockmap: Fix data loss caused by using apply_bytes on ingress redirect bpf, sockmap: Fix repeated calls to sock_put() when msg has more_data netfilter: conntrack: set icmpv6 redirects as RELATED ASoC: pcm512x: Fix PM disable depth imbalance in pcm512x_probe drm/amdgpu: Fix PCI device refcount leak in amdgpu_atrm_get_bios() drm/radeon: Fix PCI device refcount leak in radeon_atrm_get_bios() ASoC: mediatek: mt8173: Enable IRQ when pdata is ready wifi: iwlwifi: mvm: fix double free on tx path. ALSA: asihpi: fix missing pci_disable_device() NFSv4: Fix a deadlock between nfs4_open_recover_helper() and delegreturn NFSv4.2: Fix initialisation of struct nfs4_label NFSv4.2: Fix a memory stomp in decode_attr_security_label NFSv4.2: Clear FATTR4_WORD2_SECURITY_LABEL when done decoding ASoC: mediatek: mtk-btcvsd: Add checks for write and read of mtk_btcvsd_snd ASoC: dt-bindings: wcd9335: fix reset line polarity in example drm/tegra: Add missing clk_disable_unprepare() in tegra_dc_probe() media: s5p-mfc: Add variant data for MFC v7 hardware for Exynos 3250 SoC media: dvb-usb: az6027: fix null-ptr-deref in az6027_i2c_xfer() media: dvb-core: Fix ignored return value in dvb_register_frontend() pinctrl: pinconf-generic: add missing of_node_put() clk: imx: replace osc_hdmi with dummy clk: imx8mn: correct the usb1_ctrl parent to be usb_bus media: imon: fix a race condition in send_packet() mtd: maps: pxa2xx-flash: fix memory leak in probe bonding: fix link recovery in mode 2 when updelay is nonzero bonding: Rename slave_arr to usable_slaves bonding: Export skip slave logic to function clk: rockchip: Fix memory leak in rockchip_clk_register_pll() regulator: core: use kfree_const() to free space conditionally ALSA: seq: fix undefined behavior in bit shift for SNDRV_SEQ_FILTER_USE_EVENT ALSA: pcm: fix undefined behavior in bit shift for SNDRV_PCM_RATE_KNOT HID: hid-sensor-custom: set fixed size for custom attributes bpf: Move skb->len == 0 checks into __bpf_redirect media: videobuf-dma-contig: use dma_mmap_coherent media: platform: exynos4-is: Fix error handling in fimc_md_init() media: solo6x10: fix possible memory leak in solo_sysfs_init() Input: elants_i2c - properly handle the reset GPIO when power is off mtd: lpddr2_nvm: Fix possible null-ptr-deref wifi: ath10k: Fix return value in ath10k_pci_init() ima: Fix misuse of dereference of pointer in template_desc_init_fields() integrity: Fix memory leakage in keyring allocation error path amdgpu/pm: prevent array underflow in vega20_odn_edit_dpm_table() regulator: core: fix unbalanced of node refcount in regulator_dev_lookup() ASoC: pxa: fix null-pointer dereference in filter() drm/mediatek: Modify dpi power on/off sequence. drm/radeon: Add the missed acpi_put_table() to fix memory leak rxrpc: Fix ack.bufferSize to be 0 when generating an ack net, proc: Provide PROC_FS=n fallback for proc_create_net_single_write() media: camss: Clean up received buffers on failed start of streaming wifi: rsi: Fix handling of 802.3 EAPOL frames sent via control port mtd: Fix device name leak when register device failed in add_mtd_device() bpf: propagate precision in ALU/ALU64 operations media: vivid: fix compose size exceed boundary ima: Handle -ESTALE returned by ima_filter_rule_match() ima: Fix fall-through warnings for Clang ima: Rename internal filter rule functions drm/panel/panel-sitronix-st7701: Remove panel on DSI attach failure spi: Update reference to struct spi_controller clk: renesas: r9a06g032: Repair grave increment error can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming can: kvaser_usb: Add struct kvaser_usb_busparams can: kvaser_usb_leaf: Fix bogus restart events can: kvaser_usb_leaf: Fix wrong CAN state after stopping can: kvaser_usb_leaf: Fix improved state not being reported can: kvaser_usb_leaf: Set Warning state even without bus errors can: kvaser_usb: kvaser_usb_leaf: Handle CMD_ERROR_EVENT can: kvaser_usb: kvaser_usb_leaf: Rename {leaf,usbcan}_cmd_error_event to {leaf,usbcan}_cmd_can_error_event can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device can: kvaser_usb: do not increase tx statistics when sending error message frames media: i2c: ad5820: Fix error path pata_ipx4xx_cf: Fix unsigned comparison with less than zero wifi: rtl8xxxu: Fix reading the vendor of combo chips wifi: ath9k: hif_usb: Fix use-after-free in ath9k_hif_usb_reg_in_cb() wifi: ath9k: hif_usb: fix memory leak of urbs in ath9k_hif_usb_dealloc_tx_urbs() rapidio: devices: fix missing put_device in mport_cdev_open hfs: Fix OOB Write in hfs_asc2mac relay: fix type mismatch when allocating memory in relay_create_buf() eventfd: change int to __u64 in eventfd_signal() ifndef CONFIG_EVENTFD rapidio: fix possible UAF when kfifo_alloc() fails fs: sysv: Fix sysv_nblocks() returns wrong value MIPS: OCTEON: warn only once if deprecated link status is being used MIPS: BCM63xx: Add check for NULL for clk in clk_enable platform/x86: mxm-wmi: fix memleak in mxm_wmi_call_mx[ds|mx]() PM: runtime: Do not call __rpm_callback() from rpm_idle() PM: runtime: Improve path in rpm_idle() when no callback xen/privcmd: Fix a possible warning in privcmd_ioctl_mmap_resource() x86/xen: Fix memory leak in xen_init_lock_cpu() x86/xen: Fix memory leak in xen_smp_intr_init{_pv}() xen/events: only register debug interrupt for 2-level events uprobes/x86: Allow to probe a NOP instruction with 0x66 prefix ACPICA: Fix use-after-free in acpi_ut_copy_ipackage_to_ipackage() clocksource/drivers/sh_cmt: Make sure channel clock supply is enabled rapidio: rio: fix possible name leak in rio_register_mport() rapidio: fix possible name leaks when rio_add_device() fails ocfs2: fix memory leak in ocfs2_mount_volume() ocfs2: rewrite error handling of ocfs2_fill_super ocfs2: ocfs2_mount_volume does cleanup job before return error debugfs: fix error when writing negative value to atomic_t debugfs file docs: fault-injection: fix non-working usage of negative values lib/notifier-error-inject: fix error when writing -errno to debugfs file libfs: add DEFINE_SIMPLE_ATTRIBUTE_SIGNED for signed value cpufreq: amd_freq_sensitivity: Add missing pci_dev_put() genirq/irqdesc: Don't try to remove non-existing sysfs files nfsd: don't call nfsd_file_put from client states seqfile display EDAC/i10nm: fix refcount leak in pci_get_dev_wrapper() irqchip: gic-pm: Use pm_runtime_resume_and_get() in gic_probe() perf/x86/intel/uncore: Fix reference count leak in hswep_has_limit_sbox() PNP: fix name memory leak in pnp_alloc_dev() selftests/efivarfs: Add checking of the test return value MIPS: vpe-cmp: fix possible memory leak while module exiting MIPS: vpe-mt: fix possible memory leak while module exiting ocfs2: fix memory leak in ocfs2_stack_glue_init() lib/fonts: fix undefined behavior in bit shift for get_default_font proc: fixup uptime selftest timerqueue: Use rb_entry_safe() in timerqueue_getnext() perf: Fix possible memleak in pmu_dev_alloc() selftests/ftrace: event_triggers: wait longer for test_event_enable fs: don't audit the capability check in simple_xattr_list() PM: hibernate: Fix mistake in kerneldoc comment alpha: fix syscall entry in !AUDUT_SYSCALL case cpuidle: dt: Return the correct numbers of parsed idle states tpm/tpm_crb: Fix error message in __crb_relinquish_locality() pstore: Avoid kcore oops by vmap()ing with VM_IOREMAP ARM: mmp: fix timer_read delay pstore/ram: Fix error return code in ramoops_probe() arm64: dts: armada-3720-turris-mox: Add missing interrupt for RTC ARM: dts: turris-omnia: Add switch port 6 node ARM: dts: turris-omnia: Add ethernet aliases ARM: dts: armada-39x: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-38x: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-375: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-xp: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-370: Fix assigned-addresses for every PCIe Root Port ARM: dts: dove: Fix assigned-addresses for every PCIe Root Port arm64: dts: mediatek: mt6797: Fix 26M oscillator unit name arm64: dts: mt2712-evb: Fix usb vbus regulators unit names arm64: dts: mt2712-evb: Fix vproc fixed regulators unit names arm64: dts: mt2712e: Fix unit address for pinctrl node arm64: dts: mt2712e: Fix unit_address_vs_reg warning for oscillators perf/smmuv3: Fix hotplug callback leak in arm_smmu_pmu_init() perf: arm_dsu: Fix hotplug callback leak in dsu_pmu_init() soc: ti: smartreflex: Fix PM disable depth imbalance in omap_sr_probe soc: ti: knav_qmss_queue: Fix PM disable depth imbalance in knav_queue_probe soc: ti: knav_qmss_queue: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync arm: dts: spear600: Fix clcd interrupt drivers: soc: ti: knav_qmss_queue: Mark knav_acc_firmwares as static arm64: dts: qcom: sdm845-cheza: fix AP suspend pin bias ARM: dts: qcom: apq8064: fix coresight compatible usb: musb: remove extra check in musb_gadget_vbus_draw net: loopback: use NET_NAME_PREDICTABLE for name_assign_type Bluetooth: L2CAP: Fix u8 overflow HID: uclogic: Add HID_QUIRK_HIDINPUT_FORCE quirk HID: ite: Enable QUIRK_TOUCHPAD_ON_OFF_REPORT on Acer Aspire Switch V 10 HID: ite: Enable QUIRK_TOUCHPAD_ON_OFF_REPORT on Acer Aspire Switch 10E HID: ite: Add support for Acer S1002 keyboard-dock xen-netback: move removal of "hotplug-status" to the right place igb: Initialize mailbox message for VF reset USB: serial: f81534: fix division by zero on line-speed change USB: serial: f81232: fix division by zero on line-speed change USB: serial: cp210x: add Kamstrup RF sniffer PIDs USB: serial: option: add Quectel EM05-G modem usb: gadget: uvc: Prevent buffer overflow in setup handler udf: Fix extending file within last block udf: Do not bother looking for prealloc extents if i_lenExtents matches i_size udf: Fix preallocation discarding at indirect extent boundary udf: Discard preallocation before extending file with a hole tracing/ring-buffer: Only do full wait when cpu != RING_BUFFER_ALL_CPUS ANDROID: Add more hvc devices for virtio-console. Revert "can: af_can: fix NULL pointer dereference in can_rcv_filter" ANDROID: Revert "tracing/ring-buffer: Have polling block on watermark" Linux 5.4.228 ASoC: ops: Correct bounds check for second channel on SX controls can: mcba_usb: Fix termination command argument can: sja1000: fix size of OCR_MODE_MASK define pinctrl: meditatek: Startup with the IRQs disabled ASoC: ops: Check bounds for second channel in snd_soc_put_volsw_sx() nfp: fix use-after-free in area_cache_get() block: unhash blkdev part inode when the part is deleted mm/hugetlb: fix races when looking up a CONT-PTE/PMD size hugetlb page x86/smpboot: Move rcu_cpu_starting() earlier net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head Linux 5.4.227 can: esd_usb: Allow REC and TEC to return to zero net: mvneta: Fix an out of bounds check ipv6: avoid use-after-free in ip6_fragment() net: plip: don't call kfree_skb/dev_kfree_skb() under spin_lock_irq() xen/netback: fix build warning ethernet: aeroflex: fix potential skb leak in greth_init_rings() ipv4: Fix incorrect route flushing when table ID 0 is used ipv4: Fix incorrect route flushing when source address is deleted tipc: Fix potential OOB in tipc_link_proto_rcv() net: hisilicon: Fix potential use-after-free in hix5hd2_rx() net: hisilicon: Fix potential use-after-free in hisi_femac_rx() net: thunderx: Fix missing destroy_workqueue of nicvf_rx_mode_wq net: stmmac: fix "snps,axi-config" node property parsing nvme initialize core quirks before calling nvme_init_subsystem NFC: nci: Bounds check struct nfc_target arrays i40e: Disallow ip4 and ip6 l4_4_bytes i40e: Fix for VF MAC address 0 i40e: Fix not setting default xps_cpus after reset net: mvneta: Prevent out of bounds read in mvneta_config_rss() xen-netfront: Fix NULL sring after live migration net: encx24j600: Fix invalid logic in reading of MISTAT register net: encx24j600: Add parentheses to fix precedence mac802154: fix missing INIT_LIST_HEAD in ieee802154_if_add() selftests: rtnetlink: correct xfrm policy rule in kci_test_ipsec_offload net: dsa: ksz: Check return value Bluetooth: Fix not cleanup led when bt_init fails Bluetooth: 6LoWPAN: add missing hci_dev_put() in get_l2cap_conn() af_unix: Get user_ns from in_skb in unix_diag_get_exact(). igb: Allocate MSI-X vector when testing e1000e: Fix TX dispatch condition gpio: amd8111: Fix PCI device reference count leak drm/bridge: ti-sn65dsi86: Fix output polarity setting bug ca8210: Fix crash by zero initializing data ieee802154: cc2520: Fix error return code in cc2520_hw_init() can: af_can: fix NULL pointer dereference in can_rcv_filter HID: core: fix shift-out-of-bounds in hid_report_raw_event HID: hid-lg4ff: Add check for empty lbuf HID: usbhid: Add ALWAYS_POLL quirk for some mice drm/shmem-helper: Remove errant put in error path KVM: s390: vsie: Fix the initialization of the epoch extension (epdx) field mm/gup: fix gup_pud_range() for dax memcg: fix possible use-after-free in memcg_write_event_control() media: v4l2-dv-timings.c: fix too strict blanking sanity checks Revert "net: dsa: b53: Fix valid setting for MDB entries" xen/netback: don't call kfree_skb() with interrupts disabled xen/netback: do some code cleanup xen/netback: Ensure protocol headers don't fall in the non-linear area mm/khugepaged: invoke MMU notifiers in shmem/file collapse paths mm/khugepaged: fix GUP-fast interaction by sending IPI mm/khugepaged: take the right locks for page table retraction net: usb: qmi_wwan: add u-blox 0x1342 composition 9p/xen: check logical size for buffer size fbcon: Use kzalloc() in fbcon_prepare_logo() regulator: twl6030: fix get status of twl6032 regulators ASoC: soc-pcm: Add NULL check in BE reparenting btrfs: send: avoid unaligned encoded writes when attempting to clone range ALSA: seq: Fix function prototype mismatch in snd_seq_expand_var_event regulator: slg51000: Wait after asserting CS pin 9p/fd: Use P9_HDRSZ for header size ARM: dts: rockchip: disable arm_global_timer on rk3066 and rk3188 ARM: 9266/1: mm: fix no-MMU ZERO_PAGE() implementation ARM: 9251/1: perf: Fix stacktraces for tracepoint events in THUMB2 kernels ARM: dts: rockchip: rk3188: fix lcdc1-rgb24 node name ARM: dts: rockchip: fix ir-receiver node names arm: dts: rockchip: fix node name for hym8563 rtc arm64: dts: rockchip: keep I2S1 disabled for GPIO function on ROCK Pi 4 series Change-Id: I6a643eaa2ee55257328d881f5b860200719e981e Signed-off-by: kamasali Satyanarayan <quic_kamasali@quicinc.com>
5276 lines
142 KiB
C
5276 lines
142 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Generic hugetlb support.
|
|
* (C) Nadia Yvette Chambers, April 2004
|
|
*/
|
|
#include <linux/list.h>
|
|
#include <linux/init.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/sysctl.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/mmu_notifier.h>
|
|
#include <linux/nodemask.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/mempolicy.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/cpuset.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mmdebug.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/rmap.h>
|
|
#include <linux/string_helpers.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/swapops.h>
|
|
#include <linux/jhash.h>
|
|
#include <linux/numa.h>
|
|
#include <linux/llist.h>
|
|
|
|
#include <asm/page.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/tlb.h>
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/hugetlb_cgroup.h>
|
|
#include <linux/node.h>
|
|
#include <linux/userfaultfd_k.h>
|
|
#include <linux/page_owner.h>
|
|
#include "internal.h"
|
|
|
|
int hugetlb_max_hstate __read_mostly;
|
|
unsigned int default_hstate_idx;
|
|
struct hstate hstates[HUGE_MAX_HSTATE];
|
|
/*
|
|
* Minimum page order among possible hugepage sizes, set to a proper value
|
|
* at boot time.
|
|
*/
|
|
static unsigned int minimum_order __read_mostly = UINT_MAX;
|
|
|
|
__initdata LIST_HEAD(huge_boot_pages);
|
|
|
|
/* for command line parsing */
|
|
static struct hstate * __initdata parsed_hstate;
|
|
static unsigned long __initdata default_hstate_max_huge_pages;
|
|
static unsigned long __initdata default_hstate_size;
|
|
static bool __initdata parsed_valid_hugepagesz = true;
|
|
|
|
/*
|
|
* Protects updates to hugepage_freelists, hugepage_activelist, nr_huge_pages,
|
|
* free_huge_pages, and surplus_huge_pages.
|
|
*/
|
|
DEFINE_SPINLOCK(hugetlb_lock);
|
|
|
|
/*
|
|
* Serializes faults on the same logical page. This is used to
|
|
* prevent spurious OOMs when the hugepage pool is fully utilized.
|
|
*/
|
|
static int num_fault_mutexes;
|
|
struct mutex *hugetlb_fault_mutex_table ____cacheline_aligned_in_smp;
|
|
|
|
static inline bool PageHugeFreed(struct page *head)
|
|
{
|
|
return page_private(head + 4) == -1UL;
|
|
}
|
|
|
|
static inline void SetPageHugeFreed(struct page *head)
|
|
{
|
|
set_page_private(head + 4, -1UL);
|
|
}
|
|
|
|
static inline void ClearPageHugeFreed(struct page *head)
|
|
{
|
|
set_page_private(head + 4, 0);
|
|
}
|
|
|
|
/* Forward declaration */
|
|
static int hugetlb_acct_memory(struct hstate *h, long delta);
|
|
|
|
static inline void unlock_or_release_subpool(struct hugepage_subpool *spool)
|
|
{
|
|
bool free = (spool->count == 0) && (spool->used_hpages == 0);
|
|
|
|
spin_unlock(&spool->lock);
|
|
|
|
/* If no pages are used, and no other handles to the subpool
|
|
* remain, give up any reservations mased on minimum size and
|
|
* free the subpool */
|
|
if (free) {
|
|
if (spool->min_hpages != -1)
|
|
hugetlb_acct_memory(spool->hstate,
|
|
-spool->min_hpages);
|
|
kfree(spool);
|
|
}
|
|
}
|
|
|
|
struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
|
|
long min_hpages)
|
|
{
|
|
struct hugepage_subpool *spool;
|
|
|
|
spool = kzalloc(sizeof(*spool), GFP_KERNEL);
|
|
if (!spool)
|
|
return NULL;
|
|
|
|
spin_lock_init(&spool->lock);
|
|
spool->count = 1;
|
|
spool->max_hpages = max_hpages;
|
|
spool->hstate = h;
|
|
spool->min_hpages = min_hpages;
|
|
|
|
if (min_hpages != -1 && hugetlb_acct_memory(h, min_hpages)) {
|
|
kfree(spool);
|
|
return NULL;
|
|
}
|
|
spool->rsv_hpages = min_hpages;
|
|
|
|
return spool;
|
|
}
|
|
|
|
void hugepage_put_subpool(struct hugepage_subpool *spool)
|
|
{
|
|
spin_lock(&spool->lock);
|
|
BUG_ON(!spool->count);
|
|
spool->count--;
|
|
unlock_or_release_subpool(spool);
|
|
}
|
|
|
|
/*
|
|
* Subpool accounting for allocating and reserving pages.
|
|
* Return -ENOMEM if there are not enough resources to satisfy the
|
|
* the request. Otherwise, return the number of pages by which the
|
|
* global pools must be adjusted (upward). The returned value may
|
|
* only be different than the passed value (delta) in the case where
|
|
* a subpool minimum size must be manitained.
|
|
*/
|
|
static long hugepage_subpool_get_pages(struct hugepage_subpool *spool,
|
|
long delta)
|
|
{
|
|
long ret = delta;
|
|
|
|
if (!spool)
|
|
return ret;
|
|
|
|
spin_lock(&spool->lock);
|
|
|
|
if (spool->max_hpages != -1) { /* maximum size accounting */
|
|
if ((spool->used_hpages + delta) <= spool->max_hpages)
|
|
spool->used_hpages += delta;
|
|
else {
|
|
ret = -ENOMEM;
|
|
goto unlock_ret;
|
|
}
|
|
}
|
|
|
|
/* minimum size accounting */
|
|
if (spool->min_hpages != -1 && spool->rsv_hpages) {
|
|
if (delta > spool->rsv_hpages) {
|
|
/*
|
|
* Asking for more reserves than those already taken on
|
|
* behalf of subpool. Return difference.
|
|
*/
|
|
ret = delta - spool->rsv_hpages;
|
|
spool->rsv_hpages = 0;
|
|
} else {
|
|
ret = 0; /* reserves already accounted for */
|
|
spool->rsv_hpages -= delta;
|
|
}
|
|
}
|
|
|
|
unlock_ret:
|
|
spin_unlock(&spool->lock);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Subpool accounting for freeing and unreserving pages.
|
|
* Return the number of global page reservations that must be dropped.
|
|
* The return value may only be different than the passed value (delta)
|
|
* in the case where a subpool minimum size must be maintained.
|
|
*/
|
|
static long hugepage_subpool_put_pages(struct hugepage_subpool *spool,
|
|
long delta)
|
|
{
|
|
long ret = delta;
|
|
|
|
if (!spool)
|
|
return delta;
|
|
|
|
spin_lock(&spool->lock);
|
|
|
|
if (spool->max_hpages != -1) /* maximum size accounting */
|
|
spool->used_hpages -= delta;
|
|
|
|
/* minimum size accounting */
|
|
if (spool->min_hpages != -1 && spool->used_hpages < spool->min_hpages) {
|
|
if (spool->rsv_hpages + delta <= spool->min_hpages)
|
|
ret = 0;
|
|
else
|
|
ret = spool->rsv_hpages + delta - spool->min_hpages;
|
|
|
|
spool->rsv_hpages += delta;
|
|
if (spool->rsv_hpages > spool->min_hpages)
|
|
spool->rsv_hpages = spool->min_hpages;
|
|
}
|
|
|
|
/*
|
|
* If hugetlbfs_put_super couldn't free spool due to an outstanding
|
|
* quota reference, free it now.
|
|
*/
|
|
unlock_or_release_subpool(spool);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline struct hugepage_subpool *subpool_inode(struct inode *inode)
|
|
{
|
|
return HUGETLBFS_SB(inode->i_sb)->spool;
|
|
}
|
|
|
|
static inline struct hugepage_subpool *subpool_vma(struct vm_area_struct *vma)
|
|
{
|
|
return subpool_inode(file_inode(vma->vm_file));
|
|
}
|
|
|
|
/*
|
|
* Region tracking -- allows tracking of reservations and instantiated pages
|
|
* across the pages in a mapping.
|
|
*
|
|
* The region data structures are embedded into a resv_map and protected
|
|
* by a resv_map's lock. The set of regions within the resv_map represent
|
|
* reservations for huge pages, or huge pages that have already been
|
|
* instantiated within the map. The from and to elements are huge page
|
|
* indicies into the associated mapping. from indicates the starting index
|
|
* of the region. to represents the first index past the end of the region.
|
|
*
|
|
* For example, a file region structure with from == 0 and to == 4 represents
|
|
* four huge pages in a mapping. It is important to note that the to element
|
|
* represents the first element past the end of the region. This is used in
|
|
* arithmetic as 4(to) - 0(from) = 4 huge pages in the region.
|
|
*
|
|
* Interval notation of the form [from, to) will be used to indicate that
|
|
* the endpoint from is inclusive and to is exclusive.
|
|
*/
|
|
struct file_region {
|
|
struct list_head link;
|
|
long from;
|
|
long to;
|
|
};
|
|
|
|
/*
|
|
* Add the huge page range represented by [f, t) to the reserve
|
|
* map. In the normal case, existing regions will be expanded
|
|
* to accommodate the specified range. Sufficient regions should
|
|
* exist for expansion due to the previous call to region_chg
|
|
* with the same range. However, it is possible that region_del
|
|
* could have been called after region_chg and modifed the map
|
|
* in such a way that no region exists to be expanded. In this
|
|
* case, pull a region descriptor from the cache associated with
|
|
* the map and use that for the new range.
|
|
*
|
|
* Return the number of new huge pages added to the map. This
|
|
* number is greater than or equal to zero.
|
|
*/
|
|
static long region_add(struct resv_map *resv, long f, long t)
|
|
{
|
|
struct list_head *head = &resv->regions;
|
|
struct file_region *rg, *nrg, *trg;
|
|
long add = 0;
|
|
|
|
spin_lock(&resv->lock);
|
|
/* Locate the region we are either in or before. */
|
|
list_for_each_entry(rg, head, link)
|
|
if (f <= rg->to)
|
|
break;
|
|
|
|
/*
|
|
* If no region exists which can be expanded to include the
|
|
* specified range, the list must have been modified by an
|
|
* interleving call to region_del(). Pull a region descriptor
|
|
* from the cache and use it for this range.
|
|
*/
|
|
if (&rg->link == head || t < rg->from) {
|
|
VM_BUG_ON(resv->region_cache_count <= 0);
|
|
|
|
resv->region_cache_count--;
|
|
nrg = list_first_entry(&resv->region_cache, struct file_region,
|
|
link);
|
|
list_del(&nrg->link);
|
|
|
|
nrg->from = f;
|
|
nrg->to = t;
|
|
list_add(&nrg->link, rg->link.prev);
|
|
|
|
add += t - f;
|
|
goto out_locked;
|
|
}
|
|
|
|
/* Round our left edge to the current segment if it encloses us. */
|
|
if (f > rg->from)
|
|
f = rg->from;
|
|
|
|
/* Check for and consume any regions we now overlap with. */
|
|
nrg = rg;
|
|
list_for_each_entry_safe(rg, trg, rg->link.prev, link) {
|
|
if (&rg->link == head)
|
|
break;
|
|
if (rg->from > t)
|
|
break;
|
|
|
|
/* If this area reaches higher then extend our area to
|
|
* include it completely. If this is not the first area
|
|
* which we intend to reuse, free it. */
|
|
if (rg->to > t)
|
|
t = rg->to;
|
|
if (rg != nrg) {
|
|
/* Decrement return value by the deleted range.
|
|
* Another range will span this area so that by
|
|
* end of routine add will be >= zero
|
|
*/
|
|
add -= (rg->to - rg->from);
|
|
list_del(&rg->link);
|
|
kfree(rg);
|
|
}
|
|
}
|
|
|
|
add += (nrg->from - f); /* Added to beginning of region */
|
|
nrg->from = f;
|
|
add += t - nrg->to; /* Added to end of region */
|
|
nrg->to = t;
|
|
|
|
out_locked:
|
|
resv->adds_in_progress--;
|
|
spin_unlock(&resv->lock);
|
|
VM_BUG_ON(add < 0);
|
|
return add;
|
|
}
|
|
|
|
/*
|
|
* Examine the existing reserve map and determine how many
|
|
* huge pages in the specified range [f, t) are NOT currently
|
|
* represented. This routine is called before a subsequent
|
|
* call to region_add that will actually modify the reserve
|
|
* map to add the specified range [f, t). region_chg does
|
|
* not change the number of huge pages represented by the
|
|
* map. However, if the existing regions in the map can not
|
|
* be expanded to represent the new range, a new file_region
|
|
* structure is added to the map as a placeholder. This is
|
|
* so that the subsequent region_add call will have all the
|
|
* regions it needs and will not fail.
|
|
*
|
|
* Upon entry, region_chg will also examine the cache of region descriptors
|
|
* associated with the map. If there are not enough descriptors cached, one
|
|
* will be allocated for the in progress add operation.
|
|
*
|
|
* Returns the number of huge pages that need to be added to the existing
|
|
* reservation map for the range [f, t). This number is greater or equal to
|
|
* zero. -ENOMEM is returned if a new file_region structure or cache entry
|
|
* is needed and can not be allocated.
|
|
*/
|
|
static long region_chg(struct resv_map *resv, long f, long t)
|
|
{
|
|
struct list_head *head = &resv->regions;
|
|
struct file_region *rg, *nrg = NULL;
|
|
long chg = 0;
|
|
|
|
retry:
|
|
spin_lock(&resv->lock);
|
|
retry_locked:
|
|
resv->adds_in_progress++;
|
|
|
|
/*
|
|
* Check for sufficient descriptors in the cache to accommodate
|
|
* the number of in progress add operations.
|
|
*/
|
|
if (resv->adds_in_progress > resv->region_cache_count) {
|
|
struct file_region *trg;
|
|
|
|
VM_BUG_ON(resv->adds_in_progress - resv->region_cache_count > 1);
|
|
/* Must drop lock to allocate a new descriptor. */
|
|
resv->adds_in_progress--;
|
|
spin_unlock(&resv->lock);
|
|
|
|
trg = kmalloc(sizeof(*trg), GFP_KERNEL);
|
|
if (!trg) {
|
|
kfree(nrg);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
spin_lock(&resv->lock);
|
|
list_add(&trg->link, &resv->region_cache);
|
|
resv->region_cache_count++;
|
|
goto retry_locked;
|
|
}
|
|
|
|
/* Locate the region we are before or in. */
|
|
list_for_each_entry(rg, head, link)
|
|
if (f <= rg->to)
|
|
break;
|
|
|
|
/* If we are below the current region then a new region is required.
|
|
* Subtle, allocate a new region at the position but make it zero
|
|
* size such that we can guarantee to record the reservation. */
|
|
if (&rg->link == head || t < rg->from) {
|
|
if (!nrg) {
|
|
resv->adds_in_progress--;
|
|
spin_unlock(&resv->lock);
|
|
nrg = kmalloc(sizeof(*nrg), GFP_KERNEL);
|
|
if (!nrg)
|
|
return -ENOMEM;
|
|
|
|
nrg->from = f;
|
|
nrg->to = f;
|
|
INIT_LIST_HEAD(&nrg->link);
|
|
goto retry;
|
|
}
|
|
|
|
list_add(&nrg->link, rg->link.prev);
|
|
chg = t - f;
|
|
goto out_nrg;
|
|
}
|
|
|
|
/* Round our left edge to the current segment if it encloses us. */
|
|
if (f > rg->from)
|
|
f = rg->from;
|
|
chg = t - f;
|
|
|
|
/* Check for and consume any regions we now overlap with. */
|
|
list_for_each_entry(rg, rg->link.prev, link) {
|
|
if (&rg->link == head)
|
|
break;
|
|
if (rg->from > t)
|
|
goto out;
|
|
|
|
/* We overlap with this area, if it extends further than
|
|
* us then we must extend ourselves. Account for its
|
|
* existing reservation. */
|
|
if (rg->to > t) {
|
|
chg += rg->to - t;
|
|
t = rg->to;
|
|
}
|
|
chg -= rg->to - rg->from;
|
|
}
|
|
|
|
out:
|
|
spin_unlock(&resv->lock);
|
|
/* We already know we raced and no longer need the new region */
|
|
kfree(nrg);
|
|
return chg;
|
|
out_nrg:
|
|
spin_unlock(&resv->lock);
|
|
return chg;
|
|
}
|
|
|
|
/*
|
|
* Abort the in progress add operation. The adds_in_progress field
|
|
* of the resv_map keeps track of the operations in progress between
|
|
* calls to region_chg and region_add. Operations are sometimes
|
|
* aborted after the call to region_chg. In such cases, region_abort
|
|
* is called to decrement the adds_in_progress counter.
|
|
*
|
|
* NOTE: The range arguments [f, t) are not needed or used in this
|
|
* routine. They are kept to make reading the calling code easier as
|
|
* arguments will match the associated region_chg call.
|
|
*/
|
|
static void region_abort(struct resv_map *resv, long f, long t)
|
|
{
|
|
spin_lock(&resv->lock);
|
|
VM_BUG_ON(!resv->region_cache_count);
|
|
resv->adds_in_progress--;
|
|
spin_unlock(&resv->lock);
|
|
}
|
|
|
|
/*
|
|
* Delete the specified range [f, t) from the reserve map. If the
|
|
* t parameter is LONG_MAX, this indicates that ALL regions after f
|
|
* should be deleted. Locate the regions which intersect [f, t)
|
|
* and either trim, delete or split the existing regions.
|
|
*
|
|
* Returns the number of huge pages deleted from the reserve map.
|
|
* In the normal case, the return value is zero or more. In the
|
|
* case where a region must be split, a new region descriptor must
|
|
* be allocated. If the allocation fails, -ENOMEM will be returned.
|
|
* NOTE: If the parameter t == LONG_MAX, then we will never split
|
|
* a region and possibly return -ENOMEM. Callers specifying
|
|
* t == LONG_MAX do not need to check for -ENOMEM error.
|
|
*/
|
|
static long region_del(struct resv_map *resv, long f, long t)
|
|
{
|
|
struct list_head *head = &resv->regions;
|
|
struct file_region *rg, *trg;
|
|
struct file_region *nrg = NULL;
|
|
long del = 0;
|
|
|
|
retry:
|
|
spin_lock(&resv->lock);
|
|
list_for_each_entry_safe(rg, trg, head, link) {
|
|
/*
|
|
* Skip regions before the range to be deleted. file_region
|
|
* ranges are normally of the form [from, to). However, there
|
|
* may be a "placeholder" entry in the map which is of the form
|
|
* (from, to) with from == to. Check for placeholder entries
|
|
* at the beginning of the range to be deleted.
|
|
*/
|
|
if (rg->to <= f && (rg->to != rg->from || rg->to != f))
|
|
continue;
|
|
|
|
if (rg->from >= t)
|
|
break;
|
|
|
|
if (f > rg->from && t < rg->to) { /* Must split region */
|
|
/*
|
|
* Check for an entry in the cache before dropping
|
|
* lock and attempting allocation.
|
|
*/
|
|
if (!nrg &&
|
|
resv->region_cache_count > resv->adds_in_progress) {
|
|
nrg = list_first_entry(&resv->region_cache,
|
|
struct file_region,
|
|
link);
|
|
list_del(&nrg->link);
|
|
resv->region_cache_count--;
|
|
}
|
|
|
|
if (!nrg) {
|
|
spin_unlock(&resv->lock);
|
|
nrg = kmalloc(sizeof(*nrg), GFP_KERNEL);
|
|
if (!nrg)
|
|
return -ENOMEM;
|
|
goto retry;
|
|
}
|
|
|
|
del += t - f;
|
|
|
|
/* New entry for end of split region */
|
|
nrg->from = t;
|
|
nrg->to = rg->to;
|
|
INIT_LIST_HEAD(&nrg->link);
|
|
|
|
/* Original entry is trimmed */
|
|
rg->to = f;
|
|
|
|
list_add(&nrg->link, &rg->link);
|
|
nrg = NULL;
|
|
break;
|
|
}
|
|
|
|
if (f <= rg->from && t >= rg->to) { /* Remove entire region */
|
|
del += rg->to - rg->from;
|
|
list_del(&rg->link);
|
|
kfree(rg);
|
|
continue;
|
|
}
|
|
|
|
if (f <= rg->from) { /* Trim beginning of region */
|
|
del += t - rg->from;
|
|
rg->from = t;
|
|
} else { /* Trim end of region */
|
|
del += rg->to - f;
|
|
rg->to = f;
|
|
}
|
|
}
|
|
|
|
spin_unlock(&resv->lock);
|
|
kfree(nrg);
|
|
return del;
|
|
}
|
|
|
|
/*
|
|
* A rare out of memory error was encountered which prevented removal of
|
|
* the reserve map region for a page. The huge page itself was free'ed
|
|
* and removed from the page cache. This routine will adjust the subpool
|
|
* usage count, and the global reserve count if needed. By incrementing
|
|
* these counts, the reserve map entry which could not be deleted will
|
|
* appear as a "reserved" entry instead of simply dangling with incorrect
|
|
* counts.
|
|
*/
|
|
void hugetlb_fix_reserve_counts(struct inode *inode)
|
|
{
|
|
struct hugepage_subpool *spool = subpool_inode(inode);
|
|
long rsv_adjust;
|
|
bool reserved = false;
|
|
|
|
rsv_adjust = hugepage_subpool_get_pages(spool, 1);
|
|
if (rsv_adjust > 0) {
|
|
struct hstate *h = hstate_inode(inode);
|
|
|
|
if (!hugetlb_acct_memory(h, 1))
|
|
reserved = true;
|
|
} else if (!rsv_adjust) {
|
|
reserved = true;
|
|
}
|
|
|
|
if (!reserved)
|
|
pr_warn("hugetlb: Huge Page Reserved count may go negative.\n");
|
|
}
|
|
|
|
/*
|
|
* Count and return the number of huge pages in the reserve map
|
|
* that intersect with the range [f, t).
|
|
*/
|
|
static long region_count(struct resv_map *resv, long f, long t)
|
|
{
|
|
struct list_head *head = &resv->regions;
|
|
struct file_region *rg;
|
|
long chg = 0;
|
|
|
|
spin_lock(&resv->lock);
|
|
/* Locate each segment we overlap with, and count that overlap. */
|
|
list_for_each_entry(rg, head, link) {
|
|
long seg_from;
|
|
long seg_to;
|
|
|
|
if (rg->to <= f)
|
|
continue;
|
|
if (rg->from >= t)
|
|
break;
|
|
|
|
seg_from = max(rg->from, f);
|
|
seg_to = min(rg->to, t);
|
|
|
|
chg += seg_to - seg_from;
|
|
}
|
|
spin_unlock(&resv->lock);
|
|
|
|
return chg;
|
|
}
|
|
|
|
/*
|
|
* Convert the address within this vma to the page offset within
|
|
* the mapping, in pagecache page units; huge pages here.
|
|
*/
|
|
static pgoff_t vma_hugecache_offset(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long address)
|
|
{
|
|
return ((address - vma->vm_start) >> huge_page_shift(h)) +
|
|
(vma->vm_pgoff >> huge_page_order(h));
|
|
}
|
|
|
|
pgoff_t linear_hugepage_index(struct vm_area_struct *vma,
|
|
unsigned long address)
|
|
{
|
|
return vma_hugecache_offset(hstate_vma(vma), vma, address);
|
|
}
|
|
EXPORT_SYMBOL_GPL(linear_hugepage_index);
|
|
|
|
/*
|
|
* Return the size of the pages allocated when backing a VMA. In the majority
|
|
* cases this will be same size as used by the page table entries.
|
|
*/
|
|
unsigned long vma_kernel_pagesize(struct vm_area_struct *vma)
|
|
{
|
|
if (vma->vm_ops && vma->vm_ops->pagesize)
|
|
return vma->vm_ops->pagesize(vma);
|
|
return PAGE_SIZE;
|
|
}
|
|
EXPORT_SYMBOL_GPL(vma_kernel_pagesize);
|
|
|
|
/*
|
|
* Return the page size being used by the MMU to back a VMA. In the majority
|
|
* of cases, the page size used by the kernel matches the MMU size. On
|
|
* architectures where it differs, an architecture-specific 'strong'
|
|
* version of this symbol is required.
|
|
*/
|
|
__weak unsigned long vma_mmu_pagesize(struct vm_area_struct *vma)
|
|
{
|
|
return vma_kernel_pagesize(vma);
|
|
}
|
|
|
|
/*
|
|
* Flags for MAP_PRIVATE reservations. These are stored in the bottom
|
|
* bits of the reservation map pointer, which are always clear due to
|
|
* alignment.
|
|
*/
|
|
#define HPAGE_RESV_OWNER (1UL << 0)
|
|
#define HPAGE_RESV_UNMAPPED (1UL << 1)
|
|
#define HPAGE_RESV_MASK (HPAGE_RESV_OWNER | HPAGE_RESV_UNMAPPED)
|
|
|
|
/*
|
|
* These helpers are used to track how many pages are reserved for
|
|
* faults in a MAP_PRIVATE mapping. Only the process that called mmap()
|
|
* is guaranteed to have their future faults succeed.
|
|
*
|
|
* With the exception of reset_vma_resv_huge_pages() which is called at fork(),
|
|
* the reserve counters are updated with the hugetlb_lock held. It is safe
|
|
* to reset the VMA at fork() time as it is not in use yet and there is no
|
|
* chance of the global counters getting corrupted as a result of the values.
|
|
*
|
|
* The private mapping reservation is represented in a subtly different
|
|
* manner to a shared mapping. A shared mapping has a region map associated
|
|
* with the underlying file, this region map represents the backing file
|
|
* pages which have ever had a reservation assigned which this persists even
|
|
* after the page is instantiated. A private mapping has a region map
|
|
* associated with the original mmap which is attached to all VMAs which
|
|
* reference it, this region map represents those offsets which have consumed
|
|
* reservation ie. where pages have been instantiated.
|
|
*/
|
|
static unsigned long get_vma_private_data(struct vm_area_struct *vma)
|
|
{
|
|
return (unsigned long)vma->vm_private_data;
|
|
}
|
|
|
|
static void set_vma_private_data(struct vm_area_struct *vma,
|
|
unsigned long value)
|
|
{
|
|
vma->vm_private_data = (void *)value;
|
|
}
|
|
|
|
struct resv_map *resv_map_alloc(void)
|
|
{
|
|
struct resv_map *resv_map = kmalloc(sizeof(*resv_map), GFP_KERNEL);
|
|
struct file_region *rg = kmalloc(sizeof(*rg), GFP_KERNEL);
|
|
|
|
if (!resv_map || !rg) {
|
|
kfree(resv_map);
|
|
kfree(rg);
|
|
return NULL;
|
|
}
|
|
|
|
kref_init(&resv_map->refs);
|
|
spin_lock_init(&resv_map->lock);
|
|
INIT_LIST_HEAD(&resv_map->regions);
|
|
|
|
resv_map->adds_in_progress = 0;
|
|
|
|
INIT_LIST_HEAD(&resv_map->region_cache);
|
|
list_add(&rg->link, &resv_map->region_cache);
|
|
resv_map->region_cache_count = 1;
|
|
|
|
return resv_map;
|
|
}
|
|
|
|
void resv_map_release(struct kref *ref)
|
|
{
|
|
struct resv_map *resv_map = container_of(ref, struct resv_map, refs);
|
|
struct list_head *head = &resv_map->region_cache;
|
|
struct file_region *rg, *trg;
|
|
|
|
/* Clear out any active regions before we release the map. */
|
|
region_del(resv_map, 0, LONG_MAX);
|
|
|
|
/* ... and any entries left in the cache */
|
|
list_for_each_entry_safe(rg, trg, head, link) {
|
|
list_del(&rg->link);
|
|
kfree(rg);
|
|
}
|
|
|
|
VM_BUG_ON(resv_map->adds_in_progress);
|
|
|
|
kfree(resv_map);
|
|
}
|
|
|
|
static inline struct resv_map *inode_resv_map(struct inode *inode)
|
|
{
|
|
/*
|
|
* At inode evict time, i_mapping may not point to the original
|
|
* address space within the inode. This original address space
|
|
* contains the pointer to the resv_map. So, always use the
|
|
* address space embedded within the inode.
|
|
* The VERY common case is inode->mapping == &inode->i_data but,
|
|
* this may not be true for device special inodes.
|
|
*/
|
|
return (struct resv_map *)(&inode->i_data)->private_data;
|
|
}
|
|
|
|
static struct resv_map *vma_resv_map(struct vm_area_struct *vma)
|
|
{
|
|
VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
|
|
if (vma->vm_flags & VM_MAYSHARE) {
|
|
struct address_space *mapping = vma->vm_file->f_mapping;
|
|
struct inode *inode = mapping->host;
|
|
|
|
return inode_resv_map(inode);
|
|
|
|
} else {
|
|
return (struct resv_map *)(get_vma_private_data(vma) &
|
|
~HPAGE_RESV_MASK);
|
|
}
|
|
}
|
|
|
|
static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map)
|
|
{
|
|
VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
|
|
VM_BUG_ON_VMA(vma->vm_flags & VM_MAYSHARE, vma);
|
|
|
|
set_vma_private_data(vma, (get_vma_private_data(vma) &
|
|
HPAGE_RESV_MASK) | (unsigned long)map);
|
|
}
|
|
|
|
static void set_vma_resv_flags(struct vm_area_struct *vma, unsigned long flags)
|
|
{
|
|
VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
|
|
VM_BUG_ON_VMA(vma->vm_flags & VM_MAYSHARE, vma);
|
|
|
|
set_vma_private_data(vma, get_vma_private_data(vma) | flags);
|
|
}
|
|
|
|
static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag)
|
|
{
|
|
VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
|
|
|
|
return (get_vma_private_data(vma) & flag) != 0;
|
|
}
|
|
|
|
/* Reset counters to 0 and clear all HPAGE_RESV_* flags */
|
|
void reset_vma_resv_huge_pages(struct vm_area_struct *vma)
|
|
{
|
|
VM_BUG_ON_VMA(!is_vm_hugetlb_page(vma), vma);
|
|
if (!(vma->vm_flags & VM_MAYSHARE))
|
|
vma->vm_private_data = (void *)0;
|
|
}
|
|
|
|
/* Returns true if the VMA has associated reserve pages */
|
|
static bool vma_has_reserves(struct vm_area_struct *vma, long chg)
|
|
{
|
|
if (vma->vm_flags & VM_NORESERVE) {
|
|
/*
|
|
* This address is already reserved by other process(chg == 0),
|
|
* so, we should decrement reserved count. Without decrementing,
|
|
* reserve count remains after releasing inode, because this
|
|
* allocated page will go into page cache and is regarded as
|
|
* coming from reserved pool in releasing step. Currently, we
|
|
* don't have any other solution to deal with this situation
|
|
* properly, so add work-around here.
|
|
*/
|
|
if (vma->vm_flags & VM_MAYSHARE && chg == 0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* Shared mappings always use reserves */
|
|
if (vma->vm_flags & VM_MAYSHARE) {
|
|
/*
|
|
* We know VM_NORESERVE is not set. Therefore, there SHOULD
|
|
* be a region map for all pages. The only situation where
|
|
* there is no region map is if a hole was punched via
|
|
* fallocate. In this case, there really are no reverves to
|
|
* use. This situation is indicated if chg != 0.
|
|
*/
|
|
if (chg)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Only the process that called mmap() has reserves for
|
|
* private mappings.
|
|
*/
|
|
if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) {
|
|
/*
|
|
* Like the shared case above, a hole punch or truncate
|
|
* could have been performed on the private mapping.
|
|
* Examine the value of chg to determine if reserves
|
|
* actually exist or were previously consumed.
|
|
* Very Subtle - The value of chg comes from a previous
|
|
* call to vma_needs_reserves(). The reserve map for
|
|
* private mappings has different (opposite) semantics
|
|
* than that of shared mappings. vma_needs_reserves()
|
|
* has already taken this difference in semantics into
|
|
* account. Therefore, the meaning of chg is the same
|
|
* as in the shared case above. Code could easily be
|
|
* combined, but keeping it separate draws attention to
|
|
* subtle differences.
|
|
*/
|
|
if (chg)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void enqueue_huge_page(struct hstate *h, struct page *page)
|
|
{
|
|
int nid = page_to_nid(page);
|
|
list_move(&page->lru, &h->hugepage_freelists[nid]);
|
|
h->free_huge_pages++;
|
|
h->free_huge_pages_node[nid]++;
|
|
SetPageHugeFreed(page);
|
|
}
|
|
|
|
static struct page *dequeue_huge_page_node_exact(struct hstate *h, int nid)
|
|
{
|
|
struct page *page;
|
|
|
|
list_for_each_entry(page, &h->hugepage_freelists[nid], lru)
|
|
if (!PageHWPoison(page))
|
|
break;
|
|
/*
|
|
* if 'non-isolated free hugepage' not found on the list,
|
|
* the allocation fails.
|
|
*/
|
|
if (&h->hugepage_freelists[nid] == &page->lru)
|
|
return NULL;
|
|
list_move(&page->lru, &h->hugepage_activelist);
|
|
set_page_refcounted(page);
|
|
ClearPageHugeFreed(page);
|
|
h->free_huge_pages--;
|
|
h->free_huge_pages_node[nid]--;
|
|
return page;
|
|
}
|
|
|
|
static struct page *dequeue_huge_page_nodemask(struct hstate *h, gfp_t gfp_mask, int nid,
|
|
nodemask_t *nmask)
|
|
{
|
|
unsigned int cpuset_mems_cookie;
|
|
struct zonelist *zonelist;
|
|
struct zone *zone;
|
|
struct zoneref *z;
|
|
int node = NUMA_NO_NODE;
|
|
|
|
zonelist = node_zonelist(nid, gfp_mask);
|
|
|
|
retry_cpuset:
|
|
cpuset_mems_cookie = read_mems_allowed_begin();
|
|
for_each_zone_zonelist_nodemask(zone, z, zonelist, gfp_zone(gfp_mask), nmask) {
|
|
struct page *page;
|
|
|
|
if (!cpuset_zone_allowed(zone, gfp_mask))
|
|
continue;
|
|
/*
|
|
* no need to ask again on the same node. Pool is node rather than
|
|
* zone aware
|
|
*/
|
|
if (zone_to_nid(zone) == node)
|
|
continue;
|
|
node = zone_to_nid(zone);
|
|
|
|
page = dequeue_huge_page_node_exact(h, node);
|
|
if (page)
|
|
return page;
|
|
}
|
|
if (unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
|
|
goto retry_cpuset;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Movability of hugepages depends on migration support. */
|
|
static inline gfp_t htlb_alloc_mask(struct hstate *h)
|
|
{
|
|
if (hugepage_movable_supported(h))
|
|
return GFP_HIGHUSER_MOVABLE;
|
|
else
|
|
return GFP_HIGHUSER;
|
|
}
|
|
|
|
static struct page *dequeue_huge_page_vma(struct hstate *h,
|
|
struct vm_area_struct *vma,
|
|
unsigned long address, int avoid_reserve,
|
|
long chg)
|
|
{
|
|
struct page *page;
|
|
struct mempolicy *mpol;
|
|
gfp_t gfp_mask;
|
|
nodemask_t *nodemask;
|
|
int nid;
|
|
|
|
/*
|
|
* A child process with MAP_PRIVATE mappings created by their parent
|
|
* have no page reserves. This check ensures that reservations are
|
|
* not "stolen". The child may still get SIGKILLed
|
|
*/
|
|
if (!vma_has_reserves(vma, chg) &&
|
|
h->free_huge_pages - h->resv_huge_pages == 0)
|
|
goto err;
|
|
|
|
/* If reserves cannot be used, ensure enough pages are in the pool */
|
|
if (avoid_reserve && h->free_huge_pages - h->resv_huge_pages == 0)
|
|
goto err;
|
|
|
|
gfp_mask = htlb_alloc_mask(h);
|
|
nid = huge_node(vma, address, gfp_mask, &mpol, &nodemask);
|
|
page = dequeue_huge_page_nodemask(h, gfp_mask, nid, nodemask);
|
|
if (page && !avoid_reserve && vma_has_reserves(vma, chg)) {
|
|
SetPagePrivate(page);
|
|
h->resv_huge_pages--;
|
|
}
|
|
|
|
mpol_cond_put(mpol);
|
|
return page;
|
|
|
|
err:
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* common helper functions for hstate_next_node_to_{alloc|free}.
|
|
* We may have allocated or freed a huge page based on a different
|
|
* nodes_allowed previously, so h->next_node_to_{alloc|free} might
|
|
* be outside of *nodes_allowed. Ensure that we use an allowed
|
|
* node for alloc or free.
|
|
*/
|
|
static int next_node_allowed(int nid, nodemask_t *nodes_allowed)
|
|
{
|
|
nid = next_node_in(nid, *nodes_allowed);
|
|
VM_BUG_ON(nid >= MAX_NUMNODES);
|
|
|
|
return nid;
|
|
}
|
|
|
|
static int get_valid_node_allowed(int nid, nodemask_t *nodes_allowed)
|
|
{
|
|
if (!node_isset(nid, *nodes_allowed))
|
|
nid = next_node_allowed(nid, nodes_allowed);
|
|
return nid;
|
|
}
|
|
|
|
/*
|
|
* returns the previously saved node ["this node"] from which to
|
|
* allocate a persistent huge page for the pool and advance the
|
|
* next node from which to allocate, handling wrap at end of node
|
|
* mask.
|
|
*/
|
|
static int hstate_next_node_to_alloc(struct hstate *h,
|
|
nodemask_t *nodes_allowed)
|
|
{
|
|
int nid;
|
|
|
|
VM_BUG_ON(!nodes_allowed);
|
|
|
|
nid = get_valid_node_allowed(h->next_nid_to_alloc, nodes_allowed);
|
|
h->next_nid_to_alloc = next_node_allowed(nid, nodes_allowed);
|
|
|
|
return nid;
|
|
}
|
|
|
|
/*
|
|
* helper for free_pool_huge_page() - return the previously saved
|
|
* node ["this node"] from which to free a huge page. Advance the
|
|
* next node id whether or not we find a free huge page to free so
|
|
* that the next attempt to free addresses the next node.
|
|
*/
|
|
static int hstate_next_node_to_free(struct hstate *h, nodemask_t *nodes_allowed)
|
|
{
|
|
int nid;
|
|
|
|
VM_BUG_ON(!nodes_allowed);
|
|
|
|
nid = get_valid_node_allowed(h->next_nid_to_free, nodes_allowed);
|
|
h->next_nid_to_free = next_node_allowed(nid, nodes_allowed);
|
|
|
|
return nid;
|
|
}
|
|
|
|
#define for_each_node_mask_to_alloc(hs, nr_nodes, node, mask) \
|
|
for (nr_nodes = nodes_weight(*mask); \
|
|
nr_nodes > 0 && \
|
|
((node = hstate_next_node_to_alloc(hs, mask)) || 1); \
|
|
nr_nodes--)
|
|
|
|
#define for_each_node_mask_to_free(hs, nr_nodes, node, mask) \
|
|
for (nr_nodes = nodes_weight(*mask); \
|
|
nr_nodes > 0 && \
|
|
((node = hstate_next_node_to_free(hs, mask)) || 1); \
|
|
nr_nodes--)
|
|
|
|
#ifdef CONFIG_ARCH_HAS_GIGANTIC_PAGE
|
|
static void destroy_compound_gigantic_page(struct page *page,
|
|
unsigned int order)
|
|
{
|
|
int i;
|
|
int nr_pages = 1 << order;
|
|
struct page *p = page + 1;
|
|
|
|
atomic_set(compound_mapcount_ptr(page), 0);
|
|
for (i = 1; i < nr_pages; i++, p = mem_map_next(p, page, i)) {
|
|
clear_compound_head(p);
|
|
set_page_refcounted(p);
|
|
}
|
|
|
|
set_compound_order(page, 0);
|
|
__ClearPageHead(page);
|
|
}
|
|
|
|
static void free_gigantic_page(struct page *page, unsigned int order)
|
|
{
|
|
free_contig_range(page_to_pfn(page), 1 << order);
|
|
}
|
|
|
|
#ifdef CONFIG_CONTIG_ALLOC
|
|
static int __alloc_gigantic_page(unsigned long start_pfn,
|
|
unsigned long nr_pages, gfp_t gfp_mask)
|
|
{
|
|
unsigned long end_pfn = start_pfn + nr_pages;
|
|
return alloc_contig_range(start_pfn, end_pfn, MIGRATE_MOVABLE,
|
|
gfp_mask);
|
|
}
|
|
|
|
static bool pfn_range_valid_gigantic(struct zone *z,
|
|
unsigned long start_pfn, unsigned long nr_pages)
|
|
{
|
|
unsigned long i, end_pfn = start_pfn + nr_pages;
|
|
struct page *page;
|
|
|
|
for (i = start_pfn; i < end_pfn; i++) {
|
|
page = pfn_to_online_page(i);
|
|
if (!page)
|
|
return false;
|
|
|
|
if (page_zone(page) != z)
|
|
return false;
|
|
|
|
if (PageReserved(page))
|
|
return false;
|
|
|
|
if (page_count(page) > 0)
|
|
return false;
|
|
|
|
if (PageHuge(page))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool zone_spans_last_pfn(const struct zone *zone,
|
|
unsigned long start_pfn, unsigned long nr_pages)
|
|
{
|
|
unsigned long last_pfn = start_pfn + nr_pages - 1;
|
|
return zone_spans_pfn(zone, last_pfn);
|
|
}
|
|
|
|
static struct page *alloc_gigantic_page(struct hstate *h, gfp_t gfp_mask,
|
|
int nid, nodemask_t *nodemask)
|
|
{
|
|
unsigned int order = huge_page_order(h);
|
|
unsigned long nr_pages = 1 << order;
|
|
unsigned long ret, pfn, flags;
|
|
struct zonelist *zonelist;
|
|
struct zone *zone;
|
|
struct zoneref *z;
|
|
|
|
zonelist = node_zonelist(nid, gfp_mask);
|
|
for_each_zone_zonelist_nodemask(zone, z, zonelist, gfp_zone(gfp_mask), nodemask) {
|
|
spin_lock_irqsave(&zone->lock, flags);
|
|
|
|
pfn = ALIGN(zone->zone_start_pfn, nr_pages);
|
|
while (zone_spans_last_pfn(zone, pfn, nr_pages)) {
|
|
if (pfn_range_valid_gigantic(zone, pfn, nr_pages)) {
|
|
/*
|
|
* We release the zone lock here because
|
|
* alloc_contig_range() will also lock the zone
|
|
* at some point. If there's an allocation
|
|
* spinning on this lock, it may win the race
|
|
* and cause alloc_contig_range() to fail...
|
|
*/
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
ret = __alloc_gigantic_page(pfn, nr_pages, gfp_mask);
|
|
if (!ret)
|
|
return pfn_to_page(pfn);
|
|
spin_lock_irqsave(&zone->lock, flags);
|
|
}
|
|
pfn += nr_pages;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&zone->lock, flags);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void prep_new_huge_page(struct hstate *h, struct page *page, int nid);
|
|
static void prep_compound_gigantic_page(struct page *page, unsigned int order);
|
|
#else /* !CONFIG_CONTIG_ALLOC */
|
|
static struct page *alloc_gigantic_page(struct hstate *h, gfp_t gfp_mask,
|
|
int nid, nodemask_t *nodemask)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif /* CONFIG_CONTIG_ALLOC */
|
|
|
|
#else /* !CONFIG_ARCH_HAS_GIGANTIC_PAGE */
|
|
static struct page *alloc_gigantic_page(struct hstate *h, gfp_t gfp_mask,
|
|
int nid, nodemask_t *nodemask)
|
|
{
|
|
return NULL;
|
|
}
|
|
static inline void free_gigantic_page(struct page *page, unsigned int order) { }
|
|
static inline void destroy_compound_gigantic_page(struct page *page,
|
|
unsigned int order) { }
|
|
#endif
|
|
|
|
static void update_and_free_page(struct hstate *h, struct page *page)
|
|
{
|
|
int i;
|
|
struct page *subpage = page;
|
|
|
|
if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported())
|
|
return;
|
|
|
|
h->nr_huge_pages--;
|
|
h->nr_huge_pages_node[page_to_nid(page)]--;
|
|
for (i = 0; i < pages_per_huge_page(h);
|
|
i++, subpage = mem_map_next(subpage, page, i)) {
|
|
subpage->flags &= ~(1 << PG_locked | 1 << PG_error |
|
|
1 << PG_referenced | 1 << PG_dirty |
|
|
1 << PG_active | 1 << PG_private |
|
|
1 << PG_writeback);
|
|
}
|
|
VM_BUG_ON_PAGE(hugetlb_cgroup_from_page(page), page);
|
|
set_compound_page_dtor(page, NULL_COMPOUND_DTOR);
|
|
set_page_refcounted(page);
|
|
if (hstate_is_gigantic(h)) {
|
|
destroy_compound_gigantic_page(page, huge_page_order(h));
|
|
free_gigantic_page(page, huge_page_order(h));
|
|
} else {
|
|
__free_pages(page, huge_page_order(h));
|
|
}
|
|
}
|
|
|
|
struct hstate *size_to_hstate(unsigned long size)
|
|
{
|
|
struct hstate *h;
|
|
|
|
for_each_hstate(h) {
|
|
if (huge_page_size(h) == size)
|
|
return h;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Test to determine whether the hugepage is "active/in-use" (i.e. being linked
|
|
* to hstate->hugepage_activelist.)
|
|
*
|
|
* This function can be called for tail pages, but never returns true for them.
|
|
*/
|
|
bool page_huge_active(struct page *page)
|
|
{
|
|
return PageHeadHuge(page) && PagePrivate(&page[1]);
|
|
}
|
|
|
|
/* never called for tail page */
|
|
void set_page_huge_active(struct page *page)
|
|
{
|
|
VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
|
|
SetPagePrivate(&page[1]);
|
|
}
|
|
|
|
static void clear_page_huge_active(struct page *page)
|
|
{
|
|
VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
|
|
ClearPagePrivate(&page[1]);
|
|
}
|
|
|
|
/*
|
|
* Internal hugetlb specific page flag. Do not use outside of the hugetlb
|
|
* code
|
|
*/
|
|
static inline bool PageHugeTemporary(struct page *page)
|
|
{
|
|
if (!PageHuge(page))
|
|
return false;
|
|
|
|
return (unsigned long)page[2].mapping == -1U;
|
|
}
|
|
|
|
static inline void SetPageHugeTemporary(struct page *page)
|
|
{
|
|
page[2].mapping = (void *)-1U;
|
|
}
|
|
|
|
static inline void ClearPageHugeTemporary(struct page *page)
|
|
{
|
|
page[2].mapping = NULL;
|
|
}
|
|
|
|
static void __free_huge_page(struct page *page)
|
|
{
|
|
/*
|
|
* Can't pass hstate in here because it is called from the
|
|
* compound page destructor.
|
|
*/
|
|
struct hstate *h = page_hstate(page);
|
|
int nid = page_to_nid(page);
|
|
struct hugepage_subpool *spool =
|
|
(struct hugepage_subpool *)page_private(page);
|
|
bool restore_reserve;
|
|
|
|
VM_BUG_ON_PAGE(page_count(page), page);
|
|
VM_BUG_ON_PAGE(page_mapcount(page), page);
|
|
|
|
set_page_private(page, 0);
|
|
page->mapping = NULL;
|
|
restore_reserve = PagePrivate(page);
|
|
ClearPagePrivate(page);
|
|
|
|
/*
|
|
* If PagePrivate() was set on page, page allocation consumed a
|
|
* reservation. If the page was associated with a subpool, there
|
|
* would have been a page reserved in the subpool before allocation
|
|
* via hugepage_subpool_get_pages(). Since we are 'restoring' the
|
|
* reservtion, do not call hugepage_subpool_put_pages() as this will
|
|
* remove the reserved page from the subpool.
|
|
*/
|
|
if (!restore_reserve) {
|
|
/*
|
|
* A return code of zero implies that the subpool will be
|
|
* under its minimum size if the reservation is not restored
|
|
* after page is free. Therefore, force restore_reserve
|
|
* operation.
|
|
*/
|
|
if (hugepage_subpool_put_pages(spool, 1) == 0)
|
|
restore_reserve = true;
|
|
}
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
clear_page_huge_active(page);
|
|
hugetlb_cgroup_uncharge_page(hstate_index(h),
|
|
pages_per_huge_page(h), page);
|
|
if (restore_reserve)
|
|
h->resv_huge_pages++;
|
|
|
|
if (PageHugeTemporary(page)) {
|
|
list_del(&page->lru);
|
|
ClearPageHugeTemporary(page);
|
|
update_and_free_page(h, page);
|
|
} else if (h->surplus_huge_pages_node[nid]) {
|
|
/* remove the page from active list */
|
|
list_del(&page->lru);
|
|
update_and_free_page(h, page);
|
|
h->surplus_huge_pages--;
|
|
h->surplus_huge_pages_node[nid]--;
|
|
} else {
|
|
arch_clear_hugepage_flags(page);
|
|
enqueue_huge_page(h, page);
|
|
}
|
|
spin_unlock(&hugetlb_lock);
|
|
}
|
|
|
|
/*
|
|
* As free_huge_page() can be called from a non-task context, we have
|
|
* to defer the actual freeing in a workqueue to prevent potential
|
|
* hugetlb_lock deadlock.
|
|
*
|
|
* free_hpage_workfn() locklessly retrieves the linked list of pages to
|
|
* be freed and frees them one-by-one. As the page->mapping pointer is
|
|
* going to be cleared in __free_huge_page() anyway, it is reused as the
|
|
* llist_node structure of a lockless linked list of huge pages to be freed.
|
|
*/
|
|
static LLIST_HEAD(hpage_freelist);
|
|
|
|
static void free_hpage_workfn(struct work_struct *work)
|
|
{
|
|
struct llist_node *node;
|
|
struct page *page;
|
|
|
|
node = llist_del_all(&hpage_freelist);
|
|
|
|
while (node) {
|
|
page = container_of((struct address_space **)node,
|
|
struct page, mapping);
|
|
node = node->next;
|
|
__free_huge_page(page);
|
|
}
|
|
}
|
|
static DECLARE_WORK(free_hpage_work, free_hpage_workfn);
|
|
|
|
void free_huge_page(struct page *page)
|
|
{
|
|
/*
|
|
* Defer freeing if in non-task context to avoid hugetlb_lock deadlock.
|
|
*/
|
|
if (!in_task()) {
|
|
/*
|
|
* Only call schedule_work() if hpage_freelist is previously
|
|
* empty. Otherwise, schedule_work() had been called but the
|
|
* workfn hasn't retrieved the list yet.
|
|
*/
|
|
if (llist_add((struct llist_node *)&page->mapping,
|
|
&hpage_freelist))
|
|
schedule_work(&free_hpage_work);
|
|
return;
|
|
}
|
|
|
|
__free_huge_page(page);
|
|
}
|
|
|
|
static void prep_new_huge_page(struct hstate *h, struct page *page, int nid)
|
|
{
|
|
INIT_LIST_HEAD(&page->lru);
|
|
set_compound_page_dtor(page, HUGETLB_PAGE_DTOR);
|
|
spin_lock(&hugetlb_lock);
|
|
set_hugetlb_cgroup(page, NULL);
|
|
h->nr_huge_pages++;
|
|
h->nr_huge_pages_node[nid]++;
|
|
ClearPageHugeFreed(page);
|
|
spin_unlock(&hugetlb_lock);
|
|
}
|
|
|
|
static void prep_compound_gigantic_page(struct page *page, unsigned int order)
|
|
{
|
|
int i;
|
|
int nr_pages = 1 << order;
|
|
struct page *p = page + 1;
|
|
|
|
/* we rely on prep_new_huge_page to set the destructor */
|
|
set_compound_order(page, order);
|
|
__ClearPageReserved(page);
|
|
__SetPageHead(page);
|
|
for (i = 1; i < nr_pages; i++, p = mem_map_next(p, page, i)) {
|
|
/*
|
|
* For gigantic hugepages allocated through bootmem at
|
|
* boot, it's safer to be consistent with the not-gigantic
|
|
* hugepages and clear the PG_reserved bit from all tail pages
|
|
* too. Otherwse drivers using get_user_pages() to access tail
|
|
* pages may get the reference counting wrong if they see
|
|
* PG_reserved set on a tail page (despite the head page not
|
|
* having PG_reserved set). Enforcing this consistency between
|
|
* head and tail pages allows drivers to optimize away a check
|
|
* on the head page when they need know if put_page() is needed
|
|
* after get_user_pages().
|
|
*/
|
|
__ClearPageReserved(p);
|
|
set_page_count(p, 0);
|
|
set_compound_head(p, page);
|
|
}
|
|
atomic_set(compound_mapcount_ptr(page), -1);
|
|
}
|
|
|
|
/*
|
|
* PageHuge() only returns true for hugetlbfs pages, but not for normal or
|
|
* transparent huge pages. See the PageTransHuge() documentation for more
|
|
* details.
|
|
*/
|
|
int PageHuge(struct page *page)
|
|
{
|
|
if (!PageCompound(page))
|
|
return 0;
|
|
|
|
page = compound_head(page);
|
|
return page[1].compound_dtor == HUGETLB_PAGE_DTOR;
|
|
}
|
|
EXPORT_SYMBOL_GPL(PageHuge);
|
|
|
|
/*
|
|
* PageHeadHuge() only returns true for hugetlbfs head page, but not for
|
|
* normal or transparent huge pages.
|
|
*/
|
|
int PageHeadHuge(struct page *page_head)
|
|
{
|
|
if (!PageHead(page_head))
|
|
return 0;
|
|
|
|
return get_compound_page_dtor(page_head) == free_huge_page;
|
|
}
|
|
|
|
pgoff_t hugetlb_basepage_index(struct page *page)
|
|
{
|
|
struct page *page_head = compound_head(page);
|
|
pgoff_t index = page_index(page_head);
|
|
unsigned long compound_idx;
|
|
|
|
if (compound_order(page_head) >= MAX_ORDER)
|
|
compound_idx = page_to_pfn(page) - page_to_pfn(page_head);
|
|
else
|
|
compound_idx = page - page_head;
|
|
|
|
return (index << compound_order(page_head)) + compound_idx;
|
|
}
|
|
|
|
static struct page *alloc_buddy_huge_page(struct hstate *h,
|
|
gfp_t gfp_mask, int nid, nodemask_t *nmask,
|
|
nodemask_t *node_alloc_noretry)
|
|
{
|
|
int order = huge_page_order(h);
|
|
struct page *page;
|
|
bool alloc_try_hard = true;
|
|
|
|
/*
|
|
* By default we always try hard to allocate the page with
|
|
* __GFP_RETRY_MAYFAIL flag. However, if we are allocating pages in
|
|
* a loop (to adjust global huge page counts) and previous allocation
|
|
* failed, do not continue to try hard on the same node. Use the
|
|
* node_alloc_noretry bitmap to manage this state information.
|
|
*/
|
|
if (node_alloc_noretry && node_isset(nid, *node_alloc_noretry))
|
|
alloc_try_hard = false;
|
|
gfp_mask |= __GFP_COMP|__GFP_NOWARN;
|
|
if (alloc_try_hard)
|
|
gfp_mask |= __GFP_RETRY_MAYFAIL;
|
|
if (nid == NUMA_NO_NODE)
|
|
nid = numa_mem_id();
|
|
page = __alloc_pages_nodemask(gfp_mask, order, nid, nmask);
|
|
if (page)
|
|
__count_vm_event(HTLB_BUDDY_PGALLOC);
|
|
else
|
|
__count_vm_event(HTLB_BUDDY_PGALLOC_FAIL);
|
|
|
|
/*
|
|
* If we did not specify __GFP_RETRY_MAYFAIL, but still got a page this
|
|
* indicates an overall state change. Clear bit so that we resume
|
|
* normal 'try hard' allocations.
|
|
*/
|
|
if (node_alloc_noretry && page && !alloc_try_hard)
|
|
node_clear(nid, *node_alloc_noretry);
|
|
|
|
/*
|
|
* If we tried hard to get a page but failed, set bit so that
|
|
* subsequent attempts will not try as hard until there is an
|
|
* overall state change.
|
|
*/
|
|
if (node_alloc_noretry && !page && alloc_try_hard)
|
|
node_set(nid, *node_alloc_noretry);
|
|
|
|
return page;
|
|
}
|
|
|
|
/*
|
|
* Common helper to allocate a fresh hugetlb page. All specific allocators
|
|
* should use this function to get new hugetlb pages
|
|
*/
|
|
static struct page *alloc_fresh_huge_page(struct hstate *h,
|
|
gfp_t gfp_mask, int nid, nodemask_t *nmask,
|
|
nodemask_t *node_alloc_noretry)
|
|
{
|
|
struct page *page;
|
|
|
|
if (hstate_is_gigantic(h))
|
|
page = alloc_gigantic_page(h, gfp_mask, nid, nmask);
|
|
else
|
|
page = alloc_buddy_huge_page(h, gfp_mask,
|
|
nid, nmask, node_alloc_noretry);
|
|
if (!page)
|
|
return NULL;
|
|
|
|
if (hstate_is_gigantic(h))
|
|
prep_compound_gigantic_page(page, huge_page_order(h));
|
|
prep_new_huge_page(h, page, page_to_nid(page));
|
|
|
|
return page;
|
|
}
|
|
|
|
/*
|
|
* Allocates a fresh page to the hugetlb allocator pool in the node interleaved
|
|
* manner.
|
|
*/
|
|
static int alloc_pool_huge_page(struct hstate *h, nodemask_t *nodes_allowed,
|
|
nodemask_t *node_alloc_noretry)
|
|
{
|
|
struct page *page;
|
|
int nr_nodes, node;
|
|
gfp_t gfp_mask = htlb_alloc_mask(h) | __GFP_THISNODE;
|
|
|
|
for_each_node_mask_to_alloc(h, nr_nodes, node, nodes_allowed) {
|
|
page = alloc_fresh_huge_page(h, gfp_mask, node, nodes_allowed,
|
|
node_alloc_noretry);
|
|
if (page)
|
|
break;
|
|
}
|
|
|
|
if (!page)
|
|
return 0;
|
|
|
|
put_page(page); /* free it into the hugepage allocator */
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Free huge page from pool from next node to free.
|
|
* Attempt to keep persistent huge pages more or less
|
|
* balanced over allowed nodes.
|
|
* Called with hugetlb_lock locked.
|
|
*/
|
|
static int free_pool_huge_page(struct hstate *h, nodemask_t *nodes_allowed,
|
|
bool acct_surplus)
|
|
{
|
|
int nr_nodes, node;
|
|
int ret = 0;
|
|
|
|
for_each_node_mask_to_free(h, nr_nodes, node, nodes_allowed) {
|
|
/*
|
|
* If we're returning unused surplus pages, only examine
|
|
* nodes with surplus pages.
|
|
*/
|
|
if ((!acct_surplus || h->surplus_huge_pages_node[node]) &&
|
|
!list_empty(&h->hugepage_freelists[node])) {
|
|
struct page *page =
|
|
list_entry(h->hugepage_freelists[node].next,
|
|
struct page, lru);
|
|
list_del(&page->lru);
|
|
h->free_huge_pages--;
|
|
h->free_huge_pages_node[node]--;
|
|
if (acct_surplus) {
|
|
h->surplus_huge_pages--;
|
|
h->surplus_huge_pages_node[node]--;
|
|
}
|
|
update_and_free_page(h, page);
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Dissolve a given free hugepage into free buddy pages. This function does
|
|
* nothing for in-use hugepages and non-hugepages.
|
|
* This function returns values like below:
|
|
*
|
|
* -EBUSY: failed to dissolved free hugepages or the hugepage is in-use
|
|
* (allocated or reserved.)
|
|
* 0: successfully dissolved free hugepages or the page is not a
|
|
* hugepage (considered as already dissolved)
|
|
*/
|
|
int dissolve_free_huge_page(struct page *page)
|
|
{
|
|
int rc = -EBUSY;
|
|
|
|
retry:
|
|
/* Not to disrupt normal path by vainly holding hugetlb_lock */
|
|
if (!PageHuge(page))
|
|
return 0;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
if (!PageHuge(page)) {
|
|
rc = 0;
|
|
goto out;
|
|
}
|
|
|
|
if (!page_count(page)) {
|
|
struct page *head = compound_head(page);
|
|
struct hstate *h = page_hstate(head);
|
|
int nid = page_to_nid(head);
|
|
if (h->free_huge_pages - h->resv_huge_pages == 0)
|
|
goto out;
|
|
|
|
/*
|
|
* We should make sure that the page is already on the free list
|
|
* when it is dissolved.
|
|
*/
|
|
if (unlikely(!PageHugeFreed(head))) {
|
|
spin_unlock(&hugetlb_lock);
|
|
cond_resched();
|
|
|
|
/*
|
|
* Theoretically, we should return -EBUSY when we
|
|
* encounter this race. In fact, we have a chance
|
|
* to successfully dissolve the page if we do a
|
|
* retry. Because the race window is quite small.
|
|
* If we seize this opportunity, it is an optimization
|
|
* for increasing the success rate of dissolving page.
|
|
*/
|
|
goto retry;
|
|
}
|
|
|
|
/*
|
|
* Move PageHWPoison flag from head page to the raw error page,
|
|
* which makes any subpages rather than the error page reusable.
|
|
*/
|
|
if (PageHWPoison(head) && page != head) {
|
|
SetPageHWPoison(page);
|
|
ClearPageHWPoison(head);
|
|
}
|
|
list_del(&head->lru);
|
|
h->free_huge_pages--;
|
|
h->free_huge_pages_node[nid]--;
|
|
h->max_huge_pages--;
|
|
update_and_free_page(h, head);
|
|
rc = 0;
|
|
}
|
|
out:
|
|
spin_unlock(&hugetlb_lock);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Dissolve free hugepages in a given pfn range. Used by memory hotplug to
|
|
* make specified memory blocks removable from the system.
|
|
* Note that this will dissolve a free gigantic hugepage completely, if any
|
|
* part of it lies within the given range.
|
|
* Also note that if dissolve_free_huge_page() returns with an error, all
|
|
* free hugepages that were dissolved before that error are lost.
|
|
*/
|
|
int dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
|
|
{
|
|
unsigned long pfn;
|
|
struct page *page;
|
|
int rc = 0;
|
|
|
|
if (!hugepages_supported())
|
|
return rc;
|
|
|
|
for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << minimum_order) {
|
|
page = pfn_to_page(pfn);
|
|
rc = dissolve_free_huge_page(page);
|
|
if (rc)
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Allocates a fresh surplus page from the page allocator.
|
|
*/
|
|
static struct page *alloc_surplus_huge_page(struct hstate *h, gfp_t gfp_mask,
|
|
int nid, nodemask_t *nmask)
|
|
{
|
|
struct page *page = NULL;
|
|
|
|
if (hstate_is_gigantic(h))
|
|
return NULL;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
if (h->surplus_huge_pages >= h->nr_overcommit_huge_pages)
|
|
goto out_unlock;
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
page = alloc_fresh_huge_page(h, gfp_mask, nid, nmask, NULL);
|
|
if (!page)
|
|
return NULL;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
/*
|
|
* We could have raced with the pool size change.
|
|
* Double check that and simply deallocate the new page
|
|
* if we would end up overcommiting the surpluses. Abuse
|
|
* temporary page to workaround the nasty free_huge_page
|
|
* codeflow
|
|
*/
|
|
if (h->surplus_huge_pages >= h->nr_overcommit_huge_pages) {
|
|
SetPageHugeTemporary(page);
|
|
spin_unlock(&hugetlb_lock);
|
|
put_page(page);
|
|
return NULL;
|
|
} else {
|
|
h->surplus_huge_pages++;
|
|
h->surplus_huge_pages_node[page_to_nid(page)]++;
|
|
}
|
|
|
|
out_unlock:
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
return page;
|
|
}
|
|
|
|
struct page *alloc_migrate_huge_page(struct hstate *h, gfp_t gfp_mask,
|
|
int nid, nodemask_t *nmask)
|
|
{
|
|
struct page *page;
|
|
|
|
if (hstate_is_gigantic(h))
|
|
return NULL;
|
|
|
|
page = alloc_fresh_huge_page(h, gfp_mask, nid, nmask, NULL);
|
|
if (!page)
|
|
return NULL;
|
|
|
|
/*
|
|
* We do not account these pages as surplus because they are only
|
|
* temporary and will be released properly on the last reference
|
|
*/
|
|
SetPageHugeTemporary(page);
|
|
|
|
return page;
|
|
}
|
|
|
|
/*
|
|
* Use the VMA's mpolicy to allocate a huge page from the buddy.
|
|
*/
|
|
static
|
|
struct page *alloc_buddy_huge_page_with_mpol(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
struct page *page;
|
|
struct mempolicy *mpol;
|
|
gfp_t gfp_mask = htlb_alloc_mask(h);
|
|
int nid;
|
|
nodemask_t *nodemask;
|
|
|
|
nid = huge_node(vma, addr, gfp_mask, &mpol, &nodemask);
|
|
page = alloc_surplus_huge_page(h, gfp_mask, nid, nodemask);
|
|
mpol_cond_put(mpol);
|
|
|
|
return page;
|
|
}
|
|
|
|
/* page migration callback function */
|
|
struct page *alloc_huge_page_node(struct hstate *h, int nid)
|
|
{
|
|
gfp_t gfp_mask = htlb_alloc_mask(h);
|
|
struct page *page = NULL;
|
|
|
|
if (nid != NUMA_NO_NODE)
|
|
gfp_mask |= __GFP_THISNODE;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
if (h->free_huge_pages - h->resv_huge_pages > 0)
|
|
page = dequeue_huge_page_nodemask(h, gfp_mask, nid, NULL);
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
if (!page)
|
|
page = alloc_migrate_huge_page(h, gfp_mask, nid, NULL);
|
|
|
|
return page;
|
|
}
|
|
|
|
/* page migration callback function */
|
|
struct page *alloc_huge_page_nodemask(struct hstate *h, int preferred_nid,
|
|
nodemask_t *nmask)
|
|
{
|
|
gfp_t gfp_mask = htlb_alloc_mask(h);
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
if (h->free_huge_pages - h->resv_huge_pages > 0) {
|
|
struct page *page;
|
|
|
|
page = dequeue_huge_page_nodemask(h, gfp_mask, preferred_nid, nmask);
|
|
if (page) {
|
|
spin_unlock(&hugetlb_lock);
|
|
return page;
|
|
}
|
|
}
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
return alloc_migrate_huge_page(h, gfp_mask, preferred_nid, nmask);
|
|
}
|
|
|
|
/* mempolicy aware migration callback */
|
|
struct page *alloc_huge_page_vma(struct hstate *h, struct vm_area_struct *vma,
|
|
unsigned long address)
|
|
{
|
|
struct mempolicy *mpol;
|
|
nodemask_t *nodemask;
|
|
struct page *page;
|
|
gfp_t gfp_mask;
|
|
int node;
|
|
|
|
gfp_mask = htlb_alloc_mask(h);
|
|
node = huge_node(vma, address, gfp_mask, &mpol, &nodemask);
|
|
page = alloc_huge_page_nodemask(h, node, nodemask);
|
|
mpol_cond_put(mpol);
|
|
|
|
return page;
|
|
}
|
|
|
|
/*
|
|
* Increase the hugetlb pool such that it can accommodate a reservation
|
|
* of size 'delta'.
|
|
*/
|
|
static int gather_surplus_pages(struct hstate *h, int delta)
|
|
{
|
|
struct list_head surplus_list;
|
|
struct page *page, *tmp;
|
|
int ret, i;
|
|
int needed, allocated;
|
|
bool alloc_ok = true;
|
|
|
|
needed = (h->resv_huge_pages + delta) - h->free_huge_pages;
|
|
if (needed <= 0) {
|
|
h->resv_huge_pages += delta;
|
|
return 0;
|
|
}
|
|
|
|
allocated = 0;
|
|
INIT_LIST_HEAD(&surplus_list);
|
|
|
|
ret = -ENOMEM;
|
|
retry:
|
|
spin_unlock(&hugetlb_lock);
|
|
for (i = 0; i < needed; i++) {
|
|
page = alloc_surplus_huge_page(h, htlb_alloc_mask(h),
|
|
NUMA_NO_NODE, NULL);
|
|
if (!page) {
|
|
alloc_ok = false;
|
|
break;
|
|
}
|
|
list_add(&page->lru, &surplus_list);
|
|
cond_resched();
|
|
}
|
|
allocated += i;
|
|
|
|
/*
|
|
* After retaking hugetlb_lock, we need to recalculate 'needed'
|
|
* because either resv_huge_pages or free_huge_pages may have changed.
|
|
*/
|
|
spin_lock(&hugetlb_lock);
|
|
needed = (h->resv_huge_pages + delta) -
|
|
(h->free_huge_pages + allocated);
|
|
if (needed > 0) {
|
|
if (alloc_ok)
|
|
goto retry;
|
|
/*
|
|
* We were not able to allocate enough pages to
|
|
* satisfy the entire reservation so we free what
|
|
* we've allocated so far.
|
|
*/
|
|
goto free;
|
|
}
|
|
/*
|
|
* The surplus_list now contains _at_least_ the number of extra pages
|
|
* needed to accommodate the reservation. Add the appropriate number
|
|
* of pages to the hugetlb pool and free the extras back to the buddy
|
|
* allocator. Commit the entire reservation here to prevent another
|
|
* process from stealing the pages as they are added to the pool but
|
|
* before they are reserved.
|
|
*/
|
|
needed += allocated;
|
|
h->resv_huge_pages += delta;
|
|
ret = 0;
|
|
|
|
/* Free the needed pages to the hugetlb pool */
|
|
list_for_each_entry_safe(page, tmp, &surplus_list, lru) {
|
|
if ((--needed) < 0)
|
|
break;
|
|
/*
|
|
* This page is now managed by the hugetlb allocator and has
|
|
* no users -- drop the buddy allocator's reference.
|
|
*/
|
|
put_page_testzero(page);
|
|
VM_BUG_ON_PAGE(page_count(page), page);
|
|
enqueue_huge_page(h, page);
|
|
}
|
|
free:
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
/* Free unnecessary surplus pages to the buddy allocator */
|
|
list_for_each_entry_safe(page, tmp, &surplus_list, lru)
|
|
put_page(page);
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* This routine has two main purposes:
|
|
* 1) Decrement the reservation count (resv_huge_pages) by the value passed
|
|
* in unused_resv_pages. This corresponds to the prior adjustments made
|
|
* to the associated reservation map.
|
|
* 2) Free any unused surplus pages that may have been allocated to satisfy
|
|
* the reservation. As many as unused_resv_pages may be freed.
|
|
*
|
|
* Called with hugetlb_lock held. However, the lock could be dropped (and
|
|
* reacquired) during calls to cond_resched_lock. Whenever dropping the lock,
|
|
* we must make sure nobody else can claim pages we are in the process of
|
|
* freeing. Do this by ensuring resv_huge_page always is greater than the
|
|
* number of huge pages we plan to free when dropping the lock.
|
|
*/
|
|
static void return_unused_surplus_pages(struct hstate *h,
|
|
unsigned long unused_resv_pages)
|
|
{
|
|
unsigned long nr_pages;
|
|
|
|
/* Cannot return gigantic pages currently */
|
|
if (hstate_is_gigantic(h))
|
|
goto out;
|
|
|
|
/*
|
|
* Part (or even all) of the reservation could have been backed
|
|
* by pre-allocated pages. Only free surplus pages.
|
|
*/
|
|
nr_pages = min(unused_resv_pages, h->surplus_huge_pages);
|
|
|
|
/*
|
|
* We want to release as many surplus pages as possible, spread
|
|
* evenly across all nodes with memory. Iterate across these nodes
|
|
* until we can no longer free unreserved surplus pages. This occurs
|
|
* when the nodes with surplus pages have no free pages.
|
|
* free_pool_huge_page() will balance the the freed pages across the
|
|
* on-line nodes with memory and will handle the hstate accounting.
|
|
*
|
|
* Note that we decrement resv_huge_pages as we free the pages. If
|
|
* we drop the lock, resv_huge_pages will still be sufficiently large
|
|
* to cover subsequent pages we may free.
|
|
*/
|
|
while (nr_pages--) {
|
|
h->resv_huge_pages--;
|
|
unused_resv_pages--;
|
|
if (!free_pool_huge_page(h, &node_states[N_MEMORY], 1))
|
|
goto out;
|
|
cond_resched_lock(&hugetlb_lock);
|
|
}
|
|
|
|
out:
|
|
/* Fully uncommit the reservation */
|
|
h->resv_huge_pages -= unused_resv_pages;
|
|
}
|
|
|
|
|
|
/*
|
|
* vma_needs_reservation, vma_commit_reservation and vma_end_reservation
|
|
* are used by the huge page allocation routines to manage reservations.
|
|
*
|
|
* vma_needs_reservation is called to determine if the huge page at addr
|
|
* within the vma has an associated reservation. If a reservation is
|
|
* needed, the value 1 is returned. The caller is then responsible for
|
|
* managing the global reservation and subpool usage counts. After
|
|
* the huge page has been allocated, vma_commit_reservation is called
|
|
* to add the page to the reservation map. If the page allocation fails,
|
|
* the reservation must be ended instead of committed. vma_end_reservation
|
|
* is called in such cases.
|
|
*
|
|
* In the normal case, vma_commit_reservation returns the same value
|
|
* as the preceding vma_needs_reservation call. The only time this
|
|
* is not the case is if a reserve map was changed between calls. It
|
|
* is the responsibility of the caller to notice the difference and
|
|
* take appropriate action.
|
|
*
|
|
* vma_add_reservation is used in error paths where a reservation must
|
|
* be restored when a newly allocated huge page must be freed. It is
|
|
* to be called after calling vma_needs_reservation to determine if a
|
|
* reservation exists.
|
|
*/
|
|
enum vma_resv_mode {
|
|
VMA_NEEDS_RESV,
|
|
VMA_COMMIT_RESV,
|
|
VMA_END_RESV,
|
|
VMA_ADD_RESV,
|
|
};
|
|
static long __vma_reservation_common(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long addr,
|
|
enum vma_resv_mode mode)
|
|
{
|
|
struct resv_map *resv;
|
|
pgoff_t idx;
|
|
long ret;
|
|
|
|
resv = vma_resv_map(vma);
|
|
if (!resv)
|
|
return 1;
|
|
|
|
idx = vma_hugecache_offset(h, vma, addr);
|
|
switch (mode) {
|
|
case VMA_NEEDS_RESV:
|
|
ret = region_chg(resv, idx, idx + 1);
|
|
break;
|
|
case VMA_COMMIT_RESV:
|
|
ret = region_add(resv, idx, idx + 1);
|
|
break;
|
|
case VMA_END_RESV:
|
|
region_abort(resv, idx, idx + 1);
|
|
ret = 0;
|
|
break;
|
|
case VMA_ADD_RESV:
|
|
if (vma->vm_flags & VM_MAYSHARE)
|
|
ret = region_add(resv, idx, idx + 1);
|
|
else {
|
|
region_abort(resv, idx, idx + 1);
|
|
ret = region_del(resv, idx, idx + 1);
|
|
}
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
if (vma->vm_flags & VM_MAYSHARE)
|
|
return ret;
|
|
else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER) && ret >= 0) {
|
|
/*
|
|
* In most cases, reserves always exist for private mappings.
|
|
* However, a file associated with mapping could have been
|
|
* hole punched or truncated after reserves were consumed.
|
|
* As subsequent fault on such a range will not use reserves.
|
|
* Subtle - The reserve map for private mappings has the
|
|
* opposite meaning than that of shared mappings. If NO
|
|
* entry is in the reserve map, it means a reservation exists.
|
|
* If an entry exists in the reserve map, it means the
|
|
* reservation has already been consumed. As a result, the
|
|
* return value of this routine is the opposite of the
|
|
* value returned from reserve map manipulation routines above.
|
|
*/
|
|
if (ret)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
else
|
|
return ret < 0 ? ret : 0;
|
|
}
|
|
|
|
static long vma_needs_reservation(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
return __vma_reservation_common(h, vma, addr, VMA_NEEDS_RESV);
|
|
}
|
|
|
|
static long vma_commit_reservation(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
return __vma_reservation_common(h, vma, addr, VMA_COMMIT_RESV);
|
|
}
|
|
|
|
static void vma_end_reservation(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
(void)__vma_reservation_common(h, vma, addr, VMA_END_RESV);
|
|
}
|
|
|
|
static long vma_add_reservation(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
return __vma_reservation_common(h, vma, addr, VMA_ADD_RESV);
|
|
}
|
|
|
|
/*
|
|
* This routine is called to restore a reservation on error paths. In the
|
|
* specific error paths, a huge page was allocated (via alloc_huge_page)
|
|
* and is about to be freed. If a reservation for the page existed,
|
|
* alloc_huge_page would have consumed the reservation and set PagePrivate
|
|
* in the newly allocated page. When the page is freed via free_huge_page,
|
|
* the global reservation count will be incremented if PagePrivate is set.
|
|
* However, free_huge_page can not adjust the reserve map. Adjust the
|
|
* reserve map here to be consistent with global reserve count adjustments
|
|
* to be made by free_huge_page.
|
|
*/
|
|
static void restore_reserve_on_error(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long address,
|
|
struct page *page)
|
|
{
|
|
if (unlikely(PagePrivate(page))) {
|
|
long rc = vma_needs_reservation(h, vma, address);
|
|
|
|
if (unlikely(rc < 0)) {
|
|
/*
|
|
* Rare out of memory condition in reserve map
|
|
* manipulation. Clear PagePrivate so that
|
|
* global reserve count will not be incremented
|
|
* by free_huge_page. This will make it appear
|
|
* as though the reservation for this page was
|
|
* consumed. This may prevent the task from
|
|
* faulting in the page at a later time. This
|
|
* is better than inconsistent global huge page
|
|
* accounting of reserve counts.
|
|
*/
|
|
ClearPagePrivate(page);
|
|
} else if (rc) {
|
|
rc = vma_add_reservation(h, vma, address);
|
|
if (unlikely(rc < 0))
|
|
/*
|
|
* See above comment about rare out of
|
|
* memory condition.
|
|
*/
|
|
ClearPagePrivate(page);
|
|
} else
|
|
vma_end_reservation(h, vma, address);
|
|
}
|
|
}
|
|
|
|
struct page *alloc_huge_page(struct vm_area_struct *vma,
|
|
unsigned long addr, int avoid_reserve)
|
|
{
|
|
struct hugepage_subpool *spool = subpool_vma(vma);
|
|
struct hstate *h = hstate_vma(vma);
|
|
struct page *page;
|
|
long map_chg, map_commit;
|
|
long gbl_chg;
|
|
int ret, idx;
|
|
struct hugetlb_cgroup *h_cg;
|
|
|
|
idx = hstate_index(h);
|
|
/*
|
|
* Examine the region/reserve map to determine if the process
|
|
* has a reservation for the page to be allocated. A return
|
|
* code of zero indicates a reservation exists (no change).
|
|
*/
|
|
map_chg = gbl_chg = vma_needs_reservation(h, vma, addr);
|
|
if (map_chg < 0)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
/*
|
|
* Processes that did not create the mapping will have no
|
|
* reserves as indicated by the region/reserve map. Check
|
|
* that the allocation will not exceed the subpool limit.
|
|
* Allocations for MAP_NORESERVE mappings also need to be
|
|
* checked against any subpool limit.
|
|
*/
|
|
if (map_chg || avoid_reserve) {
|
|
gbl_chg = hugepage_subpool_get_pages(spool, 1);
|
|
if (gbl_chg < 0) {
|
|
vma_end_reservation(h, vma, addr);
|
|
return ERR_PTR(-ENOSPC);
|
|
}
|
|
|
|
/*
|
|
* Even though there was no reservation in the region/reserve
|
|
* map, there could be reservations associated with the
|
|
* subpool that can be used. This would be indicated if the
|
|
* return value of hugepage_subpool_get_pages() is zero.
|
|
* However, if avoid_reserve is specified we still avoid even
|
|
* the subpool reservations.
|
|
*/
|
|
if (avoid_reserve)
|
|
gbl_chg = 1;
|
|
}
|
|
|
|
ret = hugetlb_cgroup_charge_cgroup(idx, pages_per_huge_page(h), &h_cg);
|
|
if (ret)
|
|
goto out_subpool_put;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
/*
|
|
* glb_chg is passed to indicate whether or not a page must be taken
|
|
* from the global free pool (global change). gbl_chg == 0 indicates
|
|
* a reservation exists for the allocation.
|
|
*/
|
|
page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve, gbl_chg);
|
|
if (!page) {
|
|
spin_unlock(&hugetlb_lock);
|
|
page = alloc_buddy_huge_page_with_mpol(h, vma, addr);
|
|
if (!page)
|
|
goto out_uncharge_cgroup;
|
|
spin_lock(&hugetlb_lock);
|
|
if (!avoid_reserve && vma_has_reserves(vma, gbl_chg)) {
|
|
SetPagePrivate(page);
|
|
h->resv_huge_pages--;
|
|
}
|
|
list_move(&page->lru, &h->hugepage_activelist);
|
|
/* Fall through */
|
|
}
|
|
hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h), h_cg, page);
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
set_page_private(page, (unsigned long)spool);
|
|
|
|
map_commit = vma_commit_reservation(h, vma, addr);
|
|
if (unlikely(map_chg > map_commit)) {
|
|
/*
|
|
* The page was added to the reservation map between
|
|
* vma_needs_reservation and vma_commit_reservation.
|
|
* This indicates a race with hugetlb_reserve_pages.
|
|
* Adjust for the subpool count incremented above AND
|
|
* in hugetlb_reserve_pages for the same page. Also,
|
|
* the reservation count added in hugetlb_reserve_pages
|
|
* no longer applies.
|
|
*/
|
|
long rsv_adjust;
|
|
|
|
rsv_adjust = hugepage_subpool_put_pages(spool, 1);
|
|
hugetlb_acct_memory(h, -rsv_adjust);
|
|
}
|
|
return page;
|
|
|
|
out_uncharge_cgroup:
|
|
hugetlb_cgroup_uncharge_cgroup(idx, pages_per_huge_page(h), h_cg);
|
|
out_subpool_put:
|
|
if (map_chg || avoid_reserve)
|
|
hugepage_subpool_put_pages(spool, 1);
|
|
vma_end_reservation(h, vma, addr);
|
|
return ERR_PTR(-ENOSPC);
|
|
}
|
|
|
|
int alloc_bootmem_huge_page(struct hstate *h)
|
|
__attribute__ ((weak, alias("__alloc_bootmem_huge_page")));
|
|
int __alloc_bootmem_huge_page(struct hstate *h)
|
|
{
|
|
struct huge_bootmem_page *m;
|
|
int nr_nodes, node;
|
|
|
|
for_each_node_mask_to_alloc(h, nr_nodes, node, &node_states[N_MEMORY]) {
|
|
void *addr;
|
|
|
|
addr = memblock_alloc_try_nid_raw(
|
|
huge_page_size(h), huge_page_size(h),
|
|
0, MEMBLOCK_ALLOC_ACCESSIBLE, node);
|
|
if (addr) {
|
|
/*
|
|
* Use the beginning of the huge page to store the
|
|
* huge_bootmem_page struct (until gather_bootmem
|
|
* puts them into the mem_map).
|
|
*/
|
|
m = addr;
|
|
goto found;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
found:
|
|
BUG_ON(!IS_ALIGNED(virt_to_phys(m), huge_page_size(h)));
|
|
/* Put them into a private list first because mem_map is not up yet */
|
|
INIT_LIST_HEAD(&m->list);
|
|
list_add(&m->list, &huge_boot_pages);
|
|
m->hstate = h;
|
|
return 1;
|
|
}
|
|
|
|
static void __init prep_compound_huge_page(struct page *page,
|
|
unsigned int order)
|
|
{
|
|
if (unlikely(order > (MAX_ORDER - 1)))
|
|
prep_compound_gigantic_page(page, order);
|
|
else
|
|
prep_compound_page(page, order);
|
|
}
|
|
|
|
/* Put bootmem huge pages into the standard lists after mem_map is up */
|
|
static void __init gather_bootmem_prealloc(void)
|
|
{
|
|
struct huge_bootmem_page *m;
|
|
|
|
list_for_each_entry(m, &huge_boot_pages, list) {
|
|
struct page *page = virt_to_page(m);
|
|
struct hstate *h = m->hstate;
|
|
|
|
WARN_ON(page_count(page) != 1);
|
|
prep_compound_huge_page(page, h->order);
|
|
WARN_ON(PageReserved(page));
|
|
prep_new_huge_page(h, page, page_to_nid(page));
|
|
put_page(page); /* free it into the hugepage allocator */
|
|
|
|
/*
|
|
* If we had gigantic hugepages allocated at boot time, we need
|
|
* to restore the 'stolen' pages to totalram_pages in order to
|
|
* fix confusing memory reports from free(1) and another
|
|
* side-effects, like CommitLimit going negative.
|
|
*/
|
|
if (hstate_is_gigantic(h))
|
|
adjust_managed_page_count(page, 1 << h->order);
|
|
cond_resched();
|
|
}
|
|
}
|
|
|
|
static void __init hugetlb_hstate_alloc_pages(struct hstate *h)
|
|
{
|
|
unsigned long i;
|
|
nodemask_t *node_alloc_noretry;
|
|
|
|
if (!hstate_is_gigantic(h)) {
|
|
/*
|
|
* Bit mask controlling how hard we retry per-node allocations.
|
|
* Ignore errors as lower level routines can deal with
|
|
* node_alloc_noretry == NULL. If this kmalloc fails at boot
|
|
* time, we are likely in bigger trouble.
|
|
*/
|
|
node_alloc_noretry = kmalloc(sizeof(*node_alloc_noretry),
|
|
GFP_KERNEL);
|
|
} else {
|
|
/* allocations done at boot time */
|
|
node_alloc_noretry = NULL;
|
|
}
|
|
|
|
/* bit mask controlling how hard we retry per-node allocations */
|
|
if (node_alloc_noretry)
|
|
nodes_clear(*node_alloc_noretry);
|
|
|
|
for (i = 0; i < h->max_huge_pages; ++i) {
|
|
if (hstate_is_gigantic(h)) {
|
|
if (!alloc_bootmem_huge_page(h))
|
|
break;
|
|
} else if (!alloc_pool_huge_page(h,
|
|
&node_states[N_MEMORY],
|
|
node_alloc_noretry))
|
|
break;
|
|
cond_resched();
|
|
}
|
|
if (i < h->max_huge_pages) {
|
|
char buf[32];
|
|
|
|
string_get_size(huge_page_size(h), 1, STRING_UNITS_2, buf, 32);
|
|
pr_warn("HugeTLB: allocating %lu of page size %s failed. Only allocated %lu hugepages.\n",
|
|
h->max_huge_pages, buf, i);
|
|
h->max_huge_pages = i;
|
|
}
|
|
|
|
kfree(node_alloc_noretry);
|
|
}
|
|
|
|
static void __init hugetlb_init_hstates(void)
|
|
{
|
|
struct hstate *h;
|
|
|
|
for_each_hstate(h) {
|
|
if (minimum_order > huge_page_order(h))
|
|
minimum_order = huge_page_order(h);
|
|
|
|
/* oversize hugepages were init'ed in early boot */
|
|
if (!hstate_is_gigantic(h))
|
|
hugetlb_hstate_alloc_pages(h);
|
|
}
|
|
VM_BUG_ON(minimum_order == UINT_MAX);
|
|
}
|
|
|
|
static void __init report_hugepages(void)
|
|
{
|
|
struct hstate *h;
|
|
|
|
for_each_hstate(h) {
|
|
char buf[32];
|
|
|
|
string_get_size(huge_page_size(h), 1, STRING_UNITS_2, buf, 32);
|
|
pr_info("HugeTLB registered %s page size, pre-allocated %ld pages\n",
|
|
buf, h->free_huge_pages);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_HIGHMEM
|
|
static void try_to_free_low(struct hstate *h, unsigned long count,
|
|
nodemask_t *nodes_allowed)
|
|
{
|
|
int i;
|
|
|
|
if (hstate_is_gigantic(h))
|
|
return;
|
|
|
|
for_each_node_mask(i, *nodes_allowed) {
|
|
struct page *page, *next;
|
|
struct list_head *freel = &h->hugepage_freelists[i];
|
|
list_for_each_entry_safe(page, next, freel, lru) {
|
|
if (count >= h->nr_huge_pages)
|
|
return;
|
|
if (PageHighMem(page))
|
|
continue;
|
|
list_del(&page->lru);
|
|
update_and_free_page(h, page);
|
|
h->free_huge_pages--;
|
|
h->free_huge_pages_node[page_to_nid(page)]--;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
static inline void try_to_free_low(struct hstate *h, unsigned long count,
|
|
nodemask_t *nodes_allowed)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Increment or decrement surplus_huge_pages. Keep node-specific counters
|
|
* balanced by operating on them in a round-robin fashion.
|
|
* Returns 1 if an adjustment was made.
|
|
*/
|
|
static int adjust_pool_surplus(struct hstate *h, nodemask_t *nodes_allowed,
|
|
int delta)
|
|
{
|
|
int nr_nodes, node;
|
|
|
|
VM_BUG_ON(delta != -1 && delta != 1);
|
|
|
|
if (delta < 0) {
|
|
for_each_node_mask_to_alloc(h, nr_nodes, node, nodes_allowed) {
|
|
if (h->surplus_huge_pages_node[node])
|
|
goto found;
|
|
}
|
|
} else {
|
|
for_each_node_mask_to_free(h, nr_nodes, node, nodes_allowed) {
|
|
if (h->surplus_huge_pages_node[node] <
|
|
h->nr_huge_pages_node[node])
|
|
goto found;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
found:
|
|
h->surplus_huge_pages += delta;
|
|
h->surplus_huge_pages_node[node] += delta;
|
|
return 1;
|
|
}
|
|
|
|
#define persistent_huge_pages(h) (h->nr_huge_pages - h->surplus_huge_pages)
|
|
static int set_max_huge_pages(struct hstate *h, unsigned long count, int nid,
|
|
nodemask_t *nodes_allowed)
|
|
{
|
|
unsigned long min_count, ret;
|
|
NODEMASK_ALLOC(nodemask_t, node_alloc_noretry, GFP_KERNEL);
|
|
|
|
/*
|
|
* Bit mask controlling how hard we retry per-node allocations.
|
|
* If we can not allocate the bit mask, do not attempt to allocate
|
|
* the requested huge pages.
|
|
*/
|
|
if (node_alloc_noretry)
|
|
nodes_clear(*node_alloc_noretry);
|
|
else
|
|
return -ENOMEM;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
/*
|
|
* Check for a node specific request.
|
|
* Changing node specific huge page count may require a corresponding
|
|
* change to the global count. In any case, the passed node mask
|
|
* (nodes_allowed) will restrict alloc/free to the specified node.
|
|
*/
|
|
if (nid != NUMA_NO_NODE) {
|
|
unsigned long old_count = count;
|
|
|
|
count += h->nr_huge_pages - h->nr_huge_pages_node[nid];
|
|
/*
|
|
* User may have specified a large count value which caused the
|
|
* above calculation to overflow. In this case, they wanted
|
|
* to allocate as many huge pages as possible. Set count to
|
|
* largest possible value to align with their intention.
|
|
*/
|
|
if (count < old_count)
|
|
count = ULONG_MAX;
|
|
}
|
|
|
|
/*
|
|
* Gigantic pages runtime allocation depend on the capability for large
|
|
* page range allocation.
|
|
* If the system does not provide this feature, return an error when
|
|
* the user tries to allocate gigantic pages but let the user free the
|
|
* boottime allocated gigantic pages.
|
|
*/
|
|
if (hstate_is_gigantic(h) && !IS_ENABLED(CONFIG_CONTIG_ALLOC)) {
|
|
if (count > persistent_huge_pages(h)) {
|
|
spin_unlock(&hugetlb_lock);
|
|
NODEMASK_FREE(node_alloc_noretry);
|
|
return -EINVAL;
|
|
}
|
|
/* Fall through to decrease pool */
|
|
}
|
|
|
|
/*
|
|
* Increase the pool size
|
|
* First take pages out of surplus state. Then make up the
|
|
* remaining difference by allocating fresh huge pages.
|
|
*
|
|
* We might race with alloc_surplus_huge_page() here and be unable
|
|
* to convert a surplus huge page to a normal huge page. That is
|
|
* not critical, though, it just means the overall size of the
|
|
* pool might be one hugepage larger than it needs to be, but
|
|
* within all the constraints specified by the sysctls.
|
|
*/
|
|
while (h->surplus_huge_pages && count > persistent_huge_pages(h)) {
|
|
if (!adjust_pool_surplus(h, nodes_allowed, -1))
|
|
break;
|
|
}
|
|
|
|
while (count > persistent_huge_pages(h)) {
|
|
/*
|
|
* If this allocation races such that we no longer need the
|
|
* page, free_huge_page will handle it by freeing the page
|
|
* and reducing the surplus.
|
|
*/
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
/* yield cpu to avoid soft lockup */
|
|
cond_resched();
|
|
|
|
ret = alloc_pool_huge_page(h, nodes_allowed,
|
|
node_alloc_noretry);
|
|
spin_lock(&hugetlb_lock);
|
|
if (!ret)
|
|
goto out;
|
|
|
|
/* Bail for signals. Probably ctrl-c from user */
|
|
if (signal_pending(current))
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Decrease the pool size
|
|
* First return free pages to the buddy allocator (being careful
|
|
* to keep enough around to satisfy reservations). Then place
|
|
* pages into surplus state as needed so the pool will shrink
|
|
* to the desired size as pages become free.
|
|
*
|
|
* By placing pages into the surplus state independent of the
|
|
* overcommit value, we are allowing the surplus pool size to
|
|
* exceed overcommit. There are few sane options here. Since
|
|
* alloc_surplus_huge_page() is checking the global counter,
|
|
* though, we'll note that we're not allowed to exceed surplus
|
|
* and won't grow the pool anywhere else. Not until one of the
|
|
* sysctls are changed, or the surplus pages go out of use.
|
|
*/
|
|
min_count = h->resv_huge_pages + h->nr_huge_pages - h->free_huge_pages;
|
|
min_count = max(count, min_count);
|
|
try_to_free_low(h, min_count, nodes_allowed);
|
|
while (min_count < persistent_huge_pages(h)) {
|
|
if (!free_pool_huge_page(h, nodes_allowed, 0))
|
|
break;
|
|
cond_resched_lock(&hugetlb_lock);
|
|
}
|
|
while (count < persistent_huge_pages(h)) {
|
|
if (!adjust_pool_surplus(h, nodes_allowed, 1))
|
|
break;
|
|
}
|
|
out:
|
|
h->max_huge_pages = persistent_huge_pages(h);
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
NODEMASK_FREE(node_alloc_noretry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define HSTATE_ATTR_RO(_name) \
|
|
static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
|
|
|
|
#define HSTATE_ATTR(_name) \
|
|
static struct kobj_attribute _name##_attr = \
|
|
__ATTR(_name, 0644, _name##_show, _name##_store)
|
|
|
|
static struct kobject *hugepages_kobj;
|
|
static struct kobject *hstate_kobjs[HUGE_MAX_HSTATE];
|
|
|
|
static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp);
|
|
|
|
static struct hstate *kobj_to_hstate(struct kobject *kobj, int *nidp)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < HUGE_MAX_HSTATE; i++)
|
|
if (hstate_kobjs[i] == kobj) {
|
|
if (nidp)
|
|
*nidp = NUMA_NO_NODE;
|
|
return &hstates[i];
|
|
}
|
|
|
|
return kobj_to_node_hstate(kobj, nidp);
|
|
}
|
|
|
|
static ssize_t nr_hugepages_show_common(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct hstate *h;
|
|
unsigned long nr_huge_pages;
|
|
int nid;
|
|
|
|
h = kobj_to_hstate(kobj, &nid);
|
|
if (nid == NUMA_NO_NODE)
|
|
nr_huge_pages = h->nr_huge_pages;
|
|
else
|
|
nr_huge_pages = h->nr_huge_pages_node[nid];
|
|
|
|
return sprintf(buf, "%lu\n", nr_huge_pages);
|
|
}
|
|
|
|
static ssize_t __nr_hugepages_store_common(bool obey_mempolicy,
|
|
struct hstate *h, int nid,
|
|
unsigned long count, size_t len)
|
|
{
|
|
int err;
|
|
nodemask_t nodes_allowed, *n_mask;
|
|
|
|
if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported())
|
|
return -EINVAL;
|
|
|
|
if (nid == NUMA_NO_NODE) {
|
|
/*
|
|
* global hstate attribute
|
|
*/
|
|
if (!(obey_mempolicy &&
|
|
init_nodemask_of_mempolicy(&nodes_allowed)))
|
|
n_mask = &node_states[N_MEMORY];
|
|
else
|
|
n_mask = &nodes_allowed;
|
|
} else {
|
|
/*
|
|
* Node specific request. count adjustment happens in
|
|
* set_max_huge_pages() after acquiring hugetlb_lock.
|
|
*/
|
|
init_nodemask_of_node(&nodes_allowed, nid);
|
|
n_mask = &nodes_allowed;
|
|
}
|
|
|
|
err = set_max_huge_pages(h, count, nid, n_mask);
|
|
|
|
return err ? err : len;
|
|
}
|
|
|
|
static ssize_t nr_hugepages_store_common(bool obey_mempolicy,
|
|
struct kobject *kobj, const char *buf,
|
|
size_t len)
|
|
{
|
|
struct hstate *h;
|
|
unsigned long count;
|
|
int nid;
|
|
int err;
|
|
|
|
err = kstrtoul(buf, 10, &count);
|
|
if (err)
|
|
return err;
|
|
|
|
h = kobj_to_hstate(kobj, &nid);
|
|
return __nr_hugepages_store_common(obey_mempolicy, h, nid, count, len);
|
|
}
|
|
|
|
static ssize_t nr_hugepages_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
return nr_hugepages_show_common(kobj, attr, buf);
|
|
}
|
|
|
|
static ssize_t nr_hugepages_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr, const char *buf, size_t len)
|
|
{
|
|
return nr_hugepages_store_common(false, kobj, buf, len);
|
|
}
|
|
HSTATE_ATTR(nr_hugepages);
|
|
|
|
#ifdef CONFIG_NUMA
|
|
|
|
/*
|
|
* hstate attribute for optionally mempolicy-based constraint on persistent
|
|
* huge page alloc/free.
|
|
*/
|
|
static ssize_t nr_hugepages_mempolicy_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
return nr_hugepages_show_common(kobj, attr, buf);
|
|
}
|
|
|
|
static ssize_t nr_hugepages_mempolicy_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr, const char *buf, size_t len)
|
|
{
|
|
return nr_hugepages_store_common(true, kobj, buf, len);
|
|
}
|
|
HSTATE_ATTR(nr_hugepages_mempolicy);
|
|
#endif
|
|
|
|
|
|
static ssize_t nr_overcommit_hugepages_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct hstate *h = kobj_to_hstate(kobj, NULL);
|
|
return sprintf(buf, "%lu\n", h->nr_overcommit_huge_pages);
|
|
}
|
|
|
|
static ssize_t nr_overcommit_hugepages_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int err;
|
|
unsigned long input;
|
|
struct hstate *h = kobj_to_hstate(kobj, NULL);
|
|
|
|
if (hstate_is_gigantic(h))
|
|
return -EINVAL;
|
|
|
|
err = kstrtoul(buf, 10, &input);
|
|
if (err)
|
|
return err;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
h->nr_overcommit_huge_pages = input;
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
return count;
|
|
}
|
|
HSTATE_ATTR(nr_overcommit_hugepages);
|
|
|
|
static ssize_t free_hugepages_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct hstate *h;
|
|
unsigned long free_huge_pages;
|
|
int nid;
|
|
|
|
h = kobj_to_hstate(kobj, &nid);
|
|
if (nid == NUMA_NO_NODE)
|
|
free_huge_pages = h->free_huge_pages;
|
|
else
|
|
free_huge_pages = h->free_huge_pages_node[nid];
|
|
|
|
return sprintf(buf, "%lu\n", free_huge_pages);
|
|
}
|
|
HSTATE_ATTR_RO(free_hugepages);
|
|
|
|
static ssize_t resv_hugepages_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct hstate *h = kobj_to_hstate(kobj, NULL);
|
|
return sprintf(buf, "%lu\n", h->resv_huge_pages);
|
|
}
|
|
HSTATE_ATTR_RO(resv_hugepages);
|
|
|
|
static ssize_t surplus_hugepages_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct hstate *h;
|
|
unsigned long surplus_huge_pages;
|
|
int nid;
|
|
|
|
h = kobj_to_hstate(kobj, &nid);
|
|
if (nid == NUMA_NO_NODE)
|
|
surplus_huge_pages = h->surplus_huge_pages;
|
|
else
|
|
surplus_huge_pages = h->surplus_huge_pages_node[nid];
|
|
|
|
return sprintf(buf, "%lu\n", surplus_huge_pages);
|
|
}
|
|
HSTATE_ATTR_RO(surplus_hugepages);
|
|
|
|
static struct attribute *hstate_attrs[] = {
|
|
&nr_hugepages_attr.attr,
|
|
&nr_overcommit_hugepages_attr.attr,
|
|
&free_hugepages_attr.attr,
|
|
&resv_hugepages_attr.attr,
|
|
&surplus_hugepages_attr.attr,
|
|
#ifdef CONFIG_NUMA
|
|
&nr_hugepages_mempolicy_attr.attr,
|
|
#endif
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group hstate_attr_group = {
|
|
.attrs = hstate_attrs,
|
|
};
|
|
|
|
static int hugetlb_sysfs_add_hstate(struct hstate *h, struct kobject *parent,
|
|
struct kobject **hstate_kobjs,
|
|
const struct attribute_group *hstate_attr_group)
|
|
{
|
|
int retval;
|
|
int hi = hstate_index(h);
|
|
|
|
hstate_kobjs[hi] = kobject_create_and_add(h->name, parent);
|
|
if (!hstate_kobjs[hi])
|
|
return -ENOMEM;
|
|
|
|
retval = sysfs_create_group(hstate_kobjs[hi], hstate_attr_group);
|
|
if (retval) {
|
|
kobject_put(hstate_kobjs[hi]);
|
|
hstate_kobjs[hi] = NULL;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void __init hugetlb_sysfs_init(void)
|
|
{
|
|
struct hstate *h;
|
|
int err;
|
|
|
|
hugepages_kobj = kobject_create_and_add("hugepages", mm_kobj);
|
|
if (!hugepages_kobj)
|
|
return;
|
|
|
|
for_each_hstate(h) {
|
|
err = hugetlb_sysfs_add_hstate(h, hugepages_kobj,
|
|
hstate_kobjs, &hstate_attr_group);
|
|
if (err)
|
|
pr_err("Hugetlb: Unable to add hstate %s", h->name);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_NUMA
|
|
|
|
/*
|
|
* node_hstate/s - associate per node hstate attributes, via their kobjects,
|
|
* with node devices in node_devices[] using a parallel array. The array
|
|
* index of a node device or _hstate == node id.
|
|
* This is here to avoid any static dependency of the node device driver, in
|
|
* the base kernel, on the hugetlb module.
|
|
*/
|
|
struct node_hstate {
|
|
struct kobject *hugepages_kobj;
|
|
struct kobject *hstate_kobjs[HUGE_MAX_HSTATE];
|
|
};
|
|
static struct node_hstate node_hstates[MAX_NUMNODES];
|
|
|
|
/*
|
|
* A subset of global hstate attributes for node devices
|
|
*/
|
|
static struct attribute *per_node_hstate_attrs[] = {
|
|
&nr_hugepages_attr.attr,
|
|
&free_hugepages_attr.attr,
|
|
&surplus_hugepages_attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group per_node_hstate_attr_group = {
|
|
.attrs = per_node_hstate_attrs,
|
|
};
|
|
|
|
/*
|
|
* kobj_to_node_hstate - lookup global hstate for node device hstate attr kobj.
|
|
* Returns node id via non-NULL nidp.
|
|
*/
|
|
static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp)
|
|
{
|
|
int nid;
|
|
|
|
for (nid = 0; nid < nr_node_ids; nid++) {
|
|
struct node_hstate *nhs = &node_hstates[nid];
|
|
int i;
|
|
for (i = 0; i < HUGE_MAX_HSTATE; i++)
|
|
if (nhs->hstate_kobjs[i] == kobj) {
|
|
if (nidp)
|
|
*nidp = nid;
|
|
return &hstates[i];
|
|
}
|
|
}
|
|
|
|
BUG();
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Unregister hstate attributes from a single node device.
|
|
* No-op if no hstate attributes attached.
|
|
*/
|
|
static void hugetlb_unregister_node(struct node *node)
|
|
{
|
|
struct hstate *h;
|
|
struct node_hstate *nhs = &node_hstates[node->dev.id];
|
|
|
|
if (!nhs->hugepages_kobj)
|
|
return; /* no hstate attributes */
|
|
|
|
for_each_hstate(h) {
|
|
int idx = hstate_index(h);
|
|
if (nhs->hstate_kobjs[idx]) {
|
|
kobject_put(nhs->hstate_kobjs[idx]);
|
|
nhs->hstate_kobjs[idx] = NULL;
|
|
}
|
|
}
|
|
|
|
kobject_put(nhs->hugepages_kobj);
|
|
nhs->hugepages_kobj = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Register hstate attributes for a single node device.
|
|
* No-op if attributes already registered.
|
|
*/
|
|
static void hugetlb_register_node(struct node *node)
|
|
{
|
|
struct hstate *h;
|
|
struct node_hstate *nhs = &node_hstates[node->dev.id];
|
|
int err;
|
|
|
|
if (nhs->hugepages_kobj)
|
|
return; /* already allocated */
|
|
|
|
nhs->hugepages_kobj = kobject_create_and_add("hugepages",
|
|
&node->dev.kobj);
|
|
if (!nhs->hugepages_kobj)
|
|
return;
|
|
|
|
for_each_hstate(h) {
|
|
err = hugetlb_sysfs_add_hstate(h, nhs->hugepages_kobj,
|
|
nhs->hstate_kobjs,
|
|
&per_node_hstate_attr_group);
|
|
if (err) {
|
|
pr_err("Hugetlb: Unable to add hstate %s for node %d\n",
|
|
h->name, node->dev.id);
|
|
hugetlb_unregister_node(node);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* hugetlb init time: register hstate attributes for all registered node
|
|
* devices of nodes that have memory. All on-line nodes should have
|
|
* registered their associated device by this time.
|
|
*/
|
|
static void __init hugetlb_register_all_nodes(void)
|
|
{
|
|
int nid;
|
|
|
|
for_each_node_state(nid, N_MEMORY) {
|
|
struct node *node = node_devices[nid];
|
|
if (node->dev.id == nid)
|
|
hugetlb_register_node(node);
|
|
}
|
|
|
|
/*
|
|
* Let the node device driver know we're here so it can
|
|
* [un]register hstate attributes on node hotplug.
|
|
*/
|
|
register_hugetlbfs_with_node(hugetlb_register_node,
|
|
hugetlb_unregister_node);
|
|
}
|
|
#else /* !CONFIG_NUMA */
|
|
|
|
static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp)
|
|
{
|
|
BUG();
|
|
if (nidp)
|
|
*nidp = -1;
|
|
return NULL;
|
|
}
|
|
|
|
static void hugetlb_register_all_nodes(void) { }
|
|
|
|
#endif
|
|
|
|
static int __init hugetlb_init(void)
|
|
{
|
|
int i;
|
|
|
|
if (!hugepages_supported())
|
|
return 0;
|
|
|
|
if (!size_to_hstate(default_hstate_size)) {
|
|
if (default_hstate_size != 0) {
|
|
pr_err("HugeTLB: unsupported default_hugepagesz %lu. Reverting to %lu\n",
|
|
default_hstate_size, HPAGE_SIZE);
|
|
}
|
|
|
|
default_hstate_size = HPAGE_SIZE;
|
|
if (!size_to_hstate(default_hstate_size))
|
|
hugetlb_add_hstate(HUGETLB_PAGE_ORDER);
|
|
}
|
|
default_hstate_idx = hstate_index(size_to_hstate(default_hstate_size));
|
|
if (default_hstate_max_huge_pages) {
|
|
if (!default_hstate.max_huge_pages)
|
|
default_hstate.max_huge_pages = default_hstate_max_huge_pages;
|
|
}
|
|
|
|
hugetlb_init_hstates();
|
|
gather_bootmem_prealloc();
|
|
report_hugepages();
|
|
|
|
hugetlb_sysfs_init();
|
|
hugetlb_register_all_nodes();
|
|
hugetlb_cgroup_file_init();
|
|
|
|
#ifdef CONFIG_SMP
|
|
num_fault_mutexes = roundup_pow_of_two(8 * num_possible_cpus());
|
|
#else
|
|
num_fault_mutexes = 1;
|
|
#endif
|
|
hugetlb_fault_mutex_table =
|
|
kmalloc_array(num_fault_mutexes, sizeof(struct mutex),
|
|
GFP_KERNEL);
|
|
BUG_ON(!hugetlb_fault_mutex_table);
|
|
|
|
for (i = 0; i < num_fault_mutexes; i++)
|
|
mutex_init(&hugetlb_fault_mutex_table[i]);
|
|
return 0;
|
|
}
|
|
subsys_initcall(hugetlb_init);
|
|
|
|
/* Should be called on processing a hugepagesz=... option */
|
|
void __init hugetlb_bad_size(void)
|
|
{
|
|
parsed_valid_hugepagesz = false;
|
|
}
|
|
|
|
void __init hugetlb_add_hstate(unsigned int order)
|
|
{
|
|
struct hstate *h;
|
|
unsigned long i;
|
|
|
|
if (size_to_hstate(PAGE_SIZE << order)) {
|
|
pr_warn("hugepagesz= specified twice, ignoring\n");
|
|
return;
|
|
}
|
|
BUG_ON(hugetlb_max_hstate >= HUGE_MAX_HSTATE);
|
|
BUG_ON(order == 0);
|
|
h = &hstates[hugetlb_max_hstate++];
|
|
h->order = order;
|
|
h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1);
|
|
h->nr_huge_pages = 0;
|
|
h->free_huge_pages = 0;
|
|
for (i = 0; i < MAX_NUMNODES; ++i)
|
|
INIT_LIST_HEAD(&h->hugepage_freelists[i]);
|
|
INIT_LIST_HEAD(&h->hugepage_activelist);
|
|
h->next_nid_to_alloc = first_memory_node;
|
|
h->next_nid_to_free = first_memory_node;
|
|
snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB",
|
|
huge_page_size(h)/1024);
|
|
|
|
parsed_hstate = h;
|
|
}
|
|
|
|
static int __init hugetlb_nrpages_setup(char *s)
|
|
{
|
|
unsigned long *mhp;
|
|
static unsigned long *last_mhp;
|
|
|
|
if (!parsed_valid_hugepagesz) {
|
|
pr_warn("hugepages = %s preceded by "
|
|
"an unsupported hugepagesz, ignoring\n", s);
|
|
parsed_valid_hugepagesz = true;
|
|
return 1;
|
|
}
|
|
/*
|
|
* !hugetlb_max_hstate means we haven't parsed a hugepagesz= parameter yet,
|
|
* so this hugepages= parameter goes to the "default hstate".
|
|
*/
|
|
else if (!hugetlb_max_hstate)
|
|
mhp = &default_hstate_max_huge_pages;
|
|
else
|
|
mhp = &parsed_hstate->max_huge_pages;
|
|
|
|
if (mhp == last_mhp) {
|
|
pr_warn("hugepages= specified twice without interleaving hugepagesz=, ignoring\n");
|
|
return 1;
|
|
}
|
|
|
|
if (sscanf(s, "%lu", mhp) <= 0)
|
|
*mhp = 0;
|
|
|
|
/*
|
|
* Global state is always initialized later in hugetlb_init.
|
|
* But we need to allocate >= MAX_ORDER hstates here early to still
|
|
* use the bootmem allocator.
|
|
*/
|
|
if (hugetlb_max_hstate && parsed_hstate->order >= MAX_ORDER)
|
|
hugetlb_hstate_alloc_pages(parsed_hstate);
|
|
|
|
last_mhp = mhp;
|
|
|
|
return 1;
|
|
}
|
|
__setup("hugepages=", hugetlb_nrpages_setup);
|
|
|
|
static int __init hugetlb_default_setup(char *s)
|
|
{
|
|
default_hstate_size = memparse(s, &s);
|
|
return 1;
|
|
}
|
|
__setup("default_hugepagesz=", hugetlb_default_setup);
|
|
|
|
static unsigned int cpuset_mems_nr(unsigned int *array)
|
|
{
|
|
int node;
|
|
unsigned int nr = 0;
|
|
|
|
for_each_node_mask(node, cpuset_current_mems_allowed)
|
|
nr += array[node];
|
|
|
|
return nr;
|
|
}
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
static int proc_hugetlb_doulongvec_minmax(struct ctl_table *table, int write,
|
|
void *buffer, size_t *length,
|
|
loff_t *ppos, unsigned long *out)
|
|
{
|
|
struct ctl_table dup_table;
|
|
|
|
/*
|
|
* In order to avoid races with __do_proc_doulongvec_minmax(), we
|
|
* can duplicate the @table and alter the duplicate of it.
|
|
*/
|
|
dup_table = *table;
|
|
dup_table.data = out;
|
|
|
|
return proc_doulongvec_minmax(&dup_table, write, buffer, length, ppos);
|
|
}
|
|
|
|
static int hugetlb_sysctl_handler_common(bool obey_mempolicy,
|
|
struct ctl_table *table, int write,
|
|
void __user *buffer, size_t *length, loff_t *ppos)
|
|
{
|
|
struct hstate *h = &default_hstate;
|
|
unsigned long tmp = h->max_huge_pages;
|
|
int ret;
|
|
|
|
if (!hugepages_supported())
|
|
return -EOPNOTSUPP;
|
|
|
|
ret = proc_hugetlb_doulongvec_minmax(table, write, buffer, length, ppos,
|
|
&tmp);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (write)
|
|
ret = __nr_hugepages_store_common(obey_mempolicy, h,
|
|
NUMA_NO_NODE, tmp, *length);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int hugetlb_sysctl_handler(struct ctl_table *table, int write,
|
|
void __user *buffer, size_t *length, loff_t *ppos)
|
|
{
|
|
|
|
return hugetlb_sysctl_handler_common(false, table, write,
|
|
buffer, length, ppos);
|
|
}
|
|
|
|
#ifdef CONFIG_NUMA
|
|
int hugetlb_mempolicy_sysctl_handler(struct ctl_table *table, int write,
|
|
void __user *buffer, size_t *length, loff_t *ppos)
|
|
{
|
|
return hugetlb_sysctl_handler_common(true, table, write,
|
|
buffer, length, ppos);
|
|
}
|
|
#endif /* CONFIG_NUMA */
|
|
|
|
int hugetlb_overcommit_handler(struct ctl_table *table, int write,
|
|
void __user *buffer,
|
|
size_t *length, loff_t *ppos)
|
|
{
|
|
struct hstate *h = &default_hstate;
|
|
unsigned long tmp;
|
|
int ret;
|
|
|
|
if (!hugepages_supported())
|
|
return -EOPNOTSUPP;
|
|
|
|
tmp = h->nr_overcommit_huge_pages;
|
|
|
|
if (write && hstate_is_gigantic(h))
|
|
return -EINVAL;
|
|
|
|
ret = proc_hugetlb_doulongvec_minmax(table, write, buffer, length, ppos,
|
|
&tmp);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (write) {
|
|
spin_lock(&hugetlb_lock);
|
|
h->nr_overcommit_huge_pages = tmp;
|
|
spin_unlock(&hugetlb_lock);
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
#endif /* CONFIG_SYSCTL */
|
|
|
|
void hugetlb_report_meminfo(struct seq_file *m)
|
|
{
|
|
struct hstate *h;
|
|
unsigned long total = 0;
|
|
|
|
if (!hugepages_supported())
|
|
return;
|
|
|
|
for_each_hstate(h) {
|
|
unsigned long count = h->nr_huge_pages;
|
|
|
|
total += (PAGE_SIZE << huge_page_order(h)) * count;
|
|
|
|
if (h == &default_hstate)
|
|
seq_printf(m,
|
|
"HugePages_Total: %5lu\n"
|
|
"HugePages_Free: %5lu\n"
|
|
"HugePages_Rsvd: %5lu\n"
|
|
"HugePages_Surp: %5lu\n"
|
|
"Hugepagesize: %8lu kB\n",
|
|
count,
|
|
h->free_huge_pages,
|
|
h->resv_huge_pages,
|
|
h->surplus_huge_pages,
|
|
(PAGE_SIZE << huge_page_order(h)) / 1024);
|
|
}
|
|
|
|
seq_printf(m, "Hugetlb: %8lu kB\n", total / 1024);
|
|
}
|
|
|
|
int hugetlb_report_node_meminfo(int nid, char *buf)
|
|
{
|
|
struct hstate *h = &default_hstate;
|
|
if (!hugepages_supported())
|
|
return 0;
|
|
return sprintf(buf,
|
|
"Node %d HugePages_Total: %5u\n"
|
|
"Node %d HugePages_Free: %5u\n"
|
|
"Node %d HugePages_Surp: %5u\n",
|
|
nid, h->nr_huge_pages_node[nid],
|
|
nid, h->free_huge_pages_node[nid],
|
|
nid, h->surplus_huge_pages_node[nid]);
|
|
}
|
|
|
|
void hugetlb_show_meminfo(void)
|
|
{
|
|
struct hstate *h;
|
|
int nid;
|
|
|
|
if (!hugepages_supported())
|
|
return;
|
|
|
|
for_each_node_state(nid, N_MEMORY)
|
|
for_each_hstate(h)
|
|
pr_info("Node %d hugepages_total=%u hugepages_free=%u hugepages_surp=%u hugepages_size=%lukB\n",
|
|
nid,
|
|
h->nr_huge_pages_node[nid],
|
|
h->free_huge_pages_node[nid],
|
|
h->surplus_huge_pages_node[nid],
|
|
1UL << (huge_page_order(h) + PAGE_SHIFT - 10));
|
|
}
|
|
|
|
void hugetlb_report_usage(struct seq_file *m, struct mm_struct *mm)
|
|
{
|
|
seq_printf(m, "HugetlbPages:\t%8lu kB\n",
|
|
atomic_long_read(&mm->hugetlb_usage) << (PAGE_SHIFT - 10));
|
|
}
|
|
|
|
/* Return the number pages of memory we physically have, in PAGE_SIZE units. */
|
|
unsigned long hugetlb_total_pages(void)
|
|
{
|
|
struct hstate *h;
|
|
unsigned long nr_total_pages = 0;
|
|
|
|
for_each_hstate(h)
|
|
nr_total_pages += h->nr_huge_pages * pages_per_huge_page(h);
|
|
return nr_total_pages;
|
|
}
|
|
|
|
static int hugetlb_acct_memory(struct hstate *h, long delta)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
/*
|
|
* When cpuset is configured, it breaks the strict hugetlb page
|
|
* reservation as the accounting is done on a global variable. Such
|
|
* reservation is completely rubbish in the presence of cpuset because
|
|
* the reservation is not checked against page availability for the
|
|
* current cpuset. Application can still potentially OOM'ed by kernel
|
|
* with lack of free htlb page in cpuset that the task is in.
|
|
* Attempt to enforce strict accounting with cpuset is almost
|
|
* impossible (or too ugly) because cpuset is too fluid that
|
|
* task or memory node can be dynamically moved between cpusets.
|
|
*
|
|
* The change of semantics for shared hugetlb mapping with cpuset is
|
|
* undesirable. However, in order to preserve some of the semantics,
|
|
* we fall back to check against current free page availability as
|
|
* a best attempt and hopefully to minimize the impact of changing
|
|
* semantics that cpuset has.
|
|
*/
|
|
if (delta > 0) {
|
|
if (gather_surplus_pages(h, delta) < 0)
|
|
goto out;
|
|
|
|
if (delta > cpuset_mems_nr(h->free_huge_pages_node)) {
|
|
return_unused_surplus_pages(h, delta);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
if (delta < 0)
|
|
return_unused_surplus_pages(h, (unsigned long) -delta);
|
|
|
|
out:
|
|
spin_unlock(&hugetlb_lock);
|
|
return ret;
|
|
}
|
|
|
|
static void hugetlb_vm_op_open(struct vm_area_struct *vma)
|
|
{
|
|
struct resv_map *resv = vma_resv_map(vma);
|
|
|
|
/*
|
|
* This new VMA should share its siblings reservation map if present.
|
|
* The VMA will only ever have a valid reservation map pointer where
|
|
* it is being copied for another still existing VMA. As that VMA
|
|
* has a reference to the reservation map it cannot disappear until
|
|
* after this open call completes. It is therefore safe to take a
|
|
* new reference here without additional locking.
|
|
*/
|
|
if (resv && is_vma_resv_set(vma, HPAGE_RESV_OWNER))
|
|
kref_get(&resv->refs);
|
|
}
|
|
|
|
static void hugetlb_vm_op_close(struct vm_area_struct *vma)
|
|
{
|
|
struct hstate *h = hstate_vma(vma);
|
|
struct resv_map *resv = vma_resv_map(vma);
|
|
struct hugepage_subpool *spool = subpool_vma(vma);
|
|
unsigned long reserve, start, end;
|
|
long gbl_reserve;
|
|
|
|
if (!resv || !is_vma_resv_set(vma, HPAGE_RESV_OWNER))
|
|
return;
|
|
|
|
start = vma_hugecache_offset(h, vma, vma->vm_start);
|
|
end = vma_hugecache_offset(h, vma, vma->vm_end);
|
|
|
|
reserve = (end - start) - region_count(resv, start, end);
|
|
|
|
kref_put(&resv->refs, resv_map_release);
|
|
|
|
if (reserve) {
|
|
/*
|
|
* Decrement reserve counts. The global reserve count may be
|
|
* adjusted if the subpool has a minimum size.
|
|
*/
|
|
gbl_reserve = hugepage_subpool_put_pages(spool, reserve);
|
|
hugetlb_acct_memory(h, -gbl_reserve);
|
|
}
|
|
}
|
|
|
|
static int hugetlb_vm_op_split(struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
if (addr & ~(huge_page_mask(hstate_vma(vma))))
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
static unsigned long hugetlb_vm_op_pagesize(struct vm_area_struct *vma)
|
|
{
|
|
struct hstate *hstate = hstate_vma(vma);
|
|
|
|
return 1UL << huge_page_shift(hstate);
|
|
}
|
|
|
|
/*
|
|
* We cannot handle pagefaults against hugetlb pages at all. They cause
|
|
* handle_mm_fault() to try to instantiate regular-sized pages in the
|
|
* hugegpage VMA. do_page_fault() is supposed to trap this, so BUG is we get
|
|
* this far.
|
|
*/
|
|
static vm_fault_t hugetlb_vm_op_fault(struct vm_fault *vmf)
|
|
{
|
|
BUG();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* When a new function is introduced to vm_operations_struct and added
|
|
* to hugetlb_vm_ops, please consider adding the function to shm_vm_ops.
|
|
* This is because under System V memory model, mappings created via
|
|
* shmget/shmat with "huge page" specified are backed by hugetlbfs files,
|
|
* their original vm_ops are overwritten with shm_vm_ops.
|
|
*/
|
|
const struct vm_operations_struct hugetlb_vm_ops = {
|
|
.fault = hugetlb_vm_op_fault,
|
|
.open = hugetlb_vm_op_open,
|
|
.close = hugetlb_vm_op_close,
|
|
.split = hugetlb_vm_op_split,
|
|
.pagesize = hugetlb_vm_op_pagesize,
|
|
};
|
|
|
|
static pte_t make_huge_pte(struct vm_area_struct *vma, struct page *page,
|
|
int writable)
|
|
{
|
|
pte_t entry;
|
|
|
|
if (writable) {
|
|
entry = huge_pte_mkwrite(huge_pte_mkdirty(mk_huge_pte(page,
|
|
vma->vm_page_prot)));
|
|
} else {
|
|
entry = huge_pte_wrprotect(mk_huge_pte(page,
|
|
vma->vm_page_prot));
|
|
}
|
|
entry = pte_mkyoung(entry);
|
|
entry = pte_mkhuge(entry);
|
|
entry = arch_make_huge_pte(entry, vma, page, writable);
|
|
|
|
return entry;
|
|
}
|
|
|
|
static void set_huge_ptep_writable(struct vm_area_struct *vma,
|
|
unsigned long address, pte_t *ptep)
|
|
{
|
|
pte_t entry;
|
|
|
|
entry = huge_pte_mkwrite(huge_pte_mkdirty(huge_ptep_get(ptep)));
|
|
if (huge_ptep_set_access_flags(vma, address, ptep, entry, 1))
|
|
update_mmu_cache(vma, address, ptep);
|
|
}
|
|
|
|
bool is_hugetlb_entry_migration(pte_t pte)
|
|
{
|
|
swp_entry_t swp;
|
|
|
|
if (huge_pte_none(pte) || pte_present(pte))
|
|
return false;
|
|
swp = pte_to_swp_entry(pte);
|
|
if (non_swap_entry(swp) && is_migration_entry(swp))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static int is_hugetlb_entry_hwpoisoned(pte_t pte)
|
|
{
|
|
swp_entry_t swp;
|
|
|
|
if (huge_pte_none(pte) || pte_present(pte))
|
|
return 0;
|
|
swp = pte_to_swp_entry(pte);
|
|
if (non_swap_entry(swp) && is_hwpoison_entry(swp))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
|
|
struct vm_area_struct *vma)
|
|
{
|
|
pte_t *src_pte, *dst_pte, entry, dst_entry;
|
|
struct page *ptepage;
|
|
unsigned long addr;
|
|
int cow;
|
|
struct hstate *h = hstate_vma(vma);
|
|
unsigned long sz = huge_page_size(h);
|
|
struct mmu_notifier_range range;
|
|
int ret = 0;
|
|
|
|
cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
|
|
|
|
if (cow) {
|
|
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, src,
|
|
vma->vm_start,
|
|
vma->vm_end);
|
|
mmu_notifier_invalidate_range_start(&range);
|
|
}
|
|
|
|
for (addr = vma->vm_start; addr < vma->vm_end; addr += sz) {
|
|
spinlock_t *src_ptl, *dst_ptl;
|
|
src_pte = huge_pte_offset(src, addr, sz);
|
|
if (!src_pte)
|
|
continue;
|
|
dst_pte = huge_pte_alloc(dst, addr, sz);
|
|
if (!dst_pte) {
|
|
ret = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If the pagetables are shared don't copy or take references.
|
|
* dst_pte == src_pte is the common case of src/dest sharing.
|
|
*
|
|
* However, src could have 'unshared' and dst shares with
|
|
* another vma. If dst_pte !none, this implies sharing.
|
|
* Check here before taking page table lock, and once again
|
|
* after taking the lock below.
|
|
*/
|
|
dst_entry = huge_ptep_get(dst_pte);
|
|
if ((dst_pte == src_pte) || !huge_pte_none(dst_entry))
|
|
continue;
|
|
|
|
dst_ptl = huge_pte_lock(h, dst, dst_pte);
|
|
src_ptl = huge_pte_lockptr(h, src, src_pte);
|
|
spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
|
|
entry = huge_ptep_get(src_pte);
|
|
dst_entry = huge_ptep_get(dst_pte);
|
|
if (huge_pte_none(entry) || !huge_pte_none(dst_entry)) {
|
|
/*
|
|
* Skip if src entry none. Also, skip in the
|
|
* unlikely case dst entry !none as this implies
|
|
* sharing with another vma.
|
|
*/
|
|
;
|
|
} else if (unlikely(is_hugetlb_entry_migration(entry) ||
|
|
is_hugetlb_entry_hwpoisoned(entry))) {
|
|
swp_entry_t swp_entry = pte_to_swp_entry(entry);
|
|
|
|
if (is_write_migration_entry(swp_entry) && cow) {
|
|
/*
|
|
* COW mappings require pages in both
|
|
* parent and child to be set to read.
|
|
*/
|
|
make_migration_entry_read(&swp_entry);
|
|
entry = swp_entry_to_pte(swp_entry);
|
|
set_huge_swap_pte_at(src, addr, src_pte,
|
|
entry, sz);
|
|
}
|
|
set_huge_swap_pte_at(dst, addr, dst_pte, entry, sz);
|
|
} else {
|
|
if (cow) {
|
|
/*
|
|
* No need to notify as we are downgrading page
|
|
* table protection not changing it to point
|
|
* to a new page.
|
|
*
|
|
* See Documentation/vm/mmu_notifier.rst
|
|
*/
|
|
huge_ptep_set_wrprotect(src, addr, src_pte);
|
|
}
|
|
entry = huge_ptep_get(src_pte);
|
|
ptepage = pte_page(entry);
|
|
get_page(ptepage);
|
|
page_dup_rmap(ptepage, true);
|
|
set_huge_pte_at(dst, addr, dst_pte, entry);
|
|
hugetlb_count_add(pages_per_huge_page(h), dst);
|
|
}
|
|
spin_unlock(src_ptl);
|
|
spin_unlock(dst_ptl);
|
|
}
|
|
|
|
if (cow)
|
|
mmu_notifier_invalidate_range_end(&range);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
|
|
unsigned long start, unsigned long end,
|
|
struct page *ref_page)
|
|
{
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
unsigned long address;
|
|
pte_t *ptep;
|
|
pte_t pte;
|
|
spinlock_t *ptl;
|
|
struct page *page;
|
|
struct hstate *h = hstate_vma(vma);
|
|
unsigned long sz = huge_page_size(h);
|
|
struct mmu_notifier_range range;
|
|
bool force_flush = false;
|
|
|
|
WARN_ON(!is_vm_hugetlb_page(vma));
|
|
BUG_ON(start & ~huge_page_mask(h));
|
|
BUG_ON(end & ~huge_page_mask(h));
|
|
|
|
/*
|
|
* This is a hugetlb vma, all the pte entries should point
|
|
* to huge page.
|
|
*/
|
|
tlb_change_page_size(tlb, sz);
|
|
tlb_start_vma(tlb, vma);
|
|
|
|
/*
|
|
* If sharing possible, alert mmu notifiers of worst case.
|
|
*/
|
|
mmu_notifier_range_init(&range, MMU_NOTIFY_UNMAP, 0, vma, mm, start,
|
|
end);
|
|
adjust_range_if_pmd_sharing_possible(vma, &range.start, &range.end);
|
|
mmu_notifier_invalidate_range_start(&range);
|
|
address = start;
|
|
for (; address < end; address += sz) {
|
|
ptep = huge_pte_offset(mm, address, sz);
|
|
if (!ptep)
|
|
continue;
|
|
|
|
ptl = huge_pte_lock(h, mm, ptep);
|
|
if (huge_pmd_unshare(mm, &address, ptep)) {
|
|
spin_unlock(ptl);
|
|
tlb_flush_pmd_range(tlb, address & PUD_MASK, PUD_SIZE);
|
|
force_flush = true;
|
|
continue;
|
|
}
|
|
|
|
pte = huge_ptep_get(ptep);
|
|
if (huge_pte_none(pte)) {
|
|
spin_unlock(ptl);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Migrating hugepage or HWPoisoned hugepage is already
|
|
* unmapped and its refcount is dropped, so just clear pte here.
|
|
*/
|
|
if (unlikely(!pte_present(pte))) {
|
|
huge_pte_clear(mm, address, ptep, sz);
|
|
spin_unlock(ptl);
|
|
continue;
|
|
}
|
|
|
|
page = pte_page(pte);
|
|
/*
|
|
* If a reference page is supplied, it is because a specific
|
|
* page is being unmapped, not a range. Ensure the page we
|
|
* are about to unmap is the actual page of interest.
|
|
*/
|
|
if (ref_page) {
|
|
if (page != ref_page) {
|
|
spin_unlock(ptl);
|
|
continue;
|
|
}
|
|
/*
|
|
* Mark the VMA as having unmapped its page so that
|
|
* future faults in this VMA will fail rather than
|
|
* looking like data was lost
|
|
*/
|
|
set_vma_resv_flags(vma, HPAGE_RESV_UNMAPPED);
|
|
}
|
|
|
|
pte = huge_ptep_get_and_clear(mm, address, ptep);
|
|
tlb_remove_huge_tlb_entry(h, tlb, ptep, address);
|
|
if (huge_pte_dirty(pte))
|
|
set_page_dirty(page);
|
|
|
|
hugetlb_count_sub(pages_per_huge_page(h), mm);
|
|
page_remove_rmap(page, true);
|
|
|
|
spin_unlock(ptl);
|
|
tlb_remove_page_size(tlb, page, huge_page_size(h));
|
|
/*
|
|
* Bail out after unmapping reference page if supplied
|
|
*/
|
|
if (ref_page)
|
|
break;
|
|
}
|
|
mmu_notifier_invalidate_range_end(&range);
|
|
tlb_end_vma(tlb, vma);
|
|
|
|
/*
|
|
* If we unshared PMDs, the TLB flush was not recorded in mmu_gather. We
|
|
* could defer the flush until now, since by holding i_mmap_rwsem we
|
|
* guaranteed that the last refernece would not be dropped. But we must
|
|
* do the flushing before we return, as otherwise i_mmap_rwsem will be
|
|
* dropped and the last reference to the shared PMDs page might be
|
|
* dropped as well.
|
|
*
|
|
* In theory we could defer the freeing of the PMD pages as well, but
|
|
* huge_pmd_unshare() relies on the exact page_count for the PMD page to
|
|
* detect sharing, so we cannot defer the release of the page either.
|
|
* Instead, do flush now.
|
|
*/
|
|
if (force_flush)
|
|
tlb_flush_mmu_tlbonly(tlb);
|
|
}
|
|
|
|
void __unmap_hugepage_range_final(struct mmu_gather *tlb,
|
|
struct vm_area_struct *vma, unsigned long start,
|
|
unsigned long end, struct page *ref_page)
|
|
{
|
|
__unmap_hugepage_range(tlb, vma, start, end, ref_page);
|
|
|
|
/*
|
|
* Clear this flag so that x86's huge_pmd_share page_table_shareable
|
|
* test will fail on a vma being torn down, and not grab a page table
|
|
* on its way out. We're lucky that the flag has such an appropriate
|
|
* name, and can in fact be safely cleared here. We could clear it
|
|
* before the __unmap_hugepage_range above, but all that's necessary
|
|
* is to clear it before releasing the i_mmap_rwsem. This works
|
|
* because in the context this is called, the VMA is about to be
|
|
* destroyed and the i_mmap_rwsem is held.
|
|
*/
|
|
vma->vm_flags &= ~VM_MAYSHARE;
|
|
}
|
|
|
|
void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
|
|
unsigned long end, struct page *ref_page)
|
|
{
|
|
struct mm_struct *mm;
|
|
struct mmu_gather tlb;
|
|
unsigned long tlb_start = start;
|
|
unsigned long tlb_end = end;
|
|
|
|
/*
|
|
* If shared PMDs were possibly used within this vma range, adjust
|
|
* start/end for worst case tlb flushing.
|
|
* Note that we can not be sure if PMDs are shared until we try to
|
|
* unmap pages. However, we want to make sure TLB flushing covers
|
|
* the largest possible range.
|
|
*/
|
|
adjust_range_if_pmd_sharing_possible(vma, &tlb_start, &tlb_end);
|
|
|
|
mm = vma->vm_mm;
|
|
|
|
tlb_gather_mmu(&tlb, mm, tlb_start, tlb_end);
|
|
__unmap_hugepage_range(&tlb, vma, start, end, ref_page);
|
|
tlb_finish_mmu(&tlb, tlb_start, tlb_end);
|
|
}
|
|
|
|
/*
|
|
* This is called when the original mapper is failing to COW a MAP_PRIVATE
|
|
* mappping it owns the reserve page for. The intention is to unmap the page
|
|
* from other VMAs and let the children be SIGKILLed if they are faulting the
|
|
* same region.
|
|
*/
|
|
static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
struct page *page, unsigned long address)
|
|
{
|
|
struct hstate *h = hstate_vma(vma);
|
|
struct vm_area_struct *iter_vma;
|
|
struct address_space *mapping;
|
|
pgoff_t pgoff;
|
|
|
|
/*
|
|
* vm_pgoff is in PAGE_SIZE units, hence the different calculation
|
|
* from page cache lookup which is in HPAGE_SIZE units.
|
|
*/
|
|
address = address & huge_page_mask(h);
|
|
pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) +
|
|
vma->vm_pgoff;
|
|
mapping = vma->vm_file->f_mapping;
|
|
|
|
/*
|
|
* Take the mapping lock for the duration of the table walk. As
|
|
* this mapping should be shared between all the VMAs,
|
|
* __unmap_hugepage_range() is called as the lock is already held
|
|
*/
|
|
i_mmap_lock_write(mapping);
|
|
vma_interval_tree_foreach(iter_vma, &mapping->i_mmap, pgoff, pgoff) {
|
|
/* Do not unmap the current VMA */
|
|
if (iter_vma == vma)
|
|
continue;
|
|
|
|
/*
|
|
* Shared VMAs have their own reserves and do not affect
|
|
* MAP_PRIVATE accounting but it is possible that a shared
|
|
* VMA is using the same page so check and skip such VMAs.
|
|
*/
|
|
if (iter_vma->vm_flags & VM_MAYSHARE)
|
|
continue;
|
|
|
|
/*
|
|
* Unmap the page from other VMAs without their own reserves.
|
|
* They get marked to be SIGKILLed if they fault in these
|
|
* areas. This is because a future no-page fault on this VMA
|
|
* could insert a zeroed page instead of the data existing
|
|
* from the time of fork. This would look like data corruption
|
|
*/
|
|
if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
|
|
unmap_hugepage_range(iter_vma, address,
|
|
address + huge_page_size(h), page);
|
|
}
|
|
i_mmap_unlock_write(mapping);
|
|
}
|
|
|
|
/*
|
|
* Hugetlb_cow() should be called with page lock of the original hugepage held.
|
|
* Called with hugetlb_instantiation_mutex held and pte_page locked so we
|
|
* cannot race with other handlers or page migration.
|
|
* Keep the pte_same checks anyway to make transition from the mutex easier.
|
|
*/
|
|
static vm_fault_t hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
unsigned long address, pte_t *ptep,
|
|
struct page *pagecache_page, spinlock_t *ptl)
|
|
{
|
|
pte_t pte;
|
|
struct hstate *h = hstate_vma(vma);
|
|
struct page *old_page, *new_page;
|
|
int outside_reserve = 0;
|
|
vm_fault_t ret = 0;
|
|
unsigned long haddr = address & huge_page_mask(h);
|
|
struct mmu_notifier_range range;
|
|
|
|
pte = huge_ptep_get(ptep);
|
|
old_page = pte_page(pte);
|
|
|
|
retry_avoidcopy:
|
|
/* If no-one else is actually using this page, avoid the copy
|
|
* and just make the page writable */
|
|
if (page_mapcount(old_page) == 1 && PageAnon(old_page)) {
|
|
page_move_anon_rmap(old_page, vma);
|
|
set_huge_ptep_writable(vma, haddr, ptep);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the process that created a MAP_PRIVATE mapping is about to
|
|
* perform a COW due to a shared page count, attempt to satisfy
|
|
* the allocation without using the existing reserves. The pagecache
|
|
* page is used to determine if the reserve at this address was
|
|
* consumed or not. If reserves were used, a partial faulted mapping
|
|
* at the time of fork() could consume its reserves on COW instead
|
|
* of the full address range.
|
|
*/
|
|
if (is_vma_resv_set(vma, HPAGE_RESV_OWNER) &&
|
|
old_page != pagecache_page)
|
|
outside_reserve = 1;
|
|
|
|
get_page(old_page);
|
|
|
|
/*
|
|
* Drop page table lock as buddy allocator may be called. It will
|
|
* be acquired again before returning to the caller, as expected.
|
|
*/
|
|
spin_unlock(ptl);
|
|
new_page = alloc_huge_page(vma, haddr, outside_reserve);
|
|
|
|
if (IS_ERR(new_page)) {
|
|
/*
|
|
* If a process owning a MAP_PRIVATE mapping fails to COW,
|
|
* it is due to references held by a child and an insufficient
|
|
* huge page pool. To guarantee the original mappers
|
|
* reliability, unmap the page from child processes. The child
|
|
* may get SIGKILLed if it later faults.
|
|
*/
|
|
if (outside_reserve) {
|
|
put_page(old_page);
|
|
BUG_ON(huge_pte_none(pte));
|
|
unmap_ref_private(mm, vma, old_page, haddr);
|
|
BUG_ON(huge_pte_none(pte));
|
|
spin_lock(ptl);
|
|
ptep = huge_pte_offset(mm, haddr, huge_page_size(h));
|
|
if (likely(ptep &&
|
|
pte_same(huge_ptep_get(ptep), pte)))
|
|
goto retry_avoidcopy;
|
|
/*
|
|
* race occurs while re-acquiring page table
|
|
* lock, and our job is done.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
ret = vmf_error(PTR_ERR(new_page));
|
|
goto out_release_old;
|
|
}
|
|
|
|
/*
|
|
* When the original hugepage is shared one, it does not have
|
|
* anon_vma prepared.
|
|
*/
|
|
if (unlikely(anon_vma_prepare(vma))) {
|
|
ret = VM_FAULT_OOM;
|
|
goto out_release_all;
|
|
}
|
|
|
|
copy_user_huge_page(new_page, old_page, address, vma,
|
|
pages_per_huge_page(h));
|
|
__SetPageUptodate(new_page);
|
|
|
|
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, mm, haddr,
|
|
haddr + huge_page_size(h));
|
|
mmu_notifier_invalidate_range_start(&range);
|
|
|
|
/*
|
|
* Retake the page table lock to check for racing updates
|
|
* before the page tables are altered
|
|
*/
|
|
spin_lock(ptl);
|
|
ptep = huge_pte_offset(mm, haddr, huge_page_size(h));
|
|
if (likely(ptep && pte_same(huge_ptep_get(ptep), pte))) {
|
|
ClearPagePrivate(new_page);
|
|
|
|
/* Break COW */
|
|
huge_ptep_clear_flush(vma, haddr, ptep);
|
|
mmu_notifier_invalidate_range(mm, range.start, range.end);
|
|
set_huge_pte_at(mm, haddr, ptep,
|
|
make_huge_pte(vma, new_page, 1));
|
|
page_remove_rmap(old_page, true);
|
|
hugepage_add_new_anon_rmap(new_page, vma, haddr);
|
|
set_page_huge_active(new_page);
|
|
/* Make the old page be freed below */
|
|
new_page = old_page;
|
|
}
|
|
spin_unlock(ptl);
|
|
mmu_notifier_invalidate_range_end(&range);
|
|
out_release_all:
|
|
restore_reserve_on_error(h, vma, haddr, new_page);
|
|
put_page(new_page);
|
|
out_release_old:
|
|
put_page(old_page);
|
|
|
|
spin_lock(ptl); /* Caller expects lock to be held */
|
|
return ret;
|
|
}
|
|
|
|
/* Return the pagecache page at a given address within a VMA */
|
|
static struct page *hugetlbfs_pagecache_page(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long address)
|
|
{
|
|
struct address_space *mapping;
|
|
pgoff_t idx;
|
|
|
|
mapping = vma->vm_file->f_mapping;
|
|
idx = vma_hugecache_offset(h, vma, address);
|
|
|
|
return find_lock_page(mapping, idx);
|
|
}
|
|
|
|
/*
|
|
* Return whether there is a pagecache page to back given address within VMA.
|
|
* Caller follow_hugetlb_page() holds page_table_lock so we cannot lock_page.
|
|
*/
|
|
static bool hugetlbfs_pagecache_present(struct hstate *h,
|
|
struct vm_area_struct *vma, unsigned long address)
|
|
{
|
|
struct address_space *mapping;
|
|
pgoff_t idx;
|
|
struct page *page;
|
|
|
|
mapping = vma->vm_file->f_mapping;
|
|
idx = vma_hugecache_offset(h, vma, address);
|
|
|
|
page = find_get_page(mapping, idx);
|
|
if (page)
|
|
put_page(page);
|
|
return page != NULL;
|
|
}
|
|
|
|
int huge_add_to_page_cache(struct page *page, struct address_space *mapping,
|
|
pgoff_t idx)
|
|
{
|
|
struct inode *inode = mapping->host;
|
|
struct hstate *h = hstate_inode(inode);
|
|
int err = add_to_page_cache(page, mapping, idx, GFP_KERNEL);
|
|
|
|
if (err)
|
|
return err;
|
|
ClearPagePrivate(page);
|
|
|
|
/*
|
|
* set page dirty so that it will not be removed from cache/file
|
|
* by non-hugetlbfs specific code paths.
|
|
*/
|
|
set_page_dirty(page);
|
|
|
|
spin_lock(&inode->i_lock);
|
|
inode->i_blocks += blocks_per_huge_page(h);
|
|
spin_unlock(&inode->i_lock);
|
|
return 0;
|
|
}
|
|
|
|
static vm_fault_t hugetlb_no_page(struct mm_struct *mm,
|
|
struct vm_area_struct *vma,
|
|
struct address_space *mapping, pgoff_t idx,
|
|
unsigned long address, pte_t *ptep, unsigned int flags)
|
|
{
|
|
struct hstate *h = hstate_vma(vma);
|
|
vm_fault_t ret = VM_FAULT_SIGBUS;
|
|
int anon_rmap = 0;
|
|
unsigned long size;
|
|
struct page *page;
|
|
pte_t new_pte;
|
|
spinlock_t *ptl;
|
|
unsigned long haddr = address & huge_page_mask(h);
|
|
bool new_page = false;
|
|
|
|
/*
|
|
* Currently, we are forced to kill the process in the event the
|
|
* original mapper has unmapped pages from the child due to a failed
|
|
* COW. Warn that such a situation has occurred as it may not be obvious
|
|
*/
|
|
if (is_vma_resv_set(vma, HPAGE_RESV_UNMAPPED)) {
|
|
pr_warn_ratelimited("PID %d killed due to inadequate hugepage pool\n",
|
|
current->pid);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Use page lock to guard against racing truncation
|
|
* before we get page_table_lock.
|
|
*/
|
|
retry:
|
|
page = find_lock_page(mapping, idx);
|
|
if (!page) {
|
|
size = i_size_read(mapping->host) >> huge_page_shift(h);
|
|
if (idx >= size)
|
|
goto out;
|
|
|
|
/*
|
|
* Check for page in userfault range
|
|
*/
|
|
if (userfaultfd_missing(vma)) {
|
|
u32 hash;
|
|
struct vm_fault vmf = {
|
|
.vma = vma,
|
|
.address = haddr,
|
|
.flags = flags,
|
|
.vma_flags = vma->vm_flags,
|
|
.vma_page_prot = vma->vm_page_prot,
|
|
/*
|
|
* Hard to debug if it ends up being
|
|
* used by a callee that assumes
|
|
* something about the other
|
|
* uninitialized fields... same as in
|
|
* memory.c
|
|
*/
|
|
};
|
|
|
|
/*
|
|
* hugetlb_fault_mutex must be dropped before
|
|
* handling userfault. Reacquire after handling
|
|
* fault to make calling code simpler.
|
|
*/
|
|
hash = hugetlb_fault_mutex_hash(h, mapping, idx);
|
|
mutex_unlock(&hugetlb_fault_mutex_table[hash]);
|
|
ret = handle_userfault(&vmf, VM_UFFD_MISSING);
|
|
mutex_lock(&hugetlb_fault_mutex_table[hash]);
|
|
goto out;
|
|
}
|
|
|
|
page = alloc_huge_page(vma, haddr, 0);
|
|
if (IS_ERR(page)) {
|
|
/*
|
|
* Returning error will result in faulting task being
|
|
* sent SIGBUS. The hugetlb fault mutex prevents two
|
|
* tasks from racing to fault in the same page which
|
|
* could result in false unable to allocate errors.
|
|
* Page migration does not take the fault mutex, but
|
|
* does a clear then write of pte's under page table
|
|
* lock. Page fault code could race with migration,
|
|
* notice the clear pte and try to allocate a page
|
|
* here. Before returning error, get ptl and make
|
|
* sure there really is no pte entry.
|
|
*/
|
|
ptl = huge_pte_lock(h, mm, ptep);
|
|
if (!huge_pte_none(huge_ptep_get(ptep))) {
|
|
ret = 0;
|
|
spin_unlock(ptl);
|
|
goto out;
|
|
}
|
|
spin_unlock(ptl);
|
|
ret = vmf_error(PTR_ERR(page));
|
|
goto out;
|
|
}
|
|
clear_huge_page(page, address, pages_per_huge_page(h));
|
|
__SetPageUptodate(page);
|
|
new_page = true;
|
|
|
|
if (vma->vm_flags & VM_MAYSHARE) {
|
|
int err = huge_add_to_page_cache(page, mapping, idx);
|
|
if (err) {
|
|
put_page(page);
|
|
if (err == -EEXIST)
|
|
goto retry;
|
|
goto out;
|
|
}
|
|
} else {
|
|
lock_page(page);
|
|
if (unlikely(anon_vma_prepare(vma))) {
|
|
ret = VM_FAULT_OOM;
|
|
goto backout_unlocked;
|
|
}
|
|
anon_rmap = 1;
|
|
}
|
|
} else {
|
|
/*
|
|
* If memory error occurs between mmap() and fault, some process
|
|
* don't have hwpoisoned swap entry for errored virtual address.
|
|
* So we need to block hugepage fault by PG_hwpoison bit check.
|
|
*/
|
|
if (unlikely(PageHWPoison(page))) {
|
|
ret = VM_FAULT_HWPOISON_LARGE |
|
|
VM_FAULT_SET_HINDEX(hstate_index(h));
|
|
goto backout_unlocked;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we are going to COW a private mapping later, we examine the
|
|
* pending reservations for this page now. This will ensure that
|
|
* any allocations necessary to record that reservation occur outside
|
|
* the spinlock.
|
|
*/
|
|
if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
|
|
if (vma_needs_reservation(h, vma, haddr) < 0) {
|
|
ret = VM_FAULT_OOM;
|
|
goto backout_unlocked;
|
|
}
|
|
/* Just decrements count, does not deallocate */
|
|
vma_end_reservation(h, vma, haddr);
|
|
}
|
|
|
|
ptl = huge_pte_lock(h, mm, ptep);
|
|
size = i_size_read(mapping->host) >> huge_page_shift(h);
|
|
if (idx >= size)
|
|
goto backout;
|
|
|
|
ret = 0;
|
|
if (!huge_pte_none(huge_ptep_get(ptep)))
|
|
goto backout;
|
|
|
|
if (anon_rmap) {
|
|
ClearPagePrivate(page);
|
|
hugepage_add_new_anon_rmap(page, vma, haddr);
|
|
} else
|
|
page_dup_rmap(page, true);
|
|
new_pte = make_huge_pte(vma, page, ((vma->vm_flags & VM_WRITE)
|
|
&& (vma->vm_flags & VM_SHARED)));
|
|
set_huge_pte_at(mm, haddr, ptep, new_pte);
|
|
|
|
hugetlb_count_add(pages_per_huge_page(h), mm);
|
|
if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
|
|
/* Optimization, do the COW without a second fault */
|
|
ret = hugetlb_cow(mm, vma, address, ptep, page, ptl);
|
|
}
|
|
|
|
spin_unlock(ptl);
|
|
|
|
/*
|
|
* Only make newly allocated pages active. Existing pages found
|
|
* in the pagecache could be !page_huge_active() if they have been
|
|
* isolated for migration.
|
|
*/
|
|
if (new_page)
|
|
set_page_huge_active(page);
|
|
|
|
unlock_page(page);
|
|
out:
|
|
return ret;
|
|
|
|
backout:
|
|
spin_unlock(ptl);
|
|
backout_unlocked:
|
|
unlock_page(page);
|
|
restore_reserve_on_error(h, vma, haddr, page);
|
|
put_page(page);
|
|
goto out;
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
u32 hugetlb_fault_mutex_hash(struct hstate *h, struct address_space *mapping,
|
|
pgoff_t idx)
|
|
{
|
|
unsigned long key[2];
|
|
u32 hash;
|
|
|
|
key[0] = (unsigned long) mapping;
|
|
key[1] = idx;
|
|
|
|
hash = jhash2((u32 *)&key, sizeof(key)/(sizeof(u32)), 0);
|
|
|
|
return hash & (num_fault_mutexes - 1);
|
|
}
|
|
#else
|
|
/*
|
|
* For uniprocesor systems we always use a single mutex, so just
|
|
* return 0 and avoid the hashing overhead.
|
|
*/
|
|
u32 hugetlb_fault_mutex_hash(struct hstate *h, struct address_space *mapping,
|
|
pgoff_t idx)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
vm_fault_t hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
unsigned long address, unsigned int flags)
|
|
{
|
|
pte_t *ptep, entry;
|
|
spinlock_t *ptl;
|
|
vm_fault_t ret;
|
|
u32 hash;
|
|
pgoff_t idx;
|
|
struct page *page = NULL;
|
|
struct page *pagecache_page = NULL;
|
|
struct hstate *h = hstate_vma(vma);
|
|
struct address_space *mapping;
|
|
int need_wait_lock = 0;
|
|
unsigned long haddr = address & huge_page_mask(h);
|
|
|
|
ptep = huge_pte_offset(mm, haddr, huge_page_size(h));
|
|
if (ptep) {
|
|
entry = huge_ptep_get(ptep);
|
|
if (unlikely(is_hugetlb_entry_migration(entry))) {
|
|
migration_entry_wait_huge(vma, mm, ptep);
|
|
return 0;
|
|
} else if (unlikely(is_hugetlb_entry_hwpoisoned(entry)))
|
|
return VM_FAULT_HWPOISON_LARGE |
|
|
VM_FAULT_SET_HINDEX(hstate_index(h));
|
|
} else {
|
|
ptep = huge_pte_alloc(mm, haddr, huge_page_size(h));
|
|
if (!ptep)
|
|
return VM_FAULT_OOM;
|
|
}
|
|
|
|
mapping = vma->vm_file->f_mapping;
|
|
idx = vma_hugecache_offset(h, vma, haddr);
|
|
|
|
/*
|
|
* Serialize hugepage allocation and instantiation, so that we don't
|
|
* get spurious allocation failures if two CPUs race to instantiate
|
|
* the same page in the page cache.
|
|
*/
|
|
hash = hugetlb_fault_mutex_hash(h, mapping, idx);
|
|
mutex_lock(&hugetlb_fault_mutex_table[hash]);
|
|
|
|
entry = huge_ptep_get(ptep);
|
|
if (huge_pte_none(entry)) {
|
|
ret = hugetlb_no_page(mm, vma, mapping, idx, address, ptep, flags);
|
|
goto out_mutex;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
/*
|
|
* entry could be a migration/hwpoison entry at this point, so this
|
|
* check prevents the kernel from going below assuming that we have
|
|
* a active hugepage in pagecache. This goto expects the 2nd page fault,
|
|
* and is_hugetlb_entry_(migration|hwpoisoned) check will properly
|
|
* handle it.
|
|
*/
|
|
if (!pte_present(entry))
|
|
goto out_mutex;
|
|
|
|
/*
|
|
* If we are going to COW the mapping later, we examine the pending
|
|
* reservations for this page now. This will ensure that any
|
|
* allocations necessary to record that reservation occur outside the
|
|
* spinlock. For private mappings, we also lookup the pagecache
|
|
* page now as it is used to determine if a reservation has been
|
|
* consumed.
|
|
*/
|
|
if ((flags & FAULT_FLAG_WRITE) && !huge_pte_write(entry)) {
|
|
if (vma_needs_reservation(h, vma, haddr) < 0) {
|
|
ret = VM_FAULT_OOM;
|
|
goto out_mutex;
|
|
}
|
|
/* Just decrements count, does not deallocate */
|
|
vma_end_reservation(h, vma, haddr);
|
|
|
|
if (!(vma->vm_flags & VM_MAYSHARE))
|
|
pagecache_page = hugetlbfs_pagecache_page(h,
|
|
vma, haddr);
|
|
}
|
|
|
|
ptl = huge_pte_lock(h, mm, ptep);
|
|
|
|
/* Check for a racing update before calling hugetlb_cow */
|
|
if (unlikely(!pte_same(entry, huge_ptep_get(ptep))))
|
|
goto out_ptl;
|
|
|
|
/*
|
|
* hugetlb_cow() requires page locks of pte_page(entry) and
|
|
* pagecache_page, so here we need take the former one
|
|
* when page != pagecache_page or !pagecache_page.
|
|
*/
|
|
page = pte_page(entry);
|
|
if (page != pagecache_page)
|
|
if (!trylock_page(page)) {
|
|
need_wait_lock = 1;
|
|
goto out_ptl;
|
|
}
|
|
|
|
get_page(page);
|
|
|
|
if (flags & FAULT_FLAG_WRITE) {
|
|
if (!huge_pte_write(entry)) {
|
|
ret = hugetlb_cow(mm, vma, address, ptep,
|
|
pagecache_page, ptl);
|
|
goto out_put_page;
|
|
}
|
|
entry = huge_pte_mkdirty(entry);
|
|
}
|
|
entry = pte_mkyoung(entry);
|
|
if (huge_ptep_set_access_flags(vma, haddr, ptep, entry,
|
|
flags & FAULT_FLAG_WRITE))
|
|
update_mmu_cache(vma, haddr, ptep);
|
|
out_put_page:
|
|
if (page != pagecache_page)
|
|
unlock_page(page);
|
|
put_page(page);
|
|
out_ptl:
|
|
spin_unlock(ptl);
|
|
|
|
if (pagecache_page) {
|
|
unlock_page(pagecache_page);
|
|
put_page(pagecache_page);
|
|
}
|
|
out_mutex:
|
|
mutex_unlock(&hugetlb_fault_mutex_table[hash]);
|
|
/*
|
|
* Generally it's safe to hold refcount during waiting page lock. But
|
|
* here we just wait to defer the next page fault to avoid busy loop and
|
|
* the page is not used after unlocked before returning from the current
|
|
* page fault. So we are safe from accessing freed page, even if we wait
|
|
* here without taking refcount.
|
|
*/
|
|
if (need_wait_lock)
|
|
wait_on_page_locked(page);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Used by userfaultfd UFFDIO_COPY. Based on mcopy_atomic_pte with
|
|
* modifications for huge pages.
|
|
*/
|
|
int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
|
|
pte_t *dst_pte,
|
|
struct vm_area_struct *dst_vma,
|
|
unsigned long dst_addr,
|
|
unsigned long src_addr,
|
|
struct page **pagep)
|
|
{
|
|
struct address_space *mapping;
|
|
pgoff_t idx;
|
|
unsigned long size;
|
|
int vm_shared = dst_vma->vm_flags & VM_SHARED;
|
|
struct hstate *h = hstate_vma(dst_vma);
|
|
pte_t _dst_pte;
|
|
spinlock_t *ptl;
|
|
int ret;
|
|
struct page *page;
|
|
|
|
if (!*pagep) {
|
|
/* If a page already exists, then it's UFFDIO_COPY for
|
|
* a non-missing case. Return -EEXIST.
|
|
*/
|
|
if (vm_shared &&
|
|
hugetlbfs_pagecache_present(h, dst_vma, dst_addr)) {
|
|
ret = -EEXIST;
|
|
goto out;
|
|
}
|
|
|
|
page = alloc_huge_page(dst_vma, dst_addr, 0);
|
|
if (IS_ERR(page)) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
ret = copy_huge_page_from_user(page,
|
|
(const void __user *) src_addr,
|
|
pages_per_huge_page(h), false);
|
|
|
|
/* fallback to copy_from_user outside mmap_sem */
|
|
if (unlikely(ret)) {
|
|
ret = -ENOENT;
|
|
*pagep = page;
|
|
/* don't free the page */
|
|
goto out;
|
|
}
|
|
} else {
|
|
page = *pagep;
|
|
*pagep = NULL;
|
|
}
|
|
|
|
/*
|
|
* The memory barrier inside __SetPageUptodate makes sure that
|
|
* preceding stores to the page contents become visible before
|
|
* the set_pte_at() write.
|
|
*/
|
|
__SetPageUptodate(page);
|
|
|
|
mapping = dst_vma->vm_file->f_mapping;
|
|
idx = vma_hugecache_offset(h, dst_vma, dst_addr);
|
|
|
|
/*
|
|
* If shared, add to page cache
|
|
*/
|
|
if (vm_shared) {
|
|
size = i_size_read(mapping->host) >> huge_page_shift(h);
|
|
ret = -EFAULT;
|
|
if (idx >= size)
|
|
goto out_release_nounlock;
|
|
|
|
/*
|
|
* Serialization between remove_inode_hugepages() and
|
|
* huge_add_to_page_cache() below happens through the
|
|
* hugetlb_fault_mutex_table that here must be hold by
|
|
* the caller.
|
|
*/
|
|
ret = huge_add_to_page_cache(page, mapping, idx);
|
|
if (ret)
|
|
goto out_release_nounlock;
|
|
}
|
|
|
|
ptl = huge_pte_lockptr(h, dst_mm, dst_pte);
|
|
spin_lock(ptl);
|
|
|
|
/*
|
|
* Recheck the i_size after holding PT lock to make sure not
|
|
* to leave any page mapped (as page_mapped()) beyond the end
|
|
* of the i_size (remove_inode_hugepages() is strict about
|
|
* enforcing that). If we bail out here, we'll also leave a
|
|
* page in the radix tree in the vm_shared case beyond the end
|
|
* of the i_size, but remove_inode_hugepages() will take care
|
|
* of it as soon as we drop the hugetlb_fault_mutex_table.
|
|
*/
|
|
size = i_size_read(mapping->host) >> huge_page_shift(h);
|
|
ret = -EFAULT;
|
|
if (idx >= size)
|
|
goto out_release_unlock;
|
|
|
|
ret = -EEXIST;
|
|
if (!huge_pte_none(huge_ptep_get(dst_pte)))
|
|
goto out_release_unlock;
|
|
|
|
if (vm_shared) {
|
|
page_dup_rmap(page, true);
|
|
} else {
|
|
ClearPagePrivate(page);
|
|
hugepage_add_new_anon_rmap(page, dst_vma, dst_addr);
|
|
}
|
|
|
|
_dst_pte = make_huge_pte(dst_vma, page, dst_vma->vm_flags & VM_WRITE);
|
|
if (dst_vma->vm_flags & VM_WRITE)
|
|
_dst_pte = huge_pte_mkdirty(_dst_pte);
|
|
_dst_pte = pte_mkyoung(_dst_pte);
|
|
|
|
set_huge_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte);
|
|
|
|
(void)huge_ptep_set_access_flags(dst_vma, dst_addr, dst_pte, _dst_pte,
|
|
dst_vma->vm_flags & VM_WRITE);
|
|
hugetlb_count_add(pages_per_huge_page(h), dst_mm);
|
|
|
|
/* No need to invalidate - it was non-present before */
|
|
update_mmu_cache(dst_vma, dst_addr, dst_pte);
|
|
|
|
spin_unlock(ptl);
|
|
set_page_huge_active(page);
|
|
if (vm_shared)
|
|
unlock_page(page);
|
|
ret = 0;
|
|
out:
|
|
return ret;
|
|
out_release_unlock:
|
|
spin_unlock(ptl);
|
|
if (vm_shared)
|
|
unlock_page(page);
|
|
out_release_nounlock:
|
|
put_page(page);
|
|
goto out;
|
|
}
|
|
|
|
long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
struct page **pages, struct vm_area_struct **vmas,
|
|
unsigned long *position, unsigned long *nr_pages,
|
|
long i, unsigned int flags, int *nonblocking)
|
|
{
|
|
unsigned long pfn_offset;
|
|
unsigned long vaddr = *position;
|
|
unsigned long remainder = *nr_pages;
|
|
struct hstate *h = hstate_vma(vma);
|
|
int err = -EFAULT;
|
|
|
|
while (vaddr < vma->vm_end && remainder) {
|
|
pte_t *pte;
|
|
spinlock_t *ptl = NULL;
|
|
int absent;
|
|
struct page *page;
|
|
|
|
/*
|
|
* If we have a pending SIGKILL, don't keep faulting pages and
|
|
* potentially allocating memory.
|
|
*/
|
|
if (fatal_signal_pending(current)) {
|
|
remainder = 0;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Some archs (sparc64, sh*) have multiple pte_ts to
|
|
* each hugepage. We have to make sure we get the
|
|
* first, for the page indexing below to work.
|
|
*
|
|
* Note that page table lock is not held when pte is null.
|
|
*/
|
|
pte = huge_pte_offset(mm, vaddr & huge_page_mask(h),
|
|
huge_page_size(h));
|
|
if (pte)
|
|
ptl = huge_pte_lock(h, mm, pte);
|
|
absent = !pte || huge_pte_none(huge_ptep_get(pte));
|
|
|
|
/*
|
|
* When coredumping, it suits get_dump_page if we just return
|
|
* an error where there's an empty slot with no huge pagecache
|
|
* to back it. This way, we avoid allocating a hugepage, and
|
|
* the sparse dumpfile avoids allocating disk blocks, but its
|
|
* huge holes still show up with zeroes where they need to be.
|
|
*/
|
|
if (absent && (flags & FOLL_DUMP) &&
|
|
!hugetlbfs_pagecache_present(h, vma, vaddr)) {
|
|
if (pte)
|
|
spin_unlock(ptl);
|
|
remainder = 0;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* We need call hugetlb_fault for both hugepages under migration
|
|
* (in which case hugetlb_fault waits for the migration,) and
|
|
* hwpoisoned hugepages (in which case we need to prevent the
|
|
* caller from accessing to them.) In order to do this, we use
|
|
* here is_swap_pte instead of is_hugetlb_entry_migration and
|
|
* is_hugetlb_entry_hwpoisoned. This is because it simply covers
|
|
* both cases, and because we can't follow correct pages
|
|
* directly from any kind of swap entries.
|
|
*/
|
|
if (absent || is_swap_pte(huge_ptep_get(pte)) ||
|
|
((flags & FOLL_WRITE) &&
|
|
!huge_pte_write(huge_ptep_get(pte)))) {
|
|
vm_fault_t ret;
|
|
unsigned int fault_flags = 0;
|
|
|
|
if (pte)
|
|
spin_unlock(ptl);
|
|
if (flags & FOLL_WRITE)
|
|
fault_flags |= FAULT_FLAG_WRITE;
|
|
if (nonblocking)
|
|
fault_flags |= FAULT_FLAG_ALLOW_RETRY;
|
|
if (flags & FOLL_NOWAIT)
|
|
fault_flags |= FAULT_FLAG_ALLOW_RETRY |
|
|
FAULT_FLAG_RETRY_NOWAIT;
|
|
if (flags & FOLL_TRIED) {
|
|
VM_WARN_ON_ONCE(fault_flags &
|
|
FAULT_FLAG_ALLOW_RETRY);
|
|
fault_flags |= FAULT_FLAG_TRIED;
|
|
}
|
|
ret = hugetlb_fault(mm, vma, vaddr, fault_flags);
|
|
if (ret & VM_FAULT_ERROR) {
|
|
err = vm_fault_to_errno(ret, flags);
|
|
remainder = 0;
|
|
break;
|
|
}
|
|
if (ret & VM_FAULT_RETRY) {
|
|
if (nonblocking &&
|
|
!(fault_flags & FAULT_FLAG_RETRY_NOWAIT))
|
|
*nonblocking = 0;
|
|
*nr_pages = 0;
|
|
/*
|
|
* VM_FAULT_RETRY must not return an
|
|
* error, it will return zero
|
|
* instead.
|
|
*
|
|
* No need to update "position" as the
|
|
* caller will not check it after
|
|
* *nr_pages is set to 0.
|
|
*/
|
|
return i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT;
|
|
page = pte_page(huge_ptep_get(pte));
|
|
|
|
/*
|
|
* Instead of doing 'try_get_page()' below in the same_page
|
|
* loop, just check the count once here.
|
|
*/
|
|
if (unlikely(page_count(page) <= 0)) {
|
|
if (pages) {
|
|
spin_unlock(ptl);
|
|
remainder = 0;
|
|
err = -ENOMEM;
|
|
break;
|
|
}
|
|
}
|
|
same_page:
|
|
if (pages) {
|
|
pages[i] = mem_map_offset(page, pfn_offset);
|
|
get_page(pages[i]);
|
|
}
|
|
|
|
if (vmas)
|
|
vmas[i] = vma;
|
|
|
|
vaddr += PAGE_SIZE;
|
|
++pfn_offset;
|
|
--remainder;
|
|
++i;
|
|
if (vaddr < vma->vm_end && remainder &&
|
|
pfn_offset < pages_per_huge_page(h)) {
|
|
/*
|
|
* We use pfn_offset to avoid touching the pageframes
|
|
* of this compound page.
|
|
*/
|
|
goto same_page;
|
|
}
|
|
spin_unlock(ptl);
|
|
}
|
|
*nr_pages = remainder;
|
|
/*
|
|
* setting position is actually required only if remainder is
|
|
* not zero but it's faster not to add a "if (remainder)"
|
|
* branch.
|
|
*/
|
|
*position = vaddr;
|
|
|
|
return i ? i : err;
|
|
}
|
|
|
|
#ifndef __HAVE_ARCH_FLUSH_HUGETLB_TLB_RANGE
|
|
/*
|
|
* ARCHes with special requirements for evicting HUGETLB backing TLB entries can
|
|
* implement this.
|
|
*/
|
|
#define flush_hugetlb_tlb_range(vma, addr, end) flush_tlb_range(vma, addr, end)
|
|
#endif
|
|
|
|
unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
|
|
unsigned long address, unsigned long end, pgprot_t newprot)
|
|
{
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
unsigned long start = address;
|
|
pte_t *ptep;
|
|
pte_t pte;
|
|
struct hstate *h = hstate_vma(vma);
|
|
unsigned long pages = 0;
|
|
bool shared_pmd = false;
|
|
struct mmu_notifier_range range;
|
|
|
|
/*
|
|
* In the case of shared PMDs, the area to flush could be beyond
|
|
* start/end. Set range.start/range.end to cover the maximum possible
|
|
* range if PMD sharing is possible.
|
|
*/
|
|
mmu_notifier_range_init(&range, MMU_NOTIFY_PROTECTION_VMA,
|
|
0, vma, mm, start, end);
|
|
adjust_range_if_pmd_sharing_possible(vma, &range.start, &range.end);
|
|
|
|
BUG_ON(address >= end);
|
|
flush_cache_range(vma, range.start, range.end);
|
|
|
|
mmu_notifier_invalidate_range_start(&range);
|
|
i_mmap_lock_write(vma->vm_file->f_mapping);
|
|
for (; address < end; address += huge_page_size(h)) {
|
|
spinlock_t *ptl;
|
|
ptep = huge_pte_offset(mm, address, huge_page_size(h));
|
|
if (!ptep)
|
|
continue;
|
|
ptl = huge_pte_lock(h, mm, ptep);
|
|
if (huge_pmd_unshare(mm, &address, ptep)) {
|
|
pages++;
|
|
spin_unlock(ptl);
|
|
shared_pmd = true;
|
|
continue;
|
|
}
|
|
pte = huge_ptep_get(ptep);
|
|
if (unlikely(is_hugetlb_entry_hwpoisoned(pte))) {
|
|
spin_unlock(ptl);
|
|
continue;
|
|
}
|
|
if (unlikely(is_hugetlb_entry_migration(pte))) {
|
|
swp_entry_t entry = pte_to_swp_entry(pte);
|
|
|
|
if (is_write_migration_entry(entry)) {
|
|
pte_t newpte;
|
|
|
|
make_migration_entry_read(&entry);
|
|
newpte = swp_entry_to_pte(entry);
|
|
set_huge_swap_pte_at(mm, address, ptep,
|
|
newpte, huge_page_size(h));
|
|
pages++;
|
|
}
|
|
spin_unlock(ptl);
|
|
continue;
|
|
}
|
|
if (!huge_pte_none(pte)) {
|
|
pte_t old_pte;
|
|
|
|
old_pte = huge_ptep_modify_prot_start(vma, address, ptep);
|
|
pte = pte_mkhuge(huge_pte_modify(old_pte, newprot));
|
|
pte = arch_make_huge_pte(pte, vma, NULL, 0);
|
|
huge_ptep_modify_prot_commit(vma, address, ptep, old_pte, pte);
|
|
pages++;
|
|
}
|
|
spin_unlock(ptl);
|
|
}
|
|
/*
|
|
* Must flush TLB before releasing i_mmap_rwsem: x86's huge_pmd_unshare
|
|
* may have cleared our pud entry and done put_page on the page table:
|
|
* once we release i_mmap_rwsem, another task can do the final put_page
|
|
* and that page table be reused and filled with junk. If we actually
|
|
* did unshare a page of pmds, flush the range corresponding to the pud.
|
|
*/
|
|
if (shared_pmd)
|
|
flush_hugetlb_tlb_range(vma, range.start, range.end);
|
|
else
|
|
flush_hugetlb_tlb_range(vma, start, end);
|
|
/*
|
|
* No need to call mmu_notifier_invalidate_range() we are downgrading
|
|
* page table protection not changing it to point to a new page.
|
|
*
|
|
* See Documentation/vm/mmu_notifier.rst
|
|
*/
|
|
i_mmap_unlock_write(vma->vm_file->f_mapping);
|
|
mmu_notifier_invalidate_range_end(&range);
|
|
|
|
return pages << h->order;
|
|
}
|
|
|
|
int hugetlb_reserve_pages(struct inode *inode,
|
|
long from, long to,
|
|
struct vm_area_struct *vma,
|
|
vm_flags_t vm_flags)
|
|
{
|
|
long ret, chg;
|
|
struct hstate *h = hstate_inode(inode);
|
|
struct hugepage_subpool *spool = subpool_inode(inode);
|
|
struct resv_map *resv_map;
|
|
long gbl_reserve;
|
|
|
|
/* This should never happen */
|
|
if (from > to) {
|
|
VM_WARN(1, "%s called with a negative range\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Only apply hugepage reservation if asked. At fault time, an
|
|
* attempt will be made for VM_NORESERVE to allocate a page
|
|
* without using reserves
|
|
*/
|
|
if (vm_flags & VM_NORESERVE)
|
|
return 0;
|
|
|
|
/*
|
|
* Shared mappings base their reservation on the number of pages that
|
|
* are already allocated on behalf of the file. Private mappings need
|
|
* to reserve the full area even if read-only as mprotect() may be
|
|
* called to make the mapping read-write. Assume !vma is a shm mapping
|
|
*/
|
|
if (!vma || vma->vm_flags & VM_MAYSHARE) {
|
|
/*
|
|
* resv_map can not be NULL as hugetlb_reserve_pages is only
|
|
* called for inodes for which resv_maps were created (see
|
|
* hugetlbfs_get_inode).
|
|
*/
|
|
resv_map = inode_resv_map(inode);
|
|
|
|
chg = region_chg(resv_map, from, to);
|
|
|
|
} else {
|
|
resv_map = resv_map_alloc();
|
|
if (!resv_map)
|
|
return -ENOMEM;
|
|
|
|
chg = to - from;
|
|
|
|
set_vma_resv_map(vma, resv_map);
|
|
set_vma_resv_flags(vma, HPAGE_RESV_OWNER);
|
|
}
|
|
|
|
if (chg < 0) {
|
|
ret = chg;
|
|
goto out_err;
|
|
}
|
|
|
|
/*
|
|
* There must be enough pages in the subpool for the mapping. If
|
|
* the subpool has a minimum size, there may be some global
|
|
* reservations already in place (gbl_reserve).
|
|
*/
|
|
gbl_reserve = hugepage_subpool_get_pages(spool, chg);
|
|
if (gbl_reserve < 0) {
|
|
ret = -ENOSPC;
|
|
goto out_err;
|
|
}
|
|
|
|
/*
|
|
* Check enough hugepages are available for the reservation.
|
|
* Hand the pages back to the subpool if there are not
|
|
*/
|
|
ret = hugetlb_acct_memory(h, gbl_reserve);
|
|
if (ret < 0) {
|
|
/* put back original number of pages, chg */
|
|
(void)hugepage_subpool_put_pages(spool, chg);
|
|
goto out_err;
|
|
}
|
|
|
|
/*
|
|
* Account for the reservations made. Shared mappings record regions
|
|
* that have reservations as they are shared by multiple VMAs.
|
|
* When the last VMA disappears, the region map says how much
|
|
* the reservation was and the page cache tells how much of
|
|
* the reservation was consumed. Private mappings are per-VMA and
|
|
* only the consumed reservations are tracked. When the VMA
|
|
* disappears, the original reservation is the VMA size and the
|
|
* consumed reservations are stored in the map. Hence, nothing
|
|
* else has to be done for private mappings here
|
|
*/
|
|
if (!vma || vma->vm_flags & VM_MAYSHARE) {
|
|
long add = region_add(resv_map, from, to);
|
|
|
|
if (unlikely(chg > add)) {
|
|
/*
|
|
* pages in this range were added to the reserve
|
|
* map between region_chg and region_add. This
|
|
* indicates a race with alloc_huge_page. Adjust
|
|
* the subpool and reserve counts modified above
|
|
* based on the difference.
|
|
*/
|
|
long rsv_adjust;
|
|
|
|
rsv_adjust = hugepage_subpool_put_pages(spool,
|
|
chg - add);
|
|
hugetlb_acct_memory(h, -rsv_adjust);
|
|
}
|
|
}
|
|
return 0;
|
|
out_err:
|
|
if (!vma || vma->vm_flags & VM_MAYSHARE)
|
|
/* Don't call region_abort if region_chg failed */
|
|
if (chg >= 0)
|
|
region_abort(resv_map, from, to);
|
|
if (vma && is_vma_resv_set(vma, HPAGE_RESV_OWNER))
|
|
kref_put(&resv_map->refs, resv_map_release);
|
|
return ret;
|
|
}
|
|
|
|
long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
|
|
long freed)
|
|
{
|
|
struct hstate *h = hstate_inode(inode);
|
|
struct resv_map *resv_map = inode_resv_map(inode);
|
|
long chg = 0;
|
|
struct hugepage_subpool *spool = subpool_inode(inode);
|
|
long gbl_reserve;
|
|
|
|
/*
|
|
* Since this routine can be called in the evict inode path for all
|
|
* hugetlbfs inodes, resv_map could be NULL.
|
|
*/
|
|
if (resv_map) {
|
|
chg = region_del(resv_map, start, end);
|
|
/*
|
|
* region_del() can fail in the rare case where a region
|
|
* must be split and another region descriptor can not be
|
|
* allocated. If end == LONG_MAX, it will not fail.
|
|
*/
|
|
if (chg < 0)
|
|
return chg;
|
|
}
|
|
|
|
spin_lock(&inode->i_lock);
|
|
inode->i_blocks -= (blocks_per_huge_page(h) * freed);
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
/*
|
|
* If the subpool has a minimum size, the number of global
|
|
* reservations to be released may be adjusted.
|
|
*/
|
|
gbl_reserve = hugepage_subpool_put_pages(spool, (chg - freed));
|
|
hugetlb_acct_memory(h, -gbl_reserve);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
|
|
static unsigned long page_table_shareable(struct vm_area_struct *svma,
|
|
struct vm_area_struct *vma,
|
|
unsigned long addr, pgoff_t idx)
|
|
{
|
|
unsigned long saddr = ((idx - svma->vm_pgoff) << PAGE_SHIFT) +
|
|
svma->vm_start;
|
|
unsigned long sbase = saddr & PUD_MASK;
|
|
unsigned long s_end = sbase + PUD_SIZE;
|
|
|
|
/* Allow segments to share if only one is marked locked */
|
|
unsigned long vm_flags = vma->vm_flags & VM_LOCKED_CLEAR_MASK;
|
|
unsigned long svm_flags = svma->vm_flags & VM_LOCKED_CLEAR_MASK;
|
|
|
|
/*
|
|
* match the virtual addresses, permission and the alignment of the
|
|
* page table page.
|
|
*/
|
|
if (pmd_index(addr) != pmd_index(saddr) ||
|
|
vm_flags != svm_flags ||
|
|
sbase < svma->vm_start || svma->vm_end < s_end)
|
|
return 0;
|
|
|
|
return saddr;
|
|
}
|
|
|
|
static bool vma_shareable(struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
unsigned long base = addr & PUD_MASK;
|
|
unsigned long end = base + PUD_SIZE;
|
|
|
|
/*
|
|
* check on proper vm_flags and page table alignment
|
|
*/
|
|
if (vma->vm_flags & VM_MAYSHARE && range_in_vma(vma, base, end))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Determine if start,end range within vma could be mapped by shared pmd.
|
|
* If yes, adjust start and end to cover range associated with possible
|
|
* shared pmd mappings.
|
|
*/
|
|
void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,
|
|
unsigned long *start, unsigned long *end)
|
|
{
|
|
unsigned long v_start = ALIGN(vma->vm_start, PUD_SIZE),
|
|
v_end = ALIGN_DOWN(vma->vm_end, PUD_SIZE);
|
|
|
|
/*
|
|
* vma need span at least one aligned PUD size and the start,end range
|
|
* must at least partialy within it.
|
|
*/
|
|
if (!(vma->vm_flags & VM_MAYSHARE) || !(v_end > v_start) ||
|
|
(*end <= v_start) || (*start >= v_end))
|
|
return;
|
|
|
|
/* Extend the range to be PUD aligned for a worst case scenario */
|
|
if (*start > v_start)
|
|
*start = ALIGN_DOWN(*start, PUD_SIZE);
|
|
|
|
if (*end < v_end)
|
|
*end = ALIGN(*end, PUD_SIZE);
|
|
}
|
|
|
|
/*
|
|
* Search for a shareable pmd page for hugetlb. In any case calls pmd_alloc()
|
|
* and returns the corresponding pte. While this is not necessary for the
|
|
* !shared pmd case because we can allocate the pmd later as well, it makes the
|
|
* code much cleaner. pmd allocation is essential for the shared case because
|
|
* pud has to be populated inside the same i_mmap_rwsem section - otherwise
|
|
* racing tasks could either miss the sharing (see huge_pte_offset) or select a
|
|
* bad pmd for sharing.
|
|
*/
|
|
pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
|
|
{
|
|
struct vm_area_struct *vma = find_vma(mm, addr);
|
|
struct address_space *mapping = vma->vm_file->f_mapping;
|
|
pgoff_t idx = ((addr - vma->vm_start) >> PAGE_SHIFT) +
|
|
vma->vm_pgoff;
|
|
struct vm_area_struct *svma;
|
|
unsigned long saddr;
|
|
pte_t *spte = NULL;
|
|
pte_t *pte;
|
|
spinlock_t *ptl;
|
|
|
|
if (!vma_shareable(vma, addr))
|
|
return (pte_t *)pmd_alloc(mm, pud, addr);
|
|
|
|
i_mmap_lock_write(mapping);
|
|
vma_interval_tree_foreach(svma, &mapping->i_mmap, idx, idx) {
|
|
if (svma == vma)
|
|
continue;
|
|
|
|
saddr = page_table_shareable(svma, vma, addr, idx);
|
|
if (saddr) {
|
|
spte = huge_pte_offset(svma->vm_mm, saddr,
|
|
vma_mmu_pagesize(svma));
|
|
if (spte) {
|
|
get_page(virt_to_page(spte));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!spte)
|
|
goto out;
|
|
|
|
ptl = huge_pte_lock(hstate_vma(vma), mm, spte);
|
|
if (pud_none(*pud)) {
|
|
pud_populate(mm, pud,
|
|
(pmd_t *)((unsigned long)spte & PAGE_MASK));
|
|
mm_inc_nr_pmds(mm);
|
|
} else {
|
|
put_page(virt_to_page(spte));
|
|
}
|
|
spin_unlock(ptl);
|
|
out:
|
|
pte = (pte_t *)pmd_alloc(mm, pud, addr);
|
|
i_mmap_unlock_write(mapping);
|
|
return pte;
|
|
}
|
|
|
|
/*
|
|
* unmap huge page backed by shared pte.
|
|
*
|
|
* Hugetlb pte page is ref counted at the time of mapping. If pte is shared
|
|
* indicated by page_count > 1, unmap is achieved by clearing pud and
|
|
* decrementing the ref count. If count == 1, the pte page is not shared.
|
|
*
|
|
* called with page table lock held.
|
|
*
|
|
* returns: 1 successfully unmapped a shared pte page
|
|
* 0 the underlying pte page is not shared, or it is the last user
|
|
*/
|
|
int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
|
|
{
|
|
pgd_t *pgd = pgd_offset(mm, *addr);
|
|
p4d_t *p4d = p4d_offset(pgd, *addr);
|
|
pud_t *pud = pud_offset(p4d, *addr);
|
|
|
|
BUG_ON(page_count(virt_to_page(ptep)) == 0);
|
|
if (page_count(virt_to_page(ptep)) == 1)
|
|
return 0;
|
|
|
|
pud_clear(pud);
|
|
put_page(virt_to_page(ptep));
|
|
mm_dec_nr_pmds(mm);
|
|
/*
|
|
* This update of passed address optimizes loops sequentially
|
|
* processing addresses in increments of huge page size (PMD_SIZE
|
|
* in this case). By clearing the pud, a PUD_SIZE area is unmapped.
|
|
* Update address to the 'last page' in the cleared area so that
|
|
* calling loop can move to first page past this area.
|
|
*/
|
|
*addr |= PUD_SIZE - PMD_SIZE;
|
|
return 1;
|
|
}
|
|
#define want_pmd_share() (1)
|
|
#else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
|
|
pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,
|
|
unsigned long *start, unsigned long *end)
|
|
{
|
|
}
|
|
#define want_pmd_share() (0)
|
|
#endif /* CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
|
|
|
|
#ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
|
|
pte_t *huge_pte_alloc(struct mm_struct *mm,
|
|
unsigned long addr, unsigned long sz)
|
|
{
|
|
pgd_t *pgd;
|
|
p4d_t *p4d;
|
|
pud_t *pud;
|
|
pte_t *pte = NULL;
|
|
|
|
pgd = pgd_offset(mm, addr);
|
|
p4d = p4d_alloc(mm, pgd, addr);
|
|
if (!p4d)
|
|
return NULL;
|
|
pud = pud_alloc(mm, p4d, addr);
|
|
if (pud) {
|
|
if (sz == PUD_SIZE) {
|
|
pte = (pte_t *)pud;
|
|
} else {
|
|
BUG_ON(sz != PMD_SIZE);
|
|
if (want_pmd_share() && pud_none(*pud))
|
|
pte = huge_pmd_share(mm, addr, pud);
|
|
else
|
|
pte = (pte_t *)pmd_alloc(mm, pud, addr);
|
|
}
|
|
}
|
|
BUG_ON(pte && pte_present(*pte) && !pte_huge(*pte));
|
|
|
|
return pte;
|
|
}
|
|
|
|
/*
|
|
* huge_pte_offset() - Walk the page table to resolve the hugepage
|
|
* entry at address @addr
|
|
*
|
|
* Return: Pointer to page table or swap entry (PUD or PMD) for
|
|
* address @addr, or NULL if a p*d_none() entry is encountered and the
|
|
* size @sz doesn't match the hugepage size at this level of the page
|
|
* table.
|
|
*/
|
|
pte_t *huge_pte_offset(struct mm_struct *mm,
|
|
unsigned long addr, unsigned long sz)
|
|
{
|
|
pgd_t *pgd;
|
|
p4d_t *p4d;
|
|
pud_t *pud, pud_entry;
|
|
pmd_t *pmd, pmd_entry;
|
|
|
|
pgd = pgd_offset(mm, addr);
|
|
if (!pgd_present(*pgd))
|
|
return NULL;
|
|
p4d = p4d_offset(pgd, addr);
|
|
if (!p4d_present(*p4d))
|
|
return NULL;
|
|
|
|
pud = pud_offset(p4d, addr);
|
|
pud_entry = READ_ONCE(*pud);
|
|
if (sz != PUD_SIZE && pud_none(pud_entry))
|
|
return NULL;
|
|
/* hugepage or swap? */
|
|
if (pud_huge(pud_entry) || !pud_present(pud_entry))
|
|
return (pte_t *)pud;
|
|
|
|
pmd = pmd_offset(pud, addr);
|
|
pmd_entry = READ_ONCE(*pmd);
|
|
if (sz != PMD_SIZE && pmd_none(pmd_entry))
|
|
return NULL;
|
|
/* hugepage or swap? */
|
|
if (pmd_huge(pmd_entry) || !pmd_present(pmd_entry))
|
|
return (pte_t *)pmd;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
|
|
|
|
/*
|
|
* These functions are overwritable if your architecture needs its own
|
|
* behavior.
|
|
*/
|
|
struct page * __weak
|
|
follow_huge_addr(struct mm_struct *mm, unsigned long address,
|
|
int write)
|
|
{
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
struct page * __weak
|
|
follow_huge_pd(struct vm_area_struct *vma,
|
|
unsigned long address, hugepd_t hpd, int flags, int pdshift)
|
|
{
|
|
WARN(1, "hugepd follow called with no support for hugepage directory format\n");
|
|
return NULL;
|
|
}
|
|
|
|
struct page * __weak
|
|
follow_huge_pmd_pte(struct vm_area_struct *vma, unsigned long address, int flags)
|
|
{
|
|
struct hstate *h = hstate_vma(vma);
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
struct page *page = NULL;
|
|
spinlock_t *ptl;
|
|
pte_t *ptep, pte;
|
|
|
|
retry:
|
|
ptep = huge_pte_offset(mm, address, huge_page_size(h));
|
|
if (!ptep)
|
|
return NULL;
|
|
|
|
ptl = huge_pte_lock(h, mm, ptep);
|
|
pte = huge_ptep_get(ptep);
|
|
if (pte_present(pte)) {
|
|
page = pte_page(pte) +
|
|
((address & ~huge_page_mask(h)) >> PAGE_SHIFT);
|
|
if (flags & FOLL_GET)
|
|
get_page(page);
|
|
} else {
|
|
if (is_hugetlb_entry_migration(pte)) {
|
|
spin_unlock(ptl);
|
|
__migration_entry_wait(mm, ptep, ptl);
|
|
goto retry;
|
|
}
|
|
/*
|
|
* hwpoisoned entry is treated as no_page_table in
|
|
* follow_page_mask().
|
|
*/
|
|
}
|
|
|
|
spin_unlock(ptl);
|
|
return page;
|
|
}
|
|
|
|
struct page * __weak
|
|
follow_huge_pud(struct mm_struct *mm, unsigned long address,
|
|
pud_t *pud, int flags)
|
|
{
|
|
if (flags & FOLL_GET)
|
|
return NULL;
|
|
|
|
return pte_page(*(pte_t *)pud) + ((address & ~PUD_MASK) >> PAGE_SHIFT);
|
|
}
|
|
|
|
struct page * __weak
|
|
follow_huge_pgd(struct mm_struct *mm, unsigned long address, pgd_t *pgd, int flags)
|
|
{
|
|
if (flags & FOLL_GET)
|
|
return NULL;
|
|
|
|
return pte_page(*(pte_t *)pgd) + ((address & ~PGDIR_MASK) >> PAGE_SHIFT);
|
|
}
|
|
|
|
bool isolate_huge_page(struct page *page, struct list_head *list)
|
|
{
|
|
bool ret = true;
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
if (!PageHeadHuge(page) || !page_huge_active(page) ||
|
|
!get_page_unless_zero(page)) {
|
|
ret = false;
|
|
goto unlock;
|
|
}
|
|
clear_page_huge_active(page);
|
|
list_move_tail(&page->lru, list);
|
|
unlock:
|
|
spin_unlock(&hugetlb_lock);
|
|
return ret;
|
|
}
|
|
|
|
void putback_active_hugepage(struct page *page)
|
|
{
|
|
VM_BUG_ON_PAGE(!PageHead(page), page);
|
|
spin_lock(&hugetlb_lock);
|
|
set_page_huge_active(page);
|
|
list_move_tail(&page->lru, &(page_hstate(page))->hugepage_activelist);
|
|
spin_unlock(&hugetlb_lock);
|
|
put_page(page);
|
|
}
|
|
|
|
void move_hugetlb_state(struct page *oldpage, struct page *newpage, int reason)
|
|
{
|
|
struct hstate *h = page_hstate(oldpage);
|
|
|
|
hugetlb_cgroup_migrate(oldpage, newpage);
|
|
set_page_owner_migrate_reason(newpage, reason);
|
|
|
|
/*
|
|
* transfer temporary state of the new huge page. This is
|
|
* reverse to other transitions because the newpage is going to
|
|
* be final while the old one will be freed so it takes over
|
|
* the temporary status.
|
|
*
|
|
* Also note that we have to transfer the per-node surplus state
|
|
* here as well otherwise the global surplus count will not match
|
|
* the per-node's.
|
|
*/
|
|
if (PageHugeTemporary(newpage)) {
|
|
int old_nid = page_to_nid(oldpage);
|
|
int new_nid = page_to_nid(newpage);
|
|
|
|
SetPageHugeTemporary(oldpage);
|
|
ClearPageHugeTemporary(newpage);
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
if (h->surplus_huge_pages_node[old_nid]) {
|
|
h->surplus_huge_pages_node[old_nid]--;
|
|
h->surplus_huge_pages_node[new_nid]++;
|
|
}
|
|
spin_unlock(&hugetlb_lock);
|
|
}
|
|
}
|