mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
* refs/heads/tmp-4344de2: Linux 4.14.108 s390/setup: fix boot crash for machine without EDAT-1 KVM: nVMX: Ignore limit checks on VMX instructions using flat segments KVM: nVMX: Apply addr size mask to effective address for VMX instructions KVM: nVMX: Sign extend displacements of VMX instr's mem operands KVM: x86/mmu: Do not cache MMIO accesses while memslots are in flux KVM: x86/mmu: Detect MMIO generation wrap in any address space KVM: Call kvm_arch_memslots_updated() before updating memslots drm/radeon/evergreen_cs: fix missing break in switch statement media: imx: csi: Stop upstream before disabling IDMA channel media: imx: csi: Disable CSI immediately after last EOF media: vimc: Add vimc-streamer for stream control media: uvcvideo: Avoid NULL pointer dereference at the end of streaming media: imx: prpencvf: Stop upstream before disabling IDMA channel rcu: Do RCU GP kthread self-wakeup from softirq and interrupt tpm: Unify the send callback behaviour tpm/tpm_crb: Avoid unaligned reads in crb_recv() md: Fix failed allocation of md_register_thread perf intel-pt: Fix divide by zero when TSC is not available perf intel-pt: Fix overlap calculation for padding perf auxtrace: Define auxtrace record alignment perf intel-pt: Fix CYC timestamp calculation after OVF x86/unwind/orc: Fix ORC unwind table alignment bcache: never writeback a discard operation PM / wakeup: Rework wakeup source timer cancellation NFSv4.1: Reinitialise sequence results before retransmitting a request nfsd: fix wrong check in write_v4_end_grace() nfsd: fix memory corruption caused by readdir NFS: Don't recoalesce on error in nfs_pageio_complete_mirror() NFS: Fix an I/O request leakage in nfs_do_recoalesce NFS: Fix I/O request leakages cpcap-charger: generate events for userspace dm integrity: limit the rate of error messages dm: fix to_sector() for 32bit arm64: KVM: Fix architecturally invalid reset value for FPEXC32_EL2 arm64: debug: Ensure debug handlers check triggering exception level arm64: Fix HCR.TGE status for NMI contexts ARM: s3c24xx: Fix boolean expressions in osiris_dvs_notify powerpc/traps: Fix the message printed when stack overflows powerpc/traps: fix recoverability of machine check handling on book3s/32 powerpc/hugetlb: Don't do runtime allocation of 16G pages in LPAR configuration powerpc/ptrace: Simplify vr_get/set() to avoid GCC warning powerpc: Fix 32-bit KVM-PR lockup and host crash with MacOS guest powerpc/83xx: Also save/restore SPRG4-7 during suspend powerpc/powernv: Make opal log only readable by root powerpc/wii: properly disable use of BATs when requested. powerpc/32: Clear on-stack exception marker upon exception return security/selinux: fix SECURITY_LSM_NATIVE_LABELS on reused superblock jbd2: fix compile warning when using JBUFFER_TRACE jbd2: clear dirty flag when revoking a buffer from an older transaction serial: 8250_pci: Have ACCES cards that use the four port Pericom PI7C9X7954 chip use the pci_pericom_setup() serial: 8250_pci: Fix number of ports for ACCES serial cards serial: 8250_of: assume reg-shift of 2 for mrvl,mmp-uart serial: uartps: Fix stuck ISR if RX disabled with non-empty FIFO drm/i915: Relax mmap VMA check crypto: arm64/aes-neonbs - fix returning final keystream block i2c: tegra: fix maximum transfer size parport_pc: fix find_superio io compare code, should use equal test. intel_th: Don't reference unassigned outputs device property: Fix the length used in PROPERTY_ENTRY_STRING() kernel/sysctl.c: add missing range check in do_proc_dointvec_minmax_conv mm/vmalloc: fix size check for remap_vmalloc_range_partial() mm: hwpoison: fix thp split handing in soft_offline_in_use_page() nfit: acpi_nfit_ctl(): Check out_obj->type in the right place usb: chipidea: tegra: Fix missed ci_hdrc_remove_device() clk: ingenic: Fix doc of ingenic_cgu_div_info clk: ingenic: Fix round_rate misbehaving with non-integer dividers clk: clk-twl6040: Fix imprecise external abort for pdmclk clk: uniphier: Fix update register for CPU-gear ext2: Fix underflow in ext2_max_size() cxl: Wrap iterations over afu slices inside 'afu_list_lock' IB/hfi1: Close race condition on user context disable and close ext4: fix crash during online resizing ext4: add mask of ext4 flags to swap cpufreq: pxa2xx: remove incorrect __init annotation cpufreq: tegra124: add missing of_node_put() x86/kprobes: Prohibit probing on optprobe template code irqchip/gic-v3-its: Avoid parsing _indirect_ twice for Device table libertas_tf: don't set URB_ZERO_PACKET on IN USB transfer crypto: pcbc - remove bogus memcpy()s with src == dest Btrfs: fix corruption reading shared and compressed extents after hole punching btrfs: ensure that a DUP or RAID1 block group has exactly two stripes Btrfs: setup a nofs context for memory allocation at __btrfs_set_acl m68k: Add -ffreestanding to CFLAGS splice: don't merge into linked buffers fs/devpts: always delete dcache dentry-s in dput() scsi: target/iscsi: Avoid iscsit_release_commands_from_conn() deadlock scsi: sd: Optimal I/O size should be a multiple of physical block size scsi: aacraid: Fix performance issue on logical drives scsi: virtio_scsi: don't send sc payload with tmfs s390/virtio: handle find on invalid queue gracefully s390/setup: fix early warning messages clocksource/drivers/exynos_mct: Clear timer interrupt when shutdown clocksource/drivers/exynos_mct: Move one-shot check from tick clear to ISR regulator: s2mpa01: Fix step values for some LDOs regulator: max77620: Initialize values for DT properties regulator: s2mps11: Fix steps for buck7, buck8 and LDO35 spi: pxa2xx: Setup maximum supported DMA transfer length spi: ti-qspi: Fix mmap read when more than one CS in use mmc: sdhci-esdhc-imx: fix HS400 timing issue ACPI / device_sysfs: Avoid OF modalias creation for removed device xen: fix dom0 boot on huge systems tracing: Do not free iter->trace in fail path of tracing_open_pipe() tracing: Use strncpy instead of memcpy for string keys in hist triggers CIFS: Fix read after write for files with read caching CIFS: Do not reset lease state to NONE on lease break crypto: arm64/aes-ccm - fix bugs in non-NEON fallback routine crypto: arm64/aes-ccm - fix logical bug in AAD MAC handling crypto: testmgr - skip crc32c context test for ahash algorithms crypto: hash - set CRYPTO_TFM_NEED_KEY if ->setkey() fails crypto: arm64/crct10dif - revert to C code for short inputs crypto: arm/crct10dif - revert to C code for short inputs fix cgroup_do_mount() handling of failure exits libnvdimm: Fix altmap reservation size calculation libnvdimm/pmem: Honor force_raw for legacy pmem regions libnvdimm, pfn: Fix over-trim in trim_pfn_device() libnvdimm/label: Clear 'updating' flag after label-set update stm class: Prevent division by zero media: videobuf2-v4l2: drop WARN_ON in vb2_warn_zero_bytesused() tmpfs: fix uninitialized return value in shmem_link net: set static variable an initial value in atl2_probe() nfp: bpf: fix ALU32 high bits clearance bug nfp: bpf: fix code-gen bug on BPF_ALU | BPF_XOR | BPF_K net: thunderx: make CFG_DONE message to run through generic send-ack sequence mac80211_hwsim: propagate genlmsg_reply return code phonet: fix building with clang ARCv2: support manual regfile save on interrupts ARC: uacces: remove lp_start, lp_end from clobber list ARCv2: lib: memcpy: fix doing prefetchw outside of buffer ixgbe: fix older devices that do not support IXGBE_MRQC_L3L4TXSWEN tmpfs: fix link accounting when a tmpfile is linked in net: marvell: mvneta: fix DMA debug warning arm64: Relax GIC version check during early boot qed: Fix iWARP syn packet mac address validation. ASoC: topology: free created components in tplg load error mailbox: bcm-flexrm-mailbox: Fix FlexRM ring flush timeout issue net: mv643xx_eth: disable clk on error path in mv643xx_eth_shared_probe() qmi_wwan: apply SET_DTR quirk to Sierra WP7607 pinctrl: meson: meson8b: fix the sdxc_a data 1..3 pins net: systemport: Fix reception of BPDUs scsi: libiscsi: Fix race between iscsi_xmit_task and iscsi_complete_task keys: Fix dependency loop between construction record and auth key assoc_array: Fix shortcut creation af_key: unconditionally clone on broadcast ARM: 8824/1: fix a migrating irq bug when hotplug cpu esp: Skip TX bytes accounting when sending from a request socket clk: sunxi: A31: Fix wrong AHB gate number clk: sunxi-ng: v3s: Fix TCON reset de-assert bit Input: st-keyscan - fix potential zalloc NULL dereference auxdisplay: ht16k33: fix potential user-after-free on module unload i2c: bcm2835: Clear current buffer pointers and counts after a transfer i2c: cadence: Fix the hold bit setting net: hns: Fix object reference leaks in hns_dsaf_roce_reset() mm: page_alloc: fix ref bias in page_frag_alloc() for 1-byte allocs Revert "mm: use early_pfn_to_nid in page_ext_init" mm/gup: fix gup_pmd_range() for dax NFS: Don't use page_file_mapping after removing the page floppy: check_events callback should not return a negative number ipvs: fix dependency on nf_defrag_ipv6 mac80211: Fix Tx aggregation session tear down with ITXQs Input: matrix_keypad - use flush_delayed_work() Input: ps2-gpio - flush TX work when closing port Input: cap11xx - switch to using set_brightness_blocking() ARM: OMAP2+: fix lack of timer interrupts on CPU1 after hotplug KVM: arm/arm64: Reset the VCPU without preemption and vcpu state loaded ASoC: rsnd: fixup rsnd_ssi_master_clk_start() user count check ASoC: dapm: fix out-of-bounds accesses to DAPM lookup tables ARM: OMAP2+: Variable "reg" in function omap4_dsi_mux_pads() could be uninitialized Input: pwm-vibra - stop regulator after disabling pwm, not before Input: pwm-vibra - prevent unbalanced regulator s390/dasd: fix using offset into zero size array error gpu: ipu-v3: Fix CSI offsets for imx53 drm/imx: imx-ldb: add missing of_node_puts gpu: ipu-v3: Fix i.MX51 CSI control registers offset drm/imx: ignore plane updates on disabled crtcs crypto: rockchip - update new iv to device in multiple operations crypto: rockchip - fix scatterlist nents error crypto: ahash - fix another early termination in hash walk crypto: caam - fixed handling of sg list stm class: Fix an endless loop in channel allocation iio: adc: exynos-adc: Fix NULL pointer exception on unbind ASoC: fsl_esai: fix register setting issue in RIGHT_J mode 9p/net: fix memory leak in p9_client_create 9p: use inode->i_lock to protect i_size_write() under 32-bit FROMLIST: psi: introduce psi monitor FROMLIST: refactor header includes to allow kthread.h inclusion in psi_types.h FROMLIST: psi: track changed states FROMLIST: psi: split update_stats into parts FROMLIST: psi: rename psi fields in preparation for psi trigger addition FROMLIST: psi: make psi_enable static FROMLIST: psi: introduce state_mask to represent stalled psi states ANDROID: cuttlefish_defconfig: Enable CONFIG_INPUT_MOUSEDEV ANDROID: cuttlefish_defconfig: Enable CONFIG_PSI BACKPORT: kernel: cgroup: add poll file operation BACKPORT: fs: kernfs: add poll file operation UPSTREAM: psi: avoid divide-by-zero crash inside virtual machines UPSTREAM: psi: clarify the Kconfig text for the default-disable option UPSTREAM: psi: fix aggregation idle shut-off UPSTREAM: psi: fix reference to kernel commandline enable UPSTREAM: psi: make disabling/enabling easier for vendor kernels UPSTREAM: kernel/sched/psi.c: simplify cgroup_move_task() BACKPORT: psi: cgroup support UPSTREAM: psi: pressure stall information for CPU, memory, and IO UPSTREAM: sched: introduce this_rq_lock_irq() UPSTREAM: sched: sched.h: make rq locking and clock functions available in stats.h UPSTREAM: sched: loadavg: make calc_load_n() public BACKPORT: sched: loadavg: consolidate LOAD_INT, LOAD_FRAC, CALC_LOAD UPSTREAM: delayacct: track delays from thrashing cache pages UPSTREAM: mm: workingset: tell cache transitions from workingset thrashing sched/fair: fix energy compute when a cluster is only a cpu core in multi-cluster system Conflicts: arch/arm/kernel/irq.c drivers/scsi/sd.c include/linux/sched.h include/uapi/linux/taskstats.h kernel/sched/Makefile sound/soc/soc-dapm.c Change-Id: I12ebb57a34da9101ee19458d7e1f96ecc769c39a Signed-off-by: Blagovest Kolenichev <bkolenichev@codeaurora.org>
857 lines
23 KiB
C
857 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/mm/swap_state.c
|
|
*
|
|
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
|
|
* Swap reorganised 29.12.95, Stephen Tweedie
|
|
*
|
|
* Rewritten to use page cache, (C) 1998 Stephen Tweedie
|
|
*/
|
|
#include <linux/mm.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/swapops.h>
|
|
#include <linux/init.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/backing-dev.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/pagevec.h>
|
|
#include <linux/migrate.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/swap_slots.h>
|
|
#include <linux/huge_mm.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
/*
|
|
* swapper_space is a fiction, retained to simplify the path through
|
|
* vmscan's shrink_page_list.
|
|
*/
|
|
static const struct address_space_operations swap_aops = {
|
|
.writepage = swap_writepage,
|
|
.set_page_dirty = swap_set_page_dirty,
|
|
#ifdef CONFIG_MIGRATION
|
|
.migratepage = migrate_page,
|
|
#endif
|
|
};
|
|
|
|
struct address_space *swapper_spaces[MAX_SWAPFILES] __read_mostly;
|
|
static unsigned int nr_swapper_spaces[MAX_SWAPFILES] __read_mostly;
|
|
bool enable_vma_readahead __read_mostly = true;
|
|
|
|
#define SWAP_RA_WIN_SHIFT (PAGE_SHIFT / 2)
|
|
#define SWAP_RA_HITS_MASK ((1UL << SWAP_RA_WIN_SHIFT) - 1)
|
|
#define SWAP_RA_HITS_MAX SWAP_RA_HITS_MASK
|
|
#define SWAP_RA_WIN_MASK (~PAGE_MASK & ~SWAP_RA_HITS_MASK)
|
|
|
|
#define SWAP_RA_HITS(v) ((v) & SWAP_RA_HITS_MASK)
|
|
#define SWAP_RA_WIN(v) (((v) & SWAP_RA_WIN_MASK) >> SWAP_RA_WIN_SHIFT)
|
|
#define SWAP_RA_ADDR(v) ((v) & PAGE_MASK)
|
|
|
|
#define SWAP_RA_VAL(addr, win, hits) \
|
|
(((addr) & PAGE_MASK) | \
|
|
(((win) << SWAP_RA_WIN_SHIFT) & SWAP_RA_WIN_MASK) | \
|
|
((hits) & SWAP_RA_HITS_MASK))
|
|
|
|
/* Initial readahead hits is 4 to start up with a small window */
|
|
#define GET_SWAP_RA_VAL(vma) \
|
|
(atomic_long_read(&(vma)->swap_readahead_info) ? : 4)
|
|
|
|
#define INC_CACHE_INFO(x) do { swap_cache_info.x++; } while (0)
|
|
#define ADD_CACHE_INFO(x, nr) do { swap_cache_info.x += (nr); } while (0)
|
|
|
|
static struct {
|
|
unsigned long add_total;
|
|
unsigned long del_total;
|
|
unsigned long find_success;
|
|
unsigned long find_total;
|
|
} swap_cache_info;
|
|
|
|
unsigned long total_swapcache_pages(void)
|
|
{
|
|
unsigned int i, j, nr;
|
|
unsigned long ret = 0;
|
|
struct address_space *spaces;
|
|
|
|
rcu_read_lock();
|
|
for (i = 0; i < MAX_SWAPFILES; i++) {
|
|
/*
|
|
* The corresponding entries in nr_swapper_spaces and
|
|
* swapper_spaces will be reused only after at least
|
|
* one grace period. So it is impossible for them
|
|
* belongs to different usage.
|
|
*/
|
|
nr = nr_swapper_spaces[i];
|
|
spaces = rcu_dereference(swapper_spaces[i]);
|
|
if (!nr || !spaces)
|
|
continue;
|
|
for (j = 0; j < nr; j++)
|
|
ret += spaces[j].nrpages;
|
|
}
|
|
rcu_read_unlock();
|
|
return ret;
|
|
}
|
|
|
|
static atomic_t swapin_readahead_hits = ATOMIC_INIT(4);
|
|
|
|
void show_swap_cache_info(void)
|
|
{
|
|
printk("%lu pages in swap cache\n", total_swapcache_pages());
|
|
printk("Swap cache stats: add %lu, delete %lu, find %lu/%lu\n",
|
|
swap_cache_info.add_total, swap_cache_info.del_total,
|
|
swap_cache_info.find_success, swap_cache_info.find_total);
|
|
printk("Free swap = %ldkB\n",
|
|
get_nr_swap_pages() << (PAGE_SHIFT - 10));
|
|
printk("Total swap = %lukB\n", total_swap_pages << (PAGE_SHIFT - 10));
|
|
}
|
|
|
|
/*
|
|
* __add_to_swap_cache resembles add_to_page_cache_locked on swapper_space,
|
|
* but sets SwapCache flag and private instead of mapping and index.
|
|
*/
|
|
int __add_to_swap_cache(struct page *page, swp_entry_t entry)
|
|
{
|
|
int error, i, nr = hpage_nr_pages(page);
|
|
struct address_space *address_space;
|
|
pgoff_t idx = swp_offset(entry);
|
|
|
|
VM_BUG_ON_PAGE(!PageLocked(page), page);
|
|
VM_BUG_ON_PAGE(PageSwapCache(page), page);
|
|
VM_BUG_ON_PAGE(!PageSwapBacked(page), page);
|
|
|
|
page_ref_add(page, nr);
|
|
SetPageSwapCache(page);
|
|
|
|
address_space = swap_address_space(entry);
|
|
spin_lock_irq(&address_space->tree_lock);
|
|
for (i = 0; i < nr; i++) {
|
|
set_page_private(page + i, entry.val + i);
|
|
error = radix_tree_insert(&address_space->page_tree,
|
|
idx + i, page + i);
|
|
if (unlikely(error))
|
|
break;
|
|
}
|
|
if (likely(!error)) {
|
|
address_space->nrpages += nr;
|
|
__mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, nr);
|
|
ADD_CACHE_INFO(add_total, nr);
|
|
} else {
|
|
/*
|
|
* Only the context which have set SWAP_HAS_CACHE flag
|
|
* would call add_to_swap_cache().
|
|
* So add_to_swap_cache() doesn't returns -EEXIST.
|
|
*/
|
|
VM_BUG_ON(error == -EEXIST);
|
|
set_page_private(page + i, 0UL);
|
|
while (i--) {
|
|
radix_tree_delete(&address_space->page_tree, idx + i);
|
|
set_page_private(page + i, 0UL);
|
|
}
|
|
ClearPageSwapCache(page);
|
|
page_ref_sub(page, nr);
|
|
}
|
|
spin_unlock_irq(&address_space->tree_lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
|
|
{
|
|
int error;
|
|
|
|
error = radix_tree_maybe_preload_order(gfp_mask, compound_order(page));
|
|
if (!error) {
|
|
error = __add_to_swap_cache(page, entry);
|
|
radix_tree_preload_end();
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* This must be called only on pages that have
|
|
* been verified to be in the swap cache.
|
|
*/
|
|
void __delete_from_swap_cache(struct page *page)
|
|
{
|
|
struct address_space *address_space;
|
|
int i, nr = hpage_nr_pages(page);
|
|
swp_entry_t entry;
|
|
pgoff_t idx;
|
|
|
|
VM_BUG_ON_PAGE(!PageLocked(page), page);
|
|
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
|
|
VM_BUG_ON_PAGE(PageWriteback(page), page);
|
|
|
|
entry.val = page_private(page);
|
|
address_space = swap_address_space(entry);
|
|
idx = swp_offset(entry);
|
|
for (i = 0; i < nr; i++) {
|
|
radix_tree_delete(&address_space->page_tree, idx + i);
|
|
set_page_private(page + i, 0);
|
|
}
|
|
ClearPageSwapCache(page);
|
|
address_space->nrpages -= nr;
|
|
__mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, -nr);
|
|
ADD_CACHE_INFO(del_total, nr);
|
|
}
|
|
|
|
/**
|
|
* add_to_swap - allocate swap space for a page
|
|
* @page: page we want to move to swap
|
|
*
|
|
* Allocate swap space for the page and add the page to the
|
|
* swap cache. Caller needs to hold the page lock.
|
|
*/
|
|
int add_to_swap(struct page *page)
|
|
{
|
|
swp_entry_t entry;
|
|
int err;
|
|
|
|
VM_BUG_ON_PAGE(!PageLocked(page), page);
|
|
VM_BUG_ON_PAGE(!PageUptodate(page), page);
|
|
|
|
entry = get_swap_page(page);
|
|
if (!entry.val)
|
|
return 0;
|
|
|
|
if (mem_cgroup_try_charge_swap(page, entry))
|
|
goto fail;
|
|
|
|
/*
|
|
* Radix-tree node allocations from PF_MEMALLOC contexts could
|
|
* completely exhaust the page allocator. __GFP_NOMEMALLOC
|
|
* stops emergency reserves from being allocated.
|
|
*
|
|
* TODO: this could cause a theoretical memory reclaim
|
|
* deadlock in the swap out path.
|
|
*/
|
|
/*
|
|
* Add it to the swap cache.
|
|
*/
|
|
err = add_to_swap_cache(page, entry,
|
|
__GFP_HIGH|__GFP_NOMEMALLOC|__GFP_NOWARN);
|
|
/* -ENOMEM radix-tree allocation failure */
|
|
if (err)
|
|
/*
|
|
* add_to_swap_cache() doesn't return -EEXIST, so we can safely
|
|
* clear SWAP_HAS_CACHE flag.
|
|
*/
|
|
goto fail;
|
|
/*
|
|
* Normally the page will be dirtied in unmap because its pte should be
|
|
* dirty. A special case is MADV_FREE page. The page'e pte could have
|
|
* dirty bit cleared but the page's SwapBacked bit is still set because
|
|
* clearing the dirty bit and SwapBacked bit has no lock protected. For
|
|
* such page, unmap will not set dirty bit for it, so page reclaim will
|
|
* not write the page out. This can cause data corruption when the page
|
|
* is swap in later. Always setting the dirty bit for the page solves
|
|
* the problem.
|
|
*/
|
|
set_page_dirty(page);
|
|
|
|
return 1;
|
|
|
|
fail:
|
|
put_swap_page(page, entry);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This must be called only on pages that have
|
|
* been verified to be in the swap cache and locked.
|
|
* It will never put the page into the free list,
|
|
* the caller has a reference on the page.
|
|
*/
|
|
void delete_from_swap_cache(struct page *page)
|
|
{
|
|
swp_entry_t entry;
|
|
struct address_space *address_space;
|
|
|
|
entry.val = page_private(page);
|
|
|
|
address_space = swap_address_space(entry);
|
|
spin_lock_irq(&address_space->tree_lock);
|
|
__delete_from_swap_cache(page);
|
|
spin_unlock_irq(&address_space->tree_lock);
|
|
|
|
put_swap_page(page, entry);
|
|
page_ref_sub(page, hpage_nr_pages(page));
|
|
}
|
|
|
|
/*
|
|
* If we are the only user, then try to free up the swap cache.
|
|
*
|
|
* Its ok to check for PageSwapCache without the page lock
|
|
* here because we are going to recheck again inside
|
|
* try_to_free_swap() _with_ the lock.
|
|
* - Marcelo
|
|
*/
|
|
static inline void free_swap_cache(struct page *page)
|
|
{
|
|
if (PageSwapCache(page) && !page_mapped(page) && trylock_page(page)) {
|
|
try_to_free_swap(page);
|
|
unlock_page(page);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Perform a free_page(), also freeing any swap cache associated with
|
|
* this page if it is the last user of the page.
|
|
*/
|
|
void free_page_and_swap_cache(struct page *page)
|
|
{
|
|
free_swap_cache(page);
|
|
if (!is_huge_zero_page(page))
|
|
put_page(page);
|
|
}
|
|
|
|
/*
|
|
* Passed an array of pages, drop them all from swapcache and then release
|
|
* them. They are removed from the LRU and freed if this is their last use.
|
|
*/
|
|
void free_pages_and_swap_cache(struct page **pages, int nr)
|
|
{
|
|
struct page **pagep = pages;
|
|
int i;
|
|
|
|
lru_add_drain();
|
|
for (i = 0; i < nr; i++)
|
|
free_swap_cache(pagep[i]);
|
|
release_pages(pagep, nr, false);
|
|
}
|
|
|
|
static inline bool swap_use_vma_readahead(void)
|
|
{
|
|
return READ_ONCE(enable_vma_readahead) && !atomic_read(&nr_rotate_swap);
|
|
}
|
|
|
|
/*
|
|
* Lookup a swap entry in the swap cache. A found page will be returned
|
|
* unlocked and with its refcount incremented - we rely on the kernel
|
|
* lock getting page table operations atomic even if we drop the page
|
|
* lock before returning.
|
|
*/
|
|
struct page *lookup_swap_cache(swp_entry_t entry, struct vm_area_struct *vma,
|
|
unsigned long addr)
|
|
{
|
|
struct page *page;
|
|
|
|
page = find_get_page(swap_address_space(entry), swp_offset(entry));
|
|
|
|
INC_CACHE_INFO(find_total);
|
|
if (page) {
|
|
bool vma_ra = swap_use_vma_readahead();
|
|
bool readahead = TestClearPageReadahead(page);
|
|
|
|
INC_CACHE_INFO(find_success);
|
|
if (unlikely(PageTransCompound(page)))
|
|
return page;
|
|
|
|
if (vma && vma_ra) {
|
|
unsigned long ra_val;
|
|
int win, hits;
|
|
|
|
ra_val = GET_SWAP_RA_VAL(vma);
|
|
win = SWAP_RA_WIN(ra_val);
|
|
hits = SWAP_RA_HITS(ra_val);
|
|
if (readahead)
|
|
hits = min_t(int, hits + 1, SWAP_RA_HITS_MAX);
|
|
atomic_long_set(&vma->swap_readahead_info,
|
|
SWAP_RA_VAL(addr, win, hits));
|
|
}
|
|
|
|
if (readahead) {
|
|
count_vm_event(SWAP_RA_HIT);
|
|
if (!vma || !vma_ra)
|
|
atomic_inc(&swapin_readahead_hits);
|
|
}
|
|
}
|
|
|
|
return page;
|
|
}
|
|
|
|
struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
|
|
struct vm_area_struct *vma, unsigned long addr,
|
|
bool *new_page_allocated)
|
|
{
|
|
struct page *found_page, *new_page = NULL;
|
|
struct address_space *swapper_space = swap_address_space(entry);
|
|
int err;
|
|
*new_page_allocated = false;
|
|
|
|
do {
|
|
/*
|
|
* First check the swap cache. Since this is normally
|
|
* called after lookup_swap_cache() failed, re-calling
|
|
* that would confuse statistics.
|
|
*/
|
|
found_page = find_get_page(swapper_space, swp_offset(entry));
|
|
if (found_page)
|
|
break;
|
|
|
|
/*
|
|
* Just skip read ahead for unused swap slot.
|
|
* During swap_off when swap_slot_cache is disabled,
|
|
* we have to handle the race between putting
|
|
* swap entry in swap cache and marking swap slot
|
|
* as SWAP_HAS_CACHE. That's done in later part of code or
|
|
* else swap_off will be aborted if we return NULL.
|
|
*/
|
|
if (!__swp_swapcount(entry) && swap_slot_cache_enabled)
|
|
break;
|
|
|
|
/*
|
|
* Get a new page to read into from swap.
|
|
*/
|
|
if (!new_page) {
|
|
new_page = alloc_page_vma(gfp_mask, vma, addr);
|
|
if (!new_page)
|
|
break; /* Out of memory */
|
|
}
|
|
|
|
/*
|
|
* call radix_tree_preload() while we can wait.
|
|
*/
|
|
err = radix_tree_maybe_preload(gfp_mask & GFP_KERNEL);
|
|
if (err)
|
|
break;
|
|
|
|
/*
|
|
* Swap entry may have been freed since our caller observed it.
|
|
*/
|
|
err = swapcache_prepare(entry);
|
|
if (err == -EEXIST) {
|
|
radix_tree_preload_end();
|
|
/*
|
|
* We might race against get_swap_page() and stumble
|
|
* across a SWAP_HAS_CACHE swap_map entry whose page
|
|
* has not been brought into the swapcache yet.
|
|
*/
|
|
cond_resched();
|
|
continue;
|
|
}
|
|
if (err) { /* swp entry is obsolete ? */
|
|
radix_tree_preload_end();
|
|
break;
|
|
}
|
|
|
|
/* May fail (-ENOMEM) if radix-tree node allocation failed. */
|
|
__SetPageLocked(new_page);
|
|
__SetPageSwapBacked(new_page);
|
|
err = __add_to_swap_cache(new_page, entry);
|
|
if (likely(!err)) {
|
|
radix_tree_preload_end();
|
|
/*
|
|
* Initiate read into locked page and return.
|
|
*/
|
|
SetPageWorkingset(new_page);
|
|
lru_cache_add_anon(new_page);
|
|
*new_page_allocated = true;
|
|
return new_page;
|
|
}
|
|
radix_tree_preload_end();
|
|
__ClearPageLocked(new_page);
|
|
/*
|
|
* add_to_swap_cache() doesn't return -EEXIST, so we can safely
|
|
* clear SWAP_HAS_CACHE flag.
|
|
*/
|
|
put_swap_page(new_page, entry);
|
|
} while (err != -ENOMEM);
|
|
|
|
if (new_page)
|
|
put_page(new_page);
|
|
return found_page;
|
|
}
|
|
|
|
/*
|
|
* Locate a page of swap in physical memory, reserving swap cache space
|
|
* and reading the disk if it is not already cached.
|
|
* A failure return means that either the page allocation failed or that
|
|
* the swap entry is no longer in use.
|
|
*/
|
|
struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
|
|
struct vm_area_struct *vma, unsigned long addr, bool do_poll)
|
|
{
|
|
bool page_was_allocated;
|
|
struct page *retpage = __read_swap_cache_async(entry, gfp_mask,
|
|
vma, addr, &page_was_allocated);
|
|
|
|
if (page_was_allocated)
|
|
swap_readpage(retpage, do_poll);
|
|
|
|
return retpage;
|
|
}
|
|
|
|
static unsigned int __swapin_nr_pages(unsigned long prev_offset,
|
|
unsigned long offset,
|
|
int hits,
|
|
int max_pages,
|
|
int prev_win)
|
|
{
|
|
unsigned int pages, last_ra;
|
|
|
|
/*
|
|
* This heuristic has been found to work well on both sequential and
|
|
* random loads, swapping to hard disk or to SSD: please don't ask
|
|
* what the "+ 2" means, it just happens to work well, that's all.
|
|
*/
|
|
pages = hits + 2;
|
|
if (pages == 2) {
|
|
/*
|
|
* We can have no readahead hits to judge by: but must not get
|
|
* stuck here forever, so check for an adjacent offset instead
|
|
* (and don't even bother to check whether swap type is same).
|
|
*/
|
|
if (offset != prev_offset + 1 && offset != prev_offset - 1)
|
|
pages = 1;
|
|
} else {
|
|
unsigned int roundup = 4;
|
|
while (roundup < pages)
|
|
roundup <<= 1;
|
|
pages = roundup;
|
|
}
|
|
|
|
if (pages > max_pages)
|
|
pages = max_pages;
|
|
|
|
/* Don't shrink readahead too fast */
|
|
last_ra = prev_win / 2;
|
|
if (pages < last_ra)
|
|
pages = last_ra;
|
|
|
|
return pages;
|
|
}
|
|
|
|
static unsigned long swapin_nr_pages(unsigned long offset)
|
|
{
|
|
static unsigned long prev_offset;
|
|
unsigned int hits, pages, max_pages;
|
|
static atomic_t last_readahead_pages;
|
|
|
|
max_pages = 1 << READ_ONCE(page_cluster);
|
|
if (max_pages <= 1)
|
|
return 1;
|
|
|
|
hits = atomic_xchg(&swapin_readahead_hits, 0);
|
|
pages = __swapin_nr_pages(prev_offset, offset, hits, max_pages,
|
|
atomic_read(&last_readahead_pages));
|
|
if (!hits)
|
|
prev_offset = offset;
|
|
atomic_set(&last_readahead_pages, pages);
|
|
|
|
return pages;
|
|
}
|
|
|
|
/**
|
|
* swap_cluster_readahead - swap in pages in hope we need them soon
|
|
* @entry: swap entry of this memory
|
|
* @gfp_mask: memory allocation flags
|
|
* @vmf: fault information
|
|
*
|
|
* Returns the struct page for entry and addr, after queueing swapin.
|
|
*
|
|
* Primitive swap readahead code. We simply read an aligned block of
|
|
* (1 << page_cluster) entries in the swap area. This method is chosen
|
|
* because it doesn't cost us any seek time. We also make sure to queue
|
|
* the 'original' request together with the readahead ones...
|
|
*
|
|
* This has been extended to use the NUMA policies from the mm triggering
|
|
* the readahead.
|
|
*
|
|
* Caller must hold down_read on the vma->vm_mm if vmf->vma is not NULL.
|
|
* This is needed to ensure the VMA will not be freed in our back. In the case
|
|
* of the speculative page fault handler, this cannot happen, even if we don't
|
|
* hold the mmap_sem. Callees are assumed to take care of reading VMA's fields
|
|
* using READ_ONCE() to read consistent values.
|
|
*/
|
|
struct page *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask,
|
|
struct vm_fault *vmf)
|
|
{
|
|
struct page *page;
|
|
unsigned long entry_offset = swp_offset(entry);
|
|
unsigned long offset = entry_offset;
|
|
unsigned long start_offset, end_offset;
|
|
unsigned long mask;
|
|
struct blk_plug plug;
|
|
bool do_poll = true, page_allocated;
|
|
struct vm_area_struct *vma = vmf->vma;
|
|
unsigned long addr = vmf->address;
|
|
|
|
mask = swapin_nr_pages(offset) - 1;
|
|
if (!mask)
|
|
goto skip;
|
|
|
|
do_poll = false;
|
|
/* Read a page_cluster sized and aligned cluster around offset. */
|
|
start_offset = offset & ~mask;
|
|
end_offset = offset | mask;
|
|
if (!start_offset) /* First page is swap header. */
|
|
start_offset++;
|
|
|
|
blk_start_plug(&plug);
|
|
for (offset = start_offset; offset <= end_offset ; offset++) {
|
|
/* Ok, do the async read-ahead now */
|
|
page = __read_swap_cache_async(
|
|
swp_entry(swp_type(entry), offset),
|
|
gfp_mask, vma, addr, &page_allocated);
|
|
if (!page)
|
|
continue;
|
|
if (page_allocated) {
|
|
swap_readpage(page, false);
|
|
if (offset != entry_offset &&
|
|
likely(!PageTransCompound(page))) {
|
|
SetPageReadahead(page);
|
|
count_vm_event(SWAP_RA);
|
|
}
|
|
}
|
|
put_page(page);
|
|
}
|
|
blk_finish_plug(&plug);
|
|
|
|
lru_add_drain(); /* Push any new pages onto the LRU now */
|
|
skip:
|
|
return read_swap_cache_async(entry, gfp_mask, vma, addr, do_poll);
|
|
}
|
|
|
|
int init_swap_address_space(unsigned int type, unsigned long nr_pages)
|
|
{
|
|
struct address_space *spaces, *space;
|
|
unsigned int i, nr;
|
|
|
|
nr = DIV_ROUND_UP(nr_pages, SWAP_ADDRESS_SPACE_PAGES);
|
|
spaces = kvzalloc(sizeof(struct address_space) * nr, GFP_KERNEL);
|
|
if (!spaces)
|
|
return -ENOMEM;
|
|
for (i = 0; i < nr; i++) {
|
|
space = spaces + i;
|
|
INIT_RADIX_TREE(&space->page_tree, GFP_ATOMIC|__GFP_NOWARN);
|
|
atomic_set(&space->i_mmap_writable, 0);
|
|
space->a_ops = &swap_aops;
|
|
/* swap cache doesn't use writeback related tags */
|
|
mapping_set_no_writeback_tags(space);
|
|
spin_lock_init(&space->tree_lock);
|
|
}
|
|
nr_swapper_spaces[type] = nr;
|
|
rcu_assign_pointer(swapper_spaces[type], spaces);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void exit_swap_address_space(unsigned int type)
|
|
{
|
|
struct address_space *spaces;
|
|
|
|
spaces = swapper_spaces[type];
|
|
nr_swapper_spaces[type] = 0;
|
|
rcu_assign_pointer(swapper_spaces[type], NULL);
|
|
synchronize_rcu();
|
|
kvfree(spaces);
|
|
}
|
|
|
|
static inline void swap_ra_clamp_pfn(struct vm_area_struct *vma,
|
|
unsigned long faddr,
|
|
unsigned long lpfn,
|
|
unsigned long rpfn,
|
|
unsigned long *start,
|
|
unsigned long *end)
|
|
{
|
|
*start = max3(lpfn, PFN_DOWN(READ_ONCE(vma->vm_start)),
|
|
PFN_DOWN(faddr & PMD_MASK));
|
|
*end = min3(rpfn, PFN_DOWN(READ_ONCE(vma->vm_end)),
|
|
PFN_DOWN((faddr & PMD_MASK) + PMD_SIZE));
|
|
}
|
|
|
|
static void swap_ra_info(struct vm_fault *vmf,
|
|
struct vma_swap_readahead *ra_info)
|
|
{
|
|
struct vm_area_struct *vma = vmf->vma;
|
|
unsigned long ra_val;
|
|
swp_entry_t entry;
|
|
unsigned long faddr, pfn, fpfn;
|
|
unsigned long start, end;
|
|
pte_t *pte, *orig_pte;
|
|
unsigned int max_win, hits, prev_win, win, left;
|
|
#ifndef CONFIG_64BIT
|
|
pte_t *tpte;
|
|
#endif
|
|
|
|
max_win = 1 << min_t(unsigned int, READ_ONCE(page_cluster),
|
|
SWAP_RA_ORDER_CEILING);
|
|
if (max_win == 1) {
|
|
ra_info->win = 1;
|
|
return;
|
|
}
|
|
|
|
faddr = vmf->address;
|
|
orig_pte = pte = pte_offset_map(vmf->pmd, faddr);
|
|
entry = pte_to_swp_entry(*pte);
|
|
if ((unlikely(non_swap_entry(entry)))) {
|
|
pte_unmap(orig_pte);
|
|
return;
|
|
}
|
|
|
|
fpfn = PFN_DOWN(faddr);
|
|
ra_val = GET_SWAP_RA_VAL(vma);
|
|
pfn = PFN_DOWN(SWAP_RA_ADDR(ra_val));
|
|
prev_win = SWAP_RA_WIN(ra_val);
|
|
hits = SWAP_RA_HITS(ra_val);
|
|
ra_info->win = win = __swapin_nr_pages(pfn, fpfn, hits,
|
|
max_win, prev_win);
|
|
atomic_long_set(&vma->swap_readahead_info,
|
|
SWAP_RA_VAL(faddr, win, 0));
|
|
|
|
if (win == 1) {
|
|
pte_unmap(orig_pte);
|
|
return;
|
|
}
|
|
|
|
/* Copy the PTEs because the page table may be unmapped */
|
|
if (fpfn == pfn + 1)
|
|
swap_ra_clamp_pfn(vma, faddr, fpfn, fpfn + win, &start, &end);
|
|
else if (pfn == fpfn + 1)
|
|
swap_ra_clamp_pfn(vma, faddr, fpfn - win + 1, fpfn + 1,
|
|
&start, &end);
|
|
else {
|
|
left = (win - 1) / 2;
|
|
swap_ra_clamp_pfn(vma, faddr, fpfn - left, fpfn + win - left,
|
|
&start, &end);
|
|
}
|
|
ra_info->nr_pte = end - start;
|
|
ra_info->offset = fpfn - start;
|
|
pte -= ra_info->offset;
|
|
#ifdef CONFIG_64BIT
|
|
ra_info->ptes = pte;
|
|
#else
|
|
tpte = ra_info->ptes;
|
|
for (pfn = start; pfn != end; pfn++)
|
|
*tpte++ = *pte++;
|
|
#endif
|
|
pte_unmap(orig_pte);
|
|
}
|
|
|
|
struct page *swap_vma_readahead(swp_entry_t fentry, gfp_t gfp_mask,
|
|
struct vm_fault *vmf)
|
|
{
|
|
struct blk_plug plug;
|
|
struct vm_area_struct *vma = vmf->vma;
|
|
struct page *page;
|
|
pte_t *pte, pentry;
|
|
swp_entry_t entry;
|
|
unsigned int i;
|
|
bool page_allocated;
|
|
struct vma_swap_readahead ra_info = {0,};
|
|
|
|
swap_ra_info(vmf, &ra_info);
|
|
if (ra_info.win == 1)
|
|
goto skip;
|
|
|
|
blk_start_plug(&plug);
|
|
for (i = 0, pte = ra_info.ptes; i < ra_info.nr_pte;
|
|
i++, pte++) {
|
|
pentry = *pte;
|
|
if (pte_none(pentry))
|
|
continue;
|
|
if (pte_present(pentry))
|
|
continue;
|
|
entry = pte_to_swp_entry(pentry);
|
|
if (unlikely(non_swap_entry(entry)))
|
|
continue;
|
|
page = __read_swap_cache_async(entry, gfp_mask, vma,
|
|
vmf->address, &page_allocated);
|
|
if (!page)
|
|
continue;
|
|
if (page_allocated) {
|
|
swap_readpage(page, false);
|
|
if (i != ra_info.offset &&
|
|
likely(!PageTransCompound(page))) {
|
|
SetPageReadahead(page);
|
|
count_vm_event(SWAP_RA);
|
|
}
|
|
}
|
|
put_page(page);
|
|
}
|
|
blk_finish_plug(&plug);
|
|
lru_add_drain();
|
|
skip:
|
|
return read_swap_cache_async(fentry, gfp_mask, vma, vmf->address,
|
|
ra_info.win == 1);
|
|
}
|
|
|
|
/**
|
|
* swapin_readahead - swap in pages in hope we need them soon
|
|
* @entry: swap entry of this memory
|
|
* @gfp_mask: memory allocation flags
|
|
* @vmf: fault information
|
|
*
|
|
* Returns the struct page for entry and addr, after queueing swapin.
|
|
*
|
|
* It's a main entry function for swap readahead. By the configuration,
|
|
* it will read ahead blocks by cluster-based(ie, physical disk based)
|
|
* or vma-based(ie, virtual address based on faulty address) readahead.
|
|
*/
|
|
struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
|
|
struct vm_fault *vmf)
|
|
{
|
|
return swap_use_vma_readahead() ?
|
|
swap_vma_readahead(entry, gfp_mask, vmf) :
|
|
swap_cluster_readahead(entry, gfp_mask, vmf);
|
|
}
|
|
|
|
#ifdef CONFIG_SYSFS
|
|
static ssize_t vma_ra_enabled_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%s\n", enable_vma_readahead ? "true" : "false");
|
|
}
|
|
static ssize_t vma_ra_enabled_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
if (!strncmp(buf, "true", 4) || !strncmp(buf, "1", 1))
|
|
enable_vma_readahead = true;
|
|
else if (!strncmp(buf, "false", 5) || !strncmp(buf, "0", 1))
|
|
enable_vma_readahead = false;
|
|
else
|
|
return -EINVAL;
|
|
|
|
return count;
|
|
}
|
|
static struct kobj_attribute vma_ra_enabled_attr =
|
|
__ATTR(vma_ra_enabled, 0644, vma_ra_enabled_show,
|
|
vma_ra_enabled_store);
|
|
|
|
static struct attribute *swap_attrs[] = {
|
|
&vma_ra_enabled_attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group swap_attr_group = {
|
|
.attrs = swap_attrs,
|
|
};
|
|
|
|
static int __init swap_init_sysfs(void)
|
|
{
|
|
int err;
|
|
struct kobject *swap_kobj;
|
|
|
|
swap_kobj = kobject_create_and_add("swap", mm_kobj);
|
|
if (!swap_kobj) {
|
|
pr_err("failed to create swap kobject\n");
|
|
return -ENOMEM;
|
|
}
|
|
err = sysfs_create_group(swap_kobj, &swap_attr_group);
|
|
if (err) {
|
|
pr_err("failed to register swap group\n");
|
|
goto delete_obj;
|
|
}
|
|
return 0;
|
|
|
|
delete_obj:
|
|
kobject_put(swap_kobj);
|
|
return err;
|
|
}
|
|
subsys_initcall(swap_init_sysfs);
|
|
#endif
|