This brings the android14-6.1 branch up to date with the 6.1.78 LTS release. Included in here are the following commits: *4b97573495
Revert "hrtimer: Report offline hrtimer enqueue" *0e5af42a0a
Merge 6.1.78 into android14-6.1-lts |\ | *8b4118fabd
Linux 6.1.78 | *1296c110c5
netfilter: nft_set_rbtree: skip end interval element from gc | *d89a80e482
net: stmmac: xgmac: fix a typo of register name in DPP safety handling | *7b430fb924
ALSA: usb-audio: Sort quirk table entries | *06040fadbf
net: stmmac: xgmac: use #define for string constants | *499e6e9f07
clocksource: Skip watchdog check for large watchdog intervals | *492e0aba08
block: treat poll queue enter similarly to timeouts | *cf3d57ad6f
f2fs: add helper to check compression level | *9f74b3d718
RDMA/irdma: Fix support for 64k pages | *4675661672
vhost: use kzalloc() instead of kmalloc() followed by memset() | *d8712c6c6a
Revert "ASoC: amd: Add new dmi entries for acp5x platform" | *fbd77ce1d1
io_uring/net: fix sr->len for IORING_OP_RECV with MSG_WAITALL and buffers | *08249dc3d9
Input: atkbd - skip ATKBD_CMD_SETLEDS when skipping ATKBD_CMD_GETID | *315075ac73
Input: i8042 - fix strange behavior of touchpad on Clevo NS70PU | *a94d303bea
hrtimer: Report offline hrtimer enqueue | *8b380ad970
usb: host: xhci-plat: Add support for XHCI_SG_TRB_CACHE_SIZE_QUIRK | *f2cf6db285
usb: dwc3: host: Set XHCI_SG_TRB_CACHE_SIZE_QUIRK | *041cb58f54
USB: serial: cp210x: add ID for IMST iM871A-USB | *36ef5b7b4f
USB: serial: option: add Fibocom FM101-GL variant | *234099ab7f
USB: serial: qcserial: add new usb-id for Dell Wireless DW5826e | *39fbca505f
ALSA: usb-audio: add quirk for RODE NT-USB+ | *2552f6b1bd
ALSA: usb-audio: Add a quirk for Yamaha YIT-W12TX transmitter | *b8259a5023
ALSA: usb-audio: Add delay quirk for MOTU M Series 2nd revision | *82761993d4
mtd: parsers: ofpart: add workaround for #size-cells 0 | *b478e414cf
fs: dlm: don't put dlm_local_addrs on heap | *e5dc63f01e
blk-iocost: Fix an UBSAN shift-out-of-bounds warning | *1ebd75cefa
scsi: core: Move scsi_host_busy() out of host lock if it is for per-command | *ec1bedd797
fs/ntfs3: Fix an NULL dereference bug | *a442ff5405
netfilter: nft_set_pipapo: remove scratch_aligned pointer | *fac3478d5b
netfilter: nft_set_pipapo: add helper to release pcpu scratch area | *3eaab7d565
netfilter: nft_set_pipapo: store index in scratch maps | *181dade251
netfilter: nft_ct: reject direction for ct id | *efdd665ce1
drm/amd/display: Implement bounds check for stream encoder creation in DCN301 | *a060da3235
netfilter: nft_compat: restrict match/target protocol to u16 | *8762bcc927
netfilter: nft_compat: reject unused compat flag | *e79ef7966e
netfilter: nft_compat: narrow down revision to unsigned 8-bits | *69d66d493b
selftests: cmsg_ipv6: repeat the exact packet | *4e2c4846b2
ppp_async: limit MRU to 64K | *e0e09186d8
af_unix: Call kfree_skb() for dead unix_(sk)->oob_skb in GC. | *3d3a5b31b4
tipc: Check the bearer type before calling tipc_udp_nl_bearer_add() | *cf6b97e183
rxrpc: Fix response to PING RESPONSE ACKs to a dead call | *05a4d0e166
drm/i915/gvt: Fix uninitialized variable in handle_mmio() | *5453875221
inet: read sk->sk_family once in inet_recv_error() | *3fa78ee0e3
hwmon: (coretemp) Fix bogus core_id to attr name mapping | *9bce694192
hwmon: (coretemp) Fix out-of-bounds memory access | *a3156be201
hwmon: (aspeed-pwm-tacho) mutex for tach reading | *4065746686
octeontx2-pf: Fix a memleak otx2_sq_init | *cbf2e16602
atm: idt77252: fix a memleak in open_card_ubr0 | *e37cde7a57
tunnels: fix out of bounds access when building IPv6 PMTU error | *90fe47743a
selftests: net: avoid just another constant wait | *7f484179c5
selftests: net: cut more slack for gro fwd tests. | *466ceebe48
net: atlantic: Fix DMA mapping for PTP hwts ring | *0193e0660c
netdevsim: avoid potential loop in nsim_dev_trap_report_work() | *bcabbf8ab5
wifi: mac80211: fix waiting for beacons logic | *e42ff0844f
net: stmmac: xgmac: fix handling of DPP safety error for DMA channels | *fb8bfc6ea3
drm/msm/dpu: check for valid hw_pp in dpu_encoder_helper_phys_cleanup | *42939a1ea6
drm/msm/dp: return correct Colorimetry for DP_TEST_DYNAMIC_RANGE_CEA case | *d2b7e247f3
drm/msms/dp: fixed link clock divider bits be over written in BPC unknown case | *cbc53148cc
cifs: failure to add channel on iface should bump up weight | *0430bfcd46
phy: ti: phy-omap-usb2: Fix NULL pointer dereference for SRP | *296fb308f4
dmaengine: fix is_slave_direction() return false when DMA_DEV_TO_DEV | *ed3bb52a05
phy: renesas: rcar-gen3-usb2: Fix returning wrong error code | *25ab4d72eb
dmaengine: fsl-qdma: Fix a memory leak related to the queue command DMA | *13535ae766
dmaengine: fsl-qdma: Fix a memory leak related to the status queue DMA | *908939b8e8
dmaengine: ti: k3-udma: Report short packet errors | *a1d7ca71ba
dmaengine: fsl-dpaa2-qdma: Fix the size of dma pools | *78327acd4c
ext4: regenerate buddy after block freeing failed if under fc replay * |19c319d14d
Revert "drm/mipi-dsi: Fix detach call without attach" * |20b90d46a0
Merge 6.1.77 into android14-6.1-lts |\| | *f1bb70486c
Linux 6.1.77 | *d78690bb5d
drm/amdgpu: Fix missing error code in 'gmc_v6/7/8/9_0_hw_init()' | *f086c50a98
ASoC: codecs: wsa883x: fix PA volume control | *ac86261fa8
ASoC: codecs: lpass-wsa-macro: fix compander volume hack | *2386ee6cba
bonding: remove print in bond_verify_device_path | *e1edd8e6c0
gve: Fix use-after-free vulnerability | *9e584ea101
LoongArch/smp: Call rcutree_report_cpu_starting() at tlb_init() | *98c392a91a
drm/msm/dsi: Enable runtime PM | *befdb0a8a1
Revert "drm/amd/display: Disable PSR-SU on Parade 0803 TCON again" | *dc904345e3
mm, kmsan: fix infinite recursion due to RCU critical section | *c5a12dfbfa
arm64: irq: set the correct node for shadow call stack | *ddd367ebc4
selftests: bonding: Check initial state | *7ebe20e632
selftests: team: Add missing config options | *aaa8f76845
net: sysfs: Fix /sys/class/net/<iface> path | *bea0fbf857
selftests: net: fix available tunnels detection | *a2104f4387
af_unix: fix lockdep positive in sk_diag_dump_icons() | *fde3d47efe
net: ipv4: fix a memleak in ip_setup_cork | *0f501dae16
netfilter: nft_ct: sanitize layer 3 and 4 protocol number in custom expectations | *9ff981cd65
netfilter: nf_log: replace BUG_ON by WARN_ON_ONCE when putting logger | *67f0ca0a4c
netfilter: nf_tables: restrict tunnel object to NFPROTO_NETDEV | *8a51dbf7b7
netfilter: conntrack: correct window scaling with retransmitted SYN | *cd091ca44c
selftests: net: add missing config for GENEVE | *04a553d8ac
bridge: mcast: fix disabled snooping after long uptime | *9c333d9891
llc: call sock_orphan() at release time | *c59ed6592f
ipv6: Ensure natural alignment of const ipv6 loopback and router addresses | *2f3d9829f7
net: dsa: qca8k: fix illegal usage of GPIO | *1e4c227805
ixgbe: Fix an error handling path in ixgbe_read_iosf_sb_reg_x550() | *3b84b7000c
ixgbe: Refactor overtemp event handling | *9c8eafc5e9
ixgbe: Refactor returning internal error codes | *b383d4ea27
tcp: add sanity checks to rx zerocopy | *046260ce7c
net: lan966x: Fix port configuration when using SGMII interface | *d2f1b7fe74
ipmr: fix kernel panic when forwarding mcast packets | *03dc5b73af
ipv4: raw: add drop reasons | *d54e4da98b
ip6_tunnel: make sure to pull inner header in __ip6_tnl_rcv() | *262caadfa9
selftests: net: give more time for GRO aggregation | *53e94ec530
HID: hidraw: fix a problem of memory leak in hidraw_release() | *db6338f459
scsi: core: Move scsi_host_busy() out of host lock for waking up EH handler | *81e92f0c97
regulator: ti-abb: don't use devm_platform_ioremap_resource_byname for shared interrupt register | *7eb86ddaf1
scsi: isci: Fix an error code problem in isci_io_request_build() | *206dcd2624
drm: using mul_u32_u32() requires linux/math64.h | *a2f30104fe
wifi: cfg80211: fix RCU dereference in __cfg80211_bss_update | *071d98d5ee
perf: Fix the nr_addr_filters fix | *8eea7e1d69
drm/amdkfd: Fix 'node' NULL check in 'svm_range_get_range_boundaries()' | *7513f0906c
drm/amdgpu: Release 'adev->pm.fw' before return in 'amdgpu_device_need_post()' | *af8e292615
drm/amdgpu: Fix with right return code '-EIO' in 'amdgpu_gmc_vram_checking()' | *d282ea0703
drm/amd/powerplay: Fix kzalloc parameter 'ATOM_Tonga_PPM_Table' in 'get_platform_power_management_table()' | *3fbfeb8536
ceph: fix invalid pointer access if get_quota_realm return ERR_PTR | *7f2649c942
ceph: fix deadlock or deadcode of misusing dget() | *692ead237d
ceph: reinitialize mds feature bit even when session in open | *1d9c777d3e
blk-mq: fix IO hang from sbitmap wakeup race | *1f7a018857
virtio_net: Fix "‘%d’ directive writing between 1 and 11 bytes into a region of size 10" warnings | *b602f098f7
drm/amdkfd: Fix lock dependency warning with srcu | *8b25d39716
drm/amdkfd: Fix lock dependency warning | *49a7b708da
libsubcmd: Fix memory leak in uniq() | *2c1164ad92
misc: lis3lv02d_i2c: Add missing setting of the reg_ctrl callback | *91f1977487
9p: Fix initialisation of netfs_inode for 9p | *fc557b76dc
PCI/AER: Decode Requester ID when no error info found | *83c895561a
PCI: Fix 64GT/s effective data rate calculation | *521f28eedd
spmi: mediatek: Fix UAF on device remove | *089ebfab24
fs/kernfs/dir: obey S_ISGID | *c13bcbdb84
tty: allow TIOCSLCKTRMIOS with CAP_CHECKPOINT_RESTORE | *d8d7ffefc0
selftests/sgx: Fix linker script asserts | *fa3f6cd20d
usb: hub: Add quirk to decrease IN-ep poll interval for Microchip USB491x hub | *9cdf5ddb06
usb: hub: Replace hardcoded quirk value with BIT() macro | *4c8ca96124
perf cs-etm: Bump minimum OpenCSD version to ensure a bugfix is present | *1d83c85922
PCI: switchtec: Fix stdev_release() crash after surprise hot remove | *5e0160dab1
PCI: Only override AMD USB controller if required | *26b8a35fef
mailbox: arm_mhuv2: Fix a bug for mhuv2_sender_interrupt | *6e8c0eda6c
mfd: ti_am335x_tscadc: Fix TI SoC dependencies | *52e7f05549
xen/gntdev: Fix the abuse of underlying struct page in DMA-buf import | *e827364bc1
i3c: master: cdns: Update maximum prescaler value for i2c clock | *4f7dad73df
um: time-travel: fix time corruption | *d8512cc8ac
um: net: Fix return type of uml_net_start_xmit() | *d8264ce2f8
um: Don't use vfprintf() for os_info() | *a95e52af36
um: Fix naming clash between UML and scheduler | *7d1c4e5809
leds: trigger: panic: Don't register panic notifier if creating the trigger failed | *2cb659ef0a
ALSA: hda/conexant: Fix headset auto detect fail in cx8070 and SN6140 | *05a0900bd7
drm/amdkfd: Fix iterator used outside loop in 'kfd_add_peer_prop()' | *34bb1b97c3
drm/amdgpu: Drop 'fence' check in 'to_amdgpu_amdkfd_fence()' | *66d38977e2
drm/amdgpu: Fix '*fw' from request_firmware() not released in 'amdgpu_ucode_request()' | *da08dbb647
drm/amdgpu: Let KFD sync with VM fences | *adae24c5b3
drm/amdgpu: Fix ecc irq enable/disable unpaired | *aade0a0760
clk: imx: clk-imx8qxp: fix LVDS bypass, pixel and phy clocks | *7294b1bbaa
drm/amd/display: make flip_timestamp_in_us a 64-bit variable | *c95d2144be
watchdog: it87_wdt: Keep WDTCTRL bit 3 unmodified for IT8784/IT8786 | *ec74a45e80
clk: mmp: pxa168: Fix memory leak in pxa168_clk_init() | *14992bc77f
clk: hi3620: Fix memory leak in hi3620_mmc_clk_init() | *d443fb67ca
drm/amdgpu: fix ftrace event amdgpu_bo_move always move on same heap | *fe7e8ec072
drm/msm/dpu: fix writeback programming for YUV cases | *7593e62702
drm/msm/dpu: Ratelimit framedone timeout msgs | *fb017c3e6a
drm/amd/display: For prefetch mode > 0, extend prefetch if possible | *4ef53b7e30
media: i2c: imx335: Fix hblank min/max values | *5008bde32c
media: ddbridge: fix an error code problem in ddb_probe | *6408851d05
media: amphion: remove mutext lock in condition of wait_event | *5108a2dc2d
IB/ipoib: Fix mcast list locking | *fe80290b2a
drm/exynos: Call drm_atomic_helper_shutdown() at shutdown/unbind time | *7c972c8945
f2fs: fix to tag gcing flag on page during block migration | *fb55c3cee6
hwmon: (nct6775) Fix fan speed set failure in automatic mode | *bf808f5868
media: rkisp1: Fix IRQ disable race issue | *f0d0fe3787
media: rkisp1: Store IRQ lines | *fb71b54856
media: rkisp1: Fix IRQ handler return values | *1c51b6b0c6
media: rkisp1: Drop IRQF_SHARED | *c3f77c5d63
ALSA: hda: Intel: add HDA_ARL PCI ID support | *3b28da57d0
PCI: add INTEL_HDA_ARL to pci_ids.h | *16786b7090
media: rockchip: rga: fix swizzling for RGB formats | *1ef8beb4bf
media: stk1160: Fixed high volume of stk1160_dbg messages | *25eaa9f999
drm/mipi-dsi: Fix detach call without attach | *f3e41cc260
drm/framebuffer: Fix use of uninitialized variable | *406f8d5bad
drm/drm_file: fix use of uninitialized variable | *48ad42cd95
ASoC: amd: Add new dmi entries for acp5x platform | *b6ca70f06e
f2fs: fix write pointers on zoned device after roll forward | *9773a96eac
drm/amd/display: Fix tiled display misalignment | *126543736f
drm/bridge: anx7625: Fix Set HPD irq detect window to 2ms | *3c2bd20dc9
drm/panel-edp: Add override_edid_mode quirk for generic edp | *055c849724
RDMA/IPoIB: Fix error code return in ipoib_mcast_join | *c4cb42824e
fast_dput(): handle underflows gracefully | *0ee8e0a183
ASoC: doc: Fix undefined SND_SOC_DAPM_NOPM argument | *52e25a323c
ALSA: hda: Refer to correct stream index at loops | *b1020a5467
f2fs: fix to check return value of f2fs_reserve_new_block() | *332a7c108e
net: dsa: qca8k: put MDIO bus OF node on qca8k_mdio_register() failure | *0438a985de
octeontx2-af: Fix max NPC MCAM entry check while validating ref_entry | *95173204b1
i40e: Fix VF disable behavior to block all traffic | *9f9ac39adb
bridge: cfm: fix enum typo in br_cc_ccm_tx_parse | *a243e0818e
net/smc: disable SEID on non-s390 archs where virtual ISM may be used | *388736c62b
Bluetooth: L2CAP: Fix possible multiple reject send | *6d95ade9e6
Bluetooth: hci_sync: fix BR/EDR wakeup bug | *a836b1c333
Bluetooth: qca: Set both WIDEBAND_SPEECH and LE_STATES quirks for QCA2066 | *da1a6e9f01
wifi: cfg80211: free beacon_ies when overridden from hidden BSS | *3bb09b9af1
wifi: rtlwifi: rtl8723{be,ae}: using calculate_bit_shift() | *12473265f5
libbpf: Fix NULL pointer dereference in bpf_object__collect_prog_relos | *68ef19417a
wifi: rtl8xxxu: Add additional USB IDs for RTL8192EU devices | *e15fa0c67e
arm64: dts: amlogic: fix format for s4 uart node | *9e8338b72b
ice: fix pre-shifted bit usage | *9c5541f3f0
arm64: dts: qcom: msm8998: Fix 'out-ports' is a required property | *2fdbf9d9a0
arm64: dts: qcom: msm8996: Fix 'in-ports' is a required property | *fd9a2c7003
md: Whenassemble the array, consult the superblock of the freshest device | *8ae4201900
block: prevent an integer overflow in bvec_try_merge_hw_page | *44f6b75c09
net: dsa: mv88e6xxx: Fix mv88e6352_serdes_get_stats error path | *0edb3ae8bf
net: atlantic: eliminate double free in error handling logic | *ea12794ea6
ice: fix ICE_AQ_VSI_Q_OPT_RSS_* register values | *d4560c11c3
scsi: hisi_sas: Set .phy_attached before notifing phyup event HISI_PHYE_PHY_UP_PM | *dc15b313f3
ARM: dts: imx23/28: Fix the DMA controller node name | *9388665a12
ARM: dts: imx23-sansa: Use preferred i2c-gpios properties | *fb8e41af95
ARM: dts: imx27-apf27dev: Fix LED name | *6ebe86575b
ARM: dts: imx25/27: Pass timing0 | *68b2e26225
ARM: dts: imx25: Fix the iim compatible string | *af7bbdac89
block/rnbd-srv: Check for unlikely string overflow | *238ec612a2
ionic: bypass firmware cmds when stuck in reset | *434fcaf372
ionic: pass opcode to devcmd_wait | *7dc0fefd37
net: phy: at803x: fix passing the wrong reference for config_intr | *e7398f3e45
ARM: dts: imx1: Fix sram node | *7721a55c02
ARM: dts: imx27: Fix sram node | *8a0285ed7e
ARM: dts: imx: Use flash@0,0 pattern | *8953b37bb1
ARM: dts: imx25/27-eukrea: Fix RTC node name | *1acdaf9f29
ARM: dts: rockchip: fix rk3036 hdmi ports node | *574dcd3126
wifi: wfx: fix possible NULL pointer dereference in wfx_set_mfp_ap() | *5a44a664ab
bpf: Set uattr->batch.count as zero before batched update or deletion | *7719e56b20
scsi: libfc: Fix up timeout error in fc_fcp_rec_error() | *73fe92ddf9
scsi: libfc: Don't schedule abort twice | *d6d6fe4bb1
bpf: Check rcu_read_lock_trace_held() before calling bpf map helpers | *c07965d1a7
wifi: ath11k: fix race due to setting ATH11K_FLAG_EXT_IRQ_ENABLED too early | *25c6f49ef5
wifi: ath9k: Fix potential array-index-out-of-bounds read in ath9k_htc_txstatus() | *db30f469ae
ARM: dts: imx7s: Fix nand-controller #size-cells | *6fa750d62f
ARM: dts: imx7s: Fix lcdif compatible | *c9c2a35820
ARM: dts: imx7d: Fix coresight funnel ports | *89fdf0a2c7
scsi: arcmsr: Support new PCI device IDs 1883 and 1886 | *61c859bd66
scsi: mpi3mr: Add PCI checks where SAS5116 diverges from SAS4116 | *5c4cbec510
net: usb: ax88179_178a: avoid two consecutive device resets | *cd4cdad9bd
bonding: return -ENOMEM instead of BUG in alb_upper_dev_walk | *c0d5a69322
PCI: Add no PM reset quirk for NVIDIA Spectrum devices | *04dcef4a78
scsi: lpfc: Fix possible file string name overflow when updating firmware | *c0a96adce2
soc: xilinx: fix unhandled SGI warning message | *01946c3c83
soc: xilinx: Fix for call trace due to the usage of smp_processor_id() | *fd937767d5
selftests/bpf: Fix issues in setup_classid_environment() | *f58cfb63e4
wifi: rt2x00: correct wrong BBP register in RxDCOC calibration | *ad0e7bbc0b
selftests/bpf: Fix pyperf180 compilation failure with clang18 | *76ab331d6d
selftests/bpf: satisfy compiler by having explicit return in btf test | *739b3ccd94
wifi: rt2x00: restart beacon queue when hardware reset | *6d2cbf517d
ext4: avoid online resizing failures due to oversized flex bg | *dd10f82ece
ext4: remove unnecessary check from alloc_flex_gd() | *60292a12a0
ext4: unify the type of flexbg_size to unsigned int | *069ede0475
ext4: fix inconsistent between segment fstrim and full fstrim | *80cab9dad5
ecryptfs: Reject casefold directory inodes | *e8ca3e7330
SUNRPC: Fix a suspicious RCU usage warning | *0671f42a9c
KVM: s390: fix setting of fpc register | *7a4d6481fb
s390/ptrace: handle setting of fpc register correctly | *08f65c9067
s390/vfio-ap: fix sysfs status attribute for AP queue devices | *d6c8d8ab76
arch: consolidate arch_irq_work_raise prototypes | *3537f92cd2
jfs: fix array-index-out-of-bounds in diNewExt | *b03d76cc66
rxrpc_find_service_conn_rcu: fix the usage of read_seqbegin_or_lock() | *ea4eb77c53
afs: fix the usage of read_seqbegin_or_lock() in afs_find_server*() | *eef7c4cd98
afs: fix the usage of read_seqbegin_or_lock() in afs_lookup_volume_rcu() | *91256fcd57
crypto: stm32/crc32 - fix parsing list of devices | *e0e78522b4
erofs: fix ztailpacking for subpage compressed blocks | *6c7bdb97d4
crypto: octeontx2 - Fix cptvf driver cleanup | *75b0f71b26
pstore/ram: Fix crash when setting number of cpus to an odd number | *32e8f2d955
jfs: fix uaf in jfs_evict_inode | *70780914cb
jfs: fix array-index-out-of-bounds in dbAdjTree | *cab0c265ba
jfs: fix slab-out-of-bounds Read in dtSearch | *e4cbc857d7
UBSAN: array-index-out-of-bounds in dtSplitRoot | *42f433785f
FS:JFS:UBSAN:array-index-out-of-bounds in dbAdjTree | *185d97e5be
ACPI: APEI: set memory failure flags as MF_ACTION_REQUIRED on synchronous events | *31569995fc
PM / devfreq: Synchronize devfreq_monitor_[start/stop] | *7633b7a036
ACPI: NUMA: Fix the logic of getting the fake_pxm value | *33650372e3
ACPI: extlog: fix NULL pointer dereference check | *431c1a4921
PNP: ACPI: fix fortify warning | *81eb8b56e7
ACPI: video: Add quirk for the Colorful X15 AT 23 Laptop | *4d4e06eaa2
audit: Send netlink ACK before setting connection in auditd_set | *3430936a01
regulator: core: Only increment use_count when enable_count changes | *b3ae38966d
debugobjects: Stop accessing objects after releasing hash bucket lock | *74ec093dba
perf/core: Fix narrow startup race when creating the perf nr_addr_filters sysfs file | *d67e43be0e
x86/mce: Mark fatal MCE's page as poison to avoid panic in the kdump kernel | *abd26515d4
powerpc/lib: Validate size for vector operations | *0be5614f26
powerpc: pmd_move_must_withdraw() is only needed for CONFIG_TRANSPARENT_HUGEPAGE | *d4908b3431
x86/boot: Ignore NMIs during very early boot | *a6fd14db75
powerpc/64s: Fix CONFIG_NUMA=n build due to create_section_mapping() | *cf3256c431
powerpc/mm: Fix build failures due to arch_reserved_kernel_pages() | *7ad4b2a6b2
powerpc: Fix build error due to is_valid_bugaddr() | *d87d9a23a1
drivers/perf: pmuv3: don't expose SW_INCR event in sysfs | *4431284f4a
arm64: irq: set the correct node for VMAP stack | *d482d61025
powerpc/mm: Fix null-pointer dereference in pgtable_cache_add | *9e5c37e0fa
asm-generic: make sparse happy with odd-sized put_unaligned_*() * |f28d3f0d96
Merge branch 'android14-6.1' into branch 'android14-6.1-lts' * |7a8376be74
ANDROID: use reserved cpucaps for new capability * |c801066eca
Revert "mm/sparsemem: fix race in accessing memory_section->usage" * |2dbddbe358
Merge 6.1.76 into android-6.1 |\| | *e5c3b988b8
Linux 6.1.76 | *d7dc6a8604
net/mlx5e: Handle hardware IPsec limits events | *e90c7d26ca
serial: core: fix kernel-doc for uart_port_unlock_irqrestore() | *c02d3872c8
x86/entry/ia32: Ensure s32 is sign extended to s64 | *cf0b4ba4b0
tick/sched: Preserve number of idle sleeps across CPU hotplug events | *e333bbb557
mips: Call lose_fpu(0) before initializing fcr31 in mips_set_personality_nan | *ec745eeff4
cxl/region:Fix overflow issue in alloc_hpa() | *1111abee59
MIPS: lantiq: register smp_ops on non-smp platforms | *b086f6d979
spi: fix finalize message on error return | *598af91f62
spi: bcm-qspi: fix SFDP BFPT read by usig mspi read | *a2fa86e2bb
drm/bridge: anx7625: Ensure bridge is suspended in disable() | *9564767b67
block: Move checking GENHD_FL_NO_PART to bdev_add_partition() | *4b84411165
gpio: eic-sprd: Clear interrupt after set the interrupt type | *23cf4cf429
drm/exynos: gsc: minor fix for loop iteration in gsc_runtime_resume | *ba930885bf
drm/exynos: fix accidental on-stack copy of exynos_drm_plane | *c1cd4f9da5
drm/bridge: parade-ps8640: Make sure we drop the AUX mutex in the error case | *6ba690e7f7
drm/bridge: parade-ps8640: Ensure bridge is suspended in .post_disable() | *279f1cc562
drm/bridge: sii902x: Fix audio codec unregistration | *e0f83c234e
drm/bridge: sii902x: Fix probing race issue | *c46f9c7f93
drm/bridge: sii902x: Use devm_regulator_bulk_get_enable() | *ed555f5b5c
drm: panel-simple: add missing bus flags for Tianma tm070jvhg[30/33] | *6b7fb2903a
drm/bridge: parade-ps8640: Wait for HPD when doing an AUX transfer | *f9a4c401bf
Revert "powerpc/64s: Increase default stack size to 32KB" | *ec5e692cba
drm/panel-edp: drm/panel-edp: Fix AUO B116XAK01 name and timing | *f91c77d2c3
btrfs: zoned: optimize hint byte for zoned allocator | *4c45143447
btrfs: zoned: factor out prepare_allocation_zoned() | *b168029d67
serial: sc16is7xx: fix unconditional activation of THRI interrupt | *49d733c4bb
serial: sc16is7xx: Use port lock wrappers | *4fd9a02121
serial: core: Provide port lock wrappers | *e11dea8f50
dlm: use kernel_connect() and kernel_bind() | *fd7c2ffa0e
ARM: dts: qcom: sdx55: fix USB SS wakeup | *ecf87621b4
ARM: dts: qcom: sdx55: fix USB DP/DM HS PHY interrupts | *34d2c909c7
ARM: dts: qcom: sdx55: fix pdc '#interrupt-cells' | *bba1320ef2
ARM: dts: samsung: exynos4210-i9100: Unconditionally enable LDO12 | *46cd7ef69f
ARM: dts: qcom: sdx55: fix USB wakeup interrupt types | *b87a1229d8
pipe: wakeup wr_wait after setting max_usage | *6f5c4aaddd
fs/pipe: move check to pipe_has_watch_queue() | *28f010dc50
thermal: intel: hfi: Add syscore callbacks for system-wide PM | *b2517d1412
thermal: intel: hfi: Disable an HFI instance when all its CPUs go offline | *a8056e821c
thermal: intel: hfi: Refactor enabling code into helper functions | *e1c9d32c98
PM: sleep: Fix possible deadlocks in core system-wide PM code | *a9dbf8ca31
PM: core: Remove unnecessary (void *) conversions | *ea3357c6cf
bus: mhi: ep: Do not allocate event ring element on stack | *512fc4d735
media: ov13b10: Enable runtime PM before registering async sub-device | *a14c2431e5
media: ov13b10: Support device probe in non-zero ACPI D state | *33bf23c994
erofs: fix lz4 inplace decompression | *2197389e1a
erofs: get rid of the remaining kmap_atomic() | *471ab2e8b7
drm/amdgpu/pm: Fix the power source flag error | *b4cbd01832
drm/amd/display: Port DENTIST hang and TDR fixes to OTG disable W/A | *7960f14fca
drm/bridge: nxp-ptn3460: simplify some error checking | *6341140b04
platform/x86: intel-uncore-freq: Fix types in sysfs callbacks | *85d16c03dd
drm/amd/display: Disable PSR-SU on Parade 0803 TCON again | *b5fcb340b7
drm/tidss: Fix atomic_flush check | *2a81e844d1
drm/bridge: nxp-ptn3460: fix i2c_master_send() error checking | *62f2e79cf9
drm: Don't unref the same fb many times by mistake due to deadlock handling | *635e996e6e
cpufreq: intel_pstate: Refine computation of P-state for given frequency | *242996f500
gpiolib: acpi: Ignore touchpad wakeup on GPD G1619-04 | *6c495c84e2
xfs: read only mounts with fsopen mount API are busted | *7f95f6997f
firmware: arm_scmi: Check mailbox/SMT channel for consistency | *2c939c74ef
ksmbd: fix global oob in ksmbd_nl_policy | *2841631a03
platform/x86: p2sb: Allow p2sb_bar() calls during PCI device probe | *8e34430e33
netfilter: nf_tables: reject QUEUE/DROP verdict parameters | *af149a4689
netfilter: nft_chain_filter: handle NETDEV_UNREGISTER for inet/ingress basechain | *5e7d8ddf2a
hv_netvsc: Calculate correct ring size when PAGE_SIZE is not 4 Kbytes | *aa2cc93639
wifi: iwlwifi: fix a memory corruption | *dcc54a54de
exec: Fix error handling in begin_new_exec() | *4646445756
rbd: don't move requests to the running list on errors | *6e6bca99e8
btrfs: don't abort filesystem when attempting to snapshot deleted subvolume | *52e02f26d0
btrfs: defrag: reject unknown flags of btrfs_ioctl_defrag_range_args | *86aff7c5f7
btrfs: don't warn if discard range is not aligned to sector | *b60f748a2f
btrfs: tree-checker: fix inline ref size in error messages | *c91c247be4
btrfs: ref-verify: free ref cache before clearing mount opt | *9ebd514fbd
btrfs: avoid copying BTRFS_ROOT_SUBVOL_DEAD flag to snapshot of subvolume being deleted | *d9c54763e5
nbd: always initialize struct msghdr completely | *0a5a083c2b
net: fec: fix the unhandled context fault from smmu | *5b1086d226
fjes: fix memleaks in fjes_hw_setup | *4b4dcb3f42
selftests: netdevsim: fix the udp_tunnel_nic test | *cec65f09c4
net: mvpp2: clear BM pool before initialization | *acb6eaf2ea
net: stmmac: Wait a bit for the reset to take effect | *67ee37360d
netfilter: nf_tables: validate NFPROTO_* family | *ed5b62bbd4
netfilter: nf_tables: restrict anonymous set and map names to 16 bytes | *c25d7922ef
btrfs: fix race between reading a directory and adding entries to it | *fd968e683b
btrfs: refresh dir last index during a rewinddir(3) call | *a045b6b197
btrfs: set last dir index to the current last index when opening dir | *2aa515b5b5
btrfs: fix infinite directory reads | *bc6e242bb7
netfilter: nft_limit: reject configurations that cause integer overflow | *c817f5c016
rcu: Defer RCU kthreads wakeup when CPU is dying | *b2fa86b2ac
net/mlx5e: fix a potential double-free in fs_any_create_groups | *42876db001
net/mlx5e: fix a double-free in arfs_create_groups | *890881d10f
net/mlx5e: Allow software parsing when IPsec crypto is enabled | *62ce16005e
net/mlx5: Use mlx5 device constant for selecting CQ period mode for ASO | *75d9ed4930
net/mlx5: DR, Can't go to uplink vport on RX rule | *e54aedd4d0
net/mlx5: DR, Use the right GVMI number for drop action | *f11792c389
ipv6: init the accept_queue's spinlocks in inet6_create | *de061604f8
netlink: fix potential sleeping issue in mqueue_flush_file | *90fba981ca
tcp: Add memory barrier to tcp_push() | *ab49164c60
afs: Hide silly-rename files from userspace | *f4f7e696db
tracing: Ensure visibility when inserting an element into tracing_map | *82a9bc343b
netfs, fscache: Prevent Oops in fscache_put_cache() | *71024928b3
net/rds: Fix UBSAN: array-index-out-of-bounds in rds_cmsg_recv | *fcb0b4b6bc
net: micrel: Fix PTP frame parsing for lan8814 | *7a581f597a
tun: add missing rx stats accounting in tun_xdp_act | *41e7decdad
tun: fix missing dropped counter in tun_xdp_act | *a2232f29bf
net: fix removing a namespace with conflicting altnames | *6646145be9
udp: fix busy polling | *660c3053d9
llc: Drop support for ETH_P_TR_802_2. | *6d53b813ff
llc: make llc_ui_sendmsg() more robust against bonding changes | *c5e7fa4f9d
vlan: skip nested type that is not IFLA_VLAN_QOS_MAPPING | *4ee0613868
bnxt_en: Wait for FLR to complete during probe | *b1e0a68a0c
tcp: make sure init the accept_queue's spinlocks once | *6994dba063
net/smc: fix illegal rmb_desc access in SMC-D connection dump | *49aaeb8c53
wifi: mac80211: fix potential sta-link leak | *b59e08c872
drm/amd/display: pbn_div need be updated for hotplug event | *a5046e5eb8
Revert "drm/amd: Enable PCIe PME from D3" | *b1c06ee2d1
ksmbd: Add missing set_freezable() for freezable kthread | *844dfef316
ksmbd: send lease break notification on FILE_RENAME_INFORMATION | *de603a52af
ksmbd: don't increment epoch if current state and request state are same | *e61fc656ce
ksmbd: fix potential circular locking issue in smb2_set_ea() | *8fa25e67fd
ksmbd: set v2 lease version on lease upgrade | *3101b9fd74
mm: page_alloc: unreserve highatomic page blocks before oom | *1d15da5601
LoongArch/smp: Call rcutree_report_cpu_starting() earlier | *0e0653d53a
serial: sc16is7xx: improve do/while loop in sc16is7xx_irq() | *80beb4424d
serial: sc16is7xx: remove obsolete loop in sc16is7xx_port_irq() | *de8e41f78f
serial: sc16is7xx: fix invalid sc16is7xx_lines bitfield in case of probe error | *416b10d281
serial: sc16is7xx: convert from _raw_ to _noinc_ regmap functions for FIFO | *4b068e55bf
serial: sc16is7xx: change EFR lock to operate on each channels | *f6c58552a8
serial: sc16is7xx: remove unused line structure member | *6dca71e6e1
serial: sc16is7xx: remove global regmap from struct sc16is7xx_port | *9bcb019aee
serial: sc16is7xx: remove wasteful static buffer in sc16is7xx_regmap_name() | *45ec1b7acc
serial: sc16is7xx: improve regmap debugfs by using one regmap per port | *362be9ec32
rename(): fix the locking of subdirectories | *68ed9e3332
mm/sparsemem: fix race in accessing memory_section->usage | *367a47ef4c
mm/rmap: fix misplaced parenthesis of a likely() | *5d01dcda81
ubifs: ubifs_symlink: Fix memleak of inode->i_link in error path | *13a6ceeb5b
nouveau/vmm: don't set addr on the fail path to avoid warning | *40c23b5e07
rtc: Extend timeout for waiting for UIP to clear to 1s | *7971389316
rtc: Add support for configuring the UIP timeout for RTC reads | *fd1f5396be
rtc: mc146818-lib: Adjust failure return code for mc146818_get_time() | *911e7206c8
rtc: Adjust failure return code for cmos_set_alarm() | *aca1ea92f5
rtc: cmos: Use ACPI alarm for non-Intel x86 systems too | *2b1dc0666e
arm64: Rename ARM64_WORKAROUND_2966298 | *9fec4db7ff
media: mtk-jpeg: Fix use after free bug due to error path handling in mtk_jpeg_dec_device_run | *a33fbb8b6d
mmc: mmc_spi: remove custom DMA mapped buffers | *c4edcd134b
mmc: core: Use mrq.sbc in close-ended ffu | *d78fac87c6
media: videobuf2-dma-sg: fix vmap callback | *c160f2ac85
scripts/get_abi: fix source path leak | *efe3ec7066
docs: kernel_abi.py: fix command injection | *c014490c0b
lsm: new security_file_ioctl_compat() hook | *2647770eac
arm64: dts: qcom: sm8150: fix USB DP/DM HS PHY interrupts | *0168530568
arm64: dts: qcom: sdm845: fix USB DP/DM HS PHY interrupts | *69ee126bba
arm64: dts: qcom: sc7280: fix usb_1 wakeup interrupt types | *eec1f92949
arm64: dts: qcom: sm8150: fix USB wakeup interrupt types | *595d35c6ae
arm64: dts: qcom: sdm845: fix USB wakeup interrupt types | *8191aa4146
arm64: dts: qcom: sc7180: fix USB wakeup interrupt types | *9f29c5d2bf
scsi: ufs: core: Remove the ufshcd_hba_exit() call from ufshcd_async_scan() | *2ab32986a0
dmaengine: fix NULL pointer in channel unregistration function | *0c8ada71d9
iio: adc: ad7091r: Enable internal vref if external vref is not supplied | *fcf8e37152
async: Introduce async_schedule_dev_nocall() | *6e8aab4de7
async: Split async_schedule_node_domain() | *b37c1b0db1
parisc/power: Fix power soft-off button emulation on qemu | *71602d95ae
parisc/firmware: Fix F-extend for PDC addresses | *0b093176fd
bus: mhi: host: Add spinlock to protect WP access when queueing TREs | *3c5ec66b4b
bus: mhi: host: Drop chan lock before queuing buffers | *2df39ac8f8
bus: mhi: host: Add alignment check for event ring read pointer | *574f69b46b
mips: Fix max_mapnr being uninitialized on early stages | *6690a0acbb
s390/vfio-ap: let on_scan_complete() callback filter matrix and update guest's APCB | *baf3fcb268
s390/vfio-ap: loop over the shadow APCB when filtering guest's AP configuration | *d6b8d034b5
s390/vfio-ap: always filter entire AP matrix | *51a7c02bc7
media: ov9734: Enable runtime PM before registering async sub-device | *f4bb1d5daf
rpmsg: virtio: Free driver_override when rpmsg_remove() | *e8757cd139
media: imx355: Enable runtime PM before registering async sub-device | *8a7729cda2
PM / devfreq: Fix buffer overflow in trans_stat_show | *f4518de40a
s390/vfio-ap: unpin pages on gisc registration failure | *dbc9a791a7
crypto: s390/aes - Fix buffer overread in CTR mode | *aa8aa16ed9
hwrng: core - Fix page fault dead lock on mmap-ed hwrng | *3a081586c7
PM: hibernate: Enforce ordering during image compression/decompression | *680eb0a993
crypto: api - Disallow identical driver names | *562850a008
btrfs: sysfs: validate scrub_speed_max value | *29e2da3eab
OPP: Pass rounded rate to _set_opp() | *4b5f8a187f
arm64: properly install vmlinuz.efi | *852b6b2a2f
ext4: allow for the last group to be marked as trimmed | *137568aa54
iio: adc: ad7091r: Allow users to configure device events | *6f57121e9c
iio: adc: ad7091r: Set alert bit in config register | *fafda9f08a
Revert "nSVM: Check for reserved encodings of TLB_CONTROL in nested VMCB" | *c519a9054b
usb: dwc3: gadget: Handle EP0 request dequeuing properly | *c8fe8ce07f
usb: dwc3: gadget: Queue PM runtime idle on disconnect event | *ce27046883
usb: dwc3: gadget: Refactor EP0 forced stall/restart into a separate API *0eac7b614d
Merge branch 'android14-6.1' into branch 'android14-6.1-lts' *63040ce8e9
Merge branch 'android14-6.1' into branch 'android14-6.1-lts' *50fe3d2012
ANDROID: Add symbols for IIO SCMI module *e666b8755f
ANDROID: Update symbols list for open-dice.ko Change-Id: Icb970da9bfba44d188465ce8c8148858a4a2fb1a Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
1694 lines
40 KiB
C
1694 lines
40 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* linux/kernel/power/swap.c
|
|
*
|
|
* This file provides functions for reading the suspend image from
|
|
* and writing it to a swap partition.
|
|
*
|
|
* Copyright (C) 1998,2001-2005 Pavel Machek <pavel@ucw.cz>
|
|
* Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
|
|
* Copyright (C) 2010-2012 Bojan Smojver <bojan@rexursive.com>
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "PM: " fmt
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/file.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/device.h>
|
|
#include <linux/bio.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/swapops.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/crc32.h>
|
|
#include <linux/ktime.h>
|
|
#include <trace/hooks/bl_hib.h>
|
|
|
|
#include "power.h"
|
|
|
|
#define HIBERNATE_SIG "S1SUSPEND"
|
|
|
|
u32 swsusp_hardware_signature;
|
|
|
|
/*
|
|
* When reading an {un,}compressed image, we may restore pages in place,
|
|
* in which case some architectures need these pages cleaning before they
|
|
* can be executed. We don't know which pages these may be, so clean the lot.
|
|
*/
|
|
static bool clean_pages_on_read;
|
|
static bool clean_pages_on_decompress;
|
|
|
|
/*
|
|
* The swap map is a data structure used for keeping track of each page
|
|
* written to a swap partition. It consists of many swap_map_page
|
|
* structures that contain each an array of MAP_PAGE_ENTRIES swap entries.
|
|
* These structures are stored on the swap and linked together with the
|
|
* help of the .next_swap member.
|
|
*
|
|
* The swap map is created during suspend. The swap map pages are
|
|
* allocated and populated one at a time, so we only need one memory
|
|
* page to set up the entire structure.
|
|
*
|
|
* During resume we pick up all swap_map_page structures into a list.
|
|
*/
|
|
|
|
#define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(sector_t) - 1)
|
|
|
|
/*
|
|
* Number of free pages that are not high.
|
|
*/
|
|
static inline unsigned long low_free_pages(void)
|
|
{
|
|
return nr_free_pages() - nr_free_highpages();
|
|
}
|
|
|
|
/*
|
|
* Number of pages required to be kept free while writing the image. Always
|
|
* half of all available low pages before the writing starts.
|
|
*/
|
|
static inline unsigned long reqd_free_pages(void)
|
|
{
|
|
return low_free_pages() / 2;
|
|
}
|
|
|
|
struct swap_map_page {
|
|
sector_t entries[MAP_PAGE_ENTRIES];
|
|
sector_t next_swap;
|
|
};
|
|
|
|
struct swap_map_page_list {
|
|
struct swap_map_page *map;
|
|
struct swap_map_page_list *next;
|
|
};
|
|
|
|
/*
|
|
* The swap_map_handle structure is used for handling swap in
|
|
* a file-alike way
|
|
*/
|
|
|
|
struct swap_map_handle {
|
|
struct swap_map_page *cur;
|
|
struct swap_map_page_list *maps;
|
|
sector_t cur_swap;
|
|
sector_t first_sector;
|
|
unsigned int k;
|
|
unsigned long reqd_free_pages;
|
|
u32 crc32;
|
|
};
|
|
|
|
struct swsusp_header {
|
|
char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int) -
|
|
sizeof(u32) - sizeof(u32)];
|
|
u32 hw_sig;
|
|
u32 crc32;
|
|
sector_t image;
|
|
unsigned int flags; /* Flags to pass to the "boot" kernel */
|
|
char orig_sig[10];
|
|
char sig[10];
|
|
} __packed;
|
|
|
|
static struct swsusp_header *swsusp_header;
|
|
|
|
/*
|
|
* The following functions are used for tracing the allocated
|
|
* swap pages, so that they can be freed in case of an error.
|
|
*/
|
|
|
|
struct swsusp_extent {
|
|
struct rb_node node;
|
|
unsigned long start;
|
|
unsigned long end;
|
|
};
|
|
|
|
static struct rb_root swsusp_extents = RB_ROOT;
|
|
|
|
static int swsusp_extents_insert(unsigned long swap_offset)
|
|
{
|
|
struct rb_node **new = &(swsusp_extents.rb_node);
|
|
struct rb_node *parent = NULL;
|
|
struct swsusp_extent *ext;
|
|
|
|
/* Figure out where to put the new node */
|
|
while (*new) {
|
|
ext = rb_entry(*new, struct swsusp_extent, node);
|
|
parent = *new;
|
|
if (swap_offset < ext->start) {
|
|
/* Try to merge */
|
|
if (swap_offset == ext->start - 1) {
|
|
ext->start--;
|
|
return 0;
|
|
}
|
|
new = &((*new)->rb_left);
|
|
} else if (swap_offset > ext->end) {
|
|
/* Try to merge */
|
|
if (swap_offset == ext->end + 1) {
|
|
ext->end++;
|
|
return 0;
|
|
}
|
|
new = &((*new)->rb_right);
|
|
} else {
|
|
/* It already is in the tree */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
/* Add the new node and rebalance the tree. */
|
|
ext = kzalloc(sizeof(struct swsusp_extent), GFP_KERNEL);
|
|
if (!ext)
|
|
return -ENOMEM;
|
|
|
|
ext->start = swap_offset;
|
|
ext->end = swap_offset;
|
|
rb_link_node(&ext->node, parent, new);
|
|
rb_insert_color(&ext->node, &swsusp_extents);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* alloc_swapdev_block - allocate a swap page and register that it has
|
|
* been allocated, so that it can be freed in case of an error.
|
|
*/
|
|
|
|
sector_t alloc_swapdev_block(int swap)
|
|
{
|
|
unsigned long offset;
|
|
|
|
offset = swp_offset(get_swap_page_of_type(swap));
|
|
if (offset) {
|
|
if (swsusp_extents_insert(offset))
|
|
swap_free(swp_entry(swap, offset));
|
|
else
|
|
return swapdev_block(swap, offset);
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(alloc_swapdev_block);
|
|
|
|
/*
|
|
* free_all_swap_pages - free swap pages allocated for saving image data.
|
|
* It also frees the extents used to register which swap entries had been
|
|
* allocated.
|
|
*/
|
|
|
|
void free_all_swap_pages(int swap)
|
|
{
|
|
struct rb_node *node;
|
|
|
|
while ((node = swsusp_extents.rb_node)) {
|
|
struct swsusp_extent *ext;
|
|
unsigned long offset;
|
|
|
|
ext = rb_entry(node, struct swsusp_extent, node);
|
|
rb_erase(node, &swsusp_extents);
|
|
for (offset = ext->start; offset <= ext->end; offset++)
|
|
swap_free(swp_entry(swap, offset));
|
|
|
|
kfree(ext);
|
|
}
|
|
}
|
|
|
|
int swsusp_swap_in_use(void)
|
|
{
|
|
return (swsusp_extents.rb_node != NULL);
|
|
}
|
|
|
|
/*
|
|
* General things
|
|
*/
|
|
|
|
static unsigned short root_swap = 0xffff;
|
|
static struct block_device *hib_resume_bdev;
|
|
|
|
struct hib_bio_batch {
|
|
atomic_t count;
|
|
wait_queue_head_t wait;
|
|
blk_status_t error;
|
|
struct blk_plug plug;
|
|
};
|
|
|
|
static void hib_init_batch(struct hib_bio_batch *hb)
|
|
{
|
|
atomic_set(&hb->count, 0);
|
|
init_waitqueue_head(&hb->wait);
|
|
hb->error = BLK_STS_OK;
|
|
blk_start_plug(&hb->plug);
|
|
}
|
|
|
|
static void hib_finish_batch(struct hib_bio_batch *hb)
|
|
{
|
|
blk_finish_plug(&hb->plug);
|
|
}
|
|
|
|
static void hib_end_io(struct bio *bio)
|
|
{
|
|
struct hib_bio_batch *hb = bio->bi_private;
|
|
struct page *page = bio_first_page_all(bio);
|
|
|
|
if (bio->bi_status) {
|
|
pr_alert("Read-error on swap-device (%u:%u:%Lu)\n",
|
|
MAJOR(bio_dev(bio)), MINOR(bio_dev(bio)),
|
|
(unsigned long long)bio->bi_iter.bi_sector);
|
|
}
|
|
|
|
if (bio_data_dir(bio) == WRITE)
|
|
put_page(page);
|
|
else if (clean_pages_on_read)
|
|
flush_icache_range((unsigned long)page_address(page),
|
|
(unsigned long)page_address(page) + PAGE_SIZE);
|
|
|
|
if (bio->bi_status && !hb->error)
|
|
hb->error = bio->bi_status;
|
|
if (atomic_dec_and_test(&hb->count))
|
|
wake_up(&hb->wait);
|
|
|
|
bio_put(bio);
|
|
}
|
|
|
|
static int hib_submit_io(blk_opf_t opf, pgoff_t page_off, void *addr,
|
|
struct hib_bio_batch *hb)
|
|
{
|
|
struct page *page = virt_to_page(addr);
|
|
struct bio *bio;
|
|
int error = 0;
|
|
|
|
bio = bio_alloc(hib_resume_bdev, 1, opf, GFP_NOIO | __GFP_HIGH);
|
|
bio->bi_iter.bi_sector = page_off * (PAGE_SIZE >> 9);
|
|
|
|
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
|
|
pr_err("Adding page to bio failed at %llu\n",
|
|
(unsigned long long)bio->bi_iter.bi_sector);
|
|
bio_put(bio);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (hb) {
|
|
bio->bi_end_io = hib_end_io;
|
|
bio->bi_private = hb;
|
|
atomic_inc(&hb->count);
|
|
submit_bio(bio);
|
|
} else {
|
|
error = submit_bio_wait(bio);
|
|
bio_put(bio);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static int hib_wait_io(struct hib_bio_batch *hb)
|
|
{
|
|
/*
|
|
* We are relying on the behavior of blk_plug that a thread with
|
|
* a plug will flush the plug list before sleeping.
|
|
*/
|
|
wait_event(hb->wait, atomic_read(&hb->count) == 0);
|
|
return blk_status_to_errno(hb->error);
|
|
}
|
|
|
|
/*
|
|
* Saving part
|
|
*/
|
|
static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags)
|
|
{
|
|
int error;
|
|
|
|
hib_submit_io(REQ_OP_READ, swsusp_resume_block, swsusp_header, NULL);
|
|
if (!memcmp("SWAP-SPACE",swsusp_header->sig, 10) ||
|
|
!memcmp("SWAPSPACE2",swsusp_header->sig, 10)) {
|
|
memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10);
|
|
memcpy(swsusp_header->sig, HIBERNATE_SIG, 10);
|
|
swsusp_header->image = handle->first_sector;
|
|
if (swsusp_hardware_signature) {
|
|
swsusp_header->hw_sig = swsusp_hardware_signature;
|
|
flags |= SF_HW_SIG;
|
|
}
|
|
swsusp_header->flags = flags;
|
|
if (flags & SF_CRC32_MODE)
|
|
swsusp_header->crc32 = handle->crc32;
|
|
error = hib_submit_io(REQ_OP_WRITE | REQ_SYNC,
|
|
swsusp_resume_block, swsusp_header, NULL);
|
|
} else {
|
|
pr_err("Swap header not found!\n");
|
|
error = -ENODEV;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Hold the swsusp_header flag. This is used in software_resume() in
|
|
* 'kernel/power/hibernate' to check if the image is compressed and query
|
|
* for the compression algorithm support(if so).
|
|
*/
|
|
unsigned int swsusp_header_flags;
|
|
|
|
/**
|
|
* swsusp_swap_check - check if the resume device is a swap device
|
|
* and get its index (if so)
|
|
*
|
|
* This is called before saving image
|
|
*/
|
|
static int swsusp_swap_check(void)
|
|
{
|
|
int res;
|
|
|
|
if (swsusp_resume_device)
|
|
res = swap_type_of(swsusp_resume_device, swsusp_resume_block);
|
|
else
|
|
res = find_first_swap(&swsusp_resume_device);
|
|
if (res < 0)
|
|
return res;
|
|
root_swap = res;
|
|
|
|
hib_resume_bdev = blkdev_get_by_dev(swsusp_resume_device, FMODE_WRITE,
|
|
NULL);
|
|
if (IS_ERR(hib_resume_bdev))
|
|
return PTR_ERR(hib_resume_bdev);
|
|
|
|
res = set_blocksize(hib_resume_bdev, PAGE_SIZE);
|
|
if (res < 0)
|
|
blkdev_put(hib_resume_bdev, FMODE_WRITE);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* write_page - Write one page to given swap location.
|
|
* @buf: Address we're writing.
|
|
* @offset: Offset of the swap page we're writing to.
|
|
* @hb: bio completion batch
|
|
*/
|
|
|
|
static int write_page(void *buf, sector_t offset, struct hib_bio_batch *hb)
|
|
{
|
|
void *src;
|
|
int ret;
|
|
|
|
if (!offset)
|
|
return -ENOSPC;
|
|
|
|
if (hb) {
|
|
src = (void *)__get_free_page(GFP_NOIO | __GFP_NOWARN |
|
|
__GFP_NORETRY);
|
|
if (src) {
|
|
copy_page(src, buf);
|
|
} else {
|
|
ret = hib_wait_io(hb); /* Free pages */
|
|
if (ret)
|
|
return ret;
|
|
src = (void *)__get_free_page(GFP_NOIO |
|
|
__GFP_NOWARN |
|
|
__GFP_NORETRY);
|
|
if (src) {
|
|
copy_page(src, buf);
|
|
} else {
|
|
WARN_ON_ONCE(1);
|
|
hb = NULL; /* Go synchronous */
|
|
src = buf;
|
|
}
|
|
}
|
|
} else {
|
|
src = buf;
|
|
}
|
|
return hib_submit_io(REQ_OP_WRITE | REQ_SYNC, offset, src, hb);
|
|
}
|
|
|
|
static void release_swap_writer(struct swap_map_handle *handle)
|
|
{
|
|
if (handle->cur)
|
|
free_page((unsigned long)handle->cur);
|
|
handle->cur = NULL;
|
|
}
|
|
|
|
static int get_swap_writer(struct swap_map_handle *handle)
|
|
{
|
|
int ret;
|
|
|
|
ret = swsusp_swap_check();
|
|
if (ret) {
|
|
if (ret != -ENOSPC)
|
|
pr_err("Cannot find swap device, try swapon -a\n");
|
|
return ret;
|
|
}
|
|
handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
|
|
if (!handle->cur) {
|
|
ret = -ENOMEM;
|
|
goto err_close;
|
|
}
|
|
handle->cur_swap = alloc_swapdev_block(root_swap);
|
|
if (!handle->cur_swap) {
|
|
ret = -ENOSPC;
|
|
goto err_rel;
|
|
}
|
|
handle->k = 0;
|
|
handle->reqd_free_pages = reqd_free_pages();
|
|
handle->first_sector = handle->cur_swap;
|
|
return 0;
|
|
err_rel:
|
|
release_swap_writer(handle);
|
|
err_close:
|
|
swsusp_close(FMODE_WRITE);
|
|
return ret;
|
|
}
|
|
|
|
static int swap_write_page(struct swap_map_handle *handle, void *buf,
|
|
struct hib_bio_batch *hb)
|
|
{
|
|
int error = 0;
|
|
sector_t offset;
|
|
bool skip = false;
|
|
|
|
if (!handle->cur)
|
|
return -EINVAL;
|
|
offset = alloc_swapdev_block(root_swap);
|
|
error = write_page(buf, offset, hb);
|
|
if (error)
|
|
return error;
|
|
handle->cur->entries[handle->k++] = offset;
|
|
if (handle->k >= MAP_PAGE_ENTRIES) {
|
|
offset = alloc_swapdev_block(root_swap);
|
|
if (!offset)
|
|
return -ENOSPC;
|
|
handle->cur->next_swap = offset;
|
|
trace_android_vh_skip_swap_map_write(&skip);
|
|
if (!skip) {
|
|
error = write_page(handle->cur, handle->cur_swap, hb);
|
|
if (error)
|
|
goto out;
|
|
}
|
|
clear_page(handle->cur);
|
|
handle->cur_swap = offset;
|
|
handle->k = 0;
|
|
|
|
if (hb && low_free_pages() <= handle->reqd_free_pages) {
|
|
error = hib_wait_io(hb);
|
|
if (error)
|
|
goto out;
|
|
/*
|
|
* Recalculate the number of required free pages, to
|
|
* make sure we never take more than half.
|
|
*/
|
|
handle->reqd_free_pages = reqd_free_pages();
|
|
}
|
|
}
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
static int flush_swap_writer(struct swap_map_handle *handle)
|
|
{
|
|
if (handle->cur && handle->cur_swap)
|
|
return write_page(handle->cur, handle->cur_swap, NULL);
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int swap_writer_finish(struct swap_map_handle *handle,
|
|
unsigned int flags, int error)
|
|
{
|
|
if (!error) {
|
|
pr_info("S");
|
|
error = mark_swapfiles(handle, flags);
|
|
pr_cont("|\n");
|
|
flush_swap_writer(handle);
|
|
}
|
|
|
|
if (error)
|
|
free_all_swap_pages(root_swap);
|
|
release_swap_writer(handle);
|
|
swsusp_close(FMODE_WRITE);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Bytes we need for compressed data in worst case. We assume(limitation)
|
|
* this is the worst of all the compression algorithms.
|
|
*/
|
|
#define bytes_worst_compress(x) ((x) + ((x) / 16) + 64 + 3 + 2)
|
|
|
|
/* We need to remember how much compressed data we need to read. */
|
|
#define CMP_HEADER sizeof(size_t)
|
|
|
|
/* Number of pages/bytes we'll compress at one time. */
|
|
#define UNC_PAGES 32
|
|
#define UNC_SIZE (UNC_PAGES * PAGE_SIZE)
|
|
|
|
/* Number of pages we need for compressed data (worst case). */
|
|
#define CMP_PAGES DIV_ROUND_UP(bytes_worst_compress(UNC_SIZE) + \
|
|
CMP_HEADER, PAGE_SIZE)
|
|
#define CMP_SIZE (CMP_PAGES * PAGE_SIZE)
|
|
|
|
/* Maximum number of threads for compression/decompression. */
|
|
#define CMP_THREADS 3
|
|
|
|
/* Minimum/maximum number of pages for read buffering. */
|
|
#define CMP_MIN_RD_PAGES 1024
|
|
#define CMP_MAX_RD_PAGES 8192
|
|
|
|
/**
|
|
* save_image - save the suspend image data
|
|
*/
|
|
|
|
static int save_image(struct swap_map_handle *handle,
|
|
struct snapshot_handle *snapshot,
|
|
unsigned int nr_to_write)
|
|
{
|
|
unsigned int m;
|
|
int ret;
|
|
int nr_pages;
|
|
int err2;
|
|
struct hib_bio_batch hb;
|
|
ktime_t start;
|
|
ktime_t stop;
|
|
|
|
hib_init_batch(&hb);
|
|
|
|
pr_info("Saving image data pages (%u pages)...\n",
|
|
nr_to_write);
|
|
m = nr_to_write / 10;
|
|
if (!m)
|
|
m = 1;
|
|
nr_pages = 0;
|
|
start = ktime_get();
|
|
while (1) {
|
|
ret = snapshot_read_next(snapshot);
|
|
if (ret <= 0)
|
|
break;
|
|
trace_android_vh_encrypt_page(data_of(*snapshot));
|
|
ret = swap_write_page(handle, data_of(*snapshot), &hb);
|
|
if (ret)
|
|
break;
|
|
if (!(nr_pages % m))
|
|
pr_info("Image saving progress: %3d%%\n",
|
|
nr_pages / m * 10);
|
|
nr_pages++;
|
|
}
|
|
err2 = hib_wait_io(&hb);
|
|
hib_finish_batch(&hb);
|
|
stop = ktime_get();
|
|
if (!ret)
|
|
ret = err2;
|
|
if (!ret)
|
|
pr_info("Image saving done\n");
|
|
swsusp_show_speed(start, stop, nr_to_write, "Wrote");
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Structure used for CRC32.
|
|
*/
|
|
struct crc_data {
|
|
struct task_struct *thr; /* thread */
|
|
atomic_t ready; /* ready to start flag */
|
|
atomic_t stop; /* ready to stop flag */
|
|
unsigned run_threads; /* nr current threads */
|
|
wait_queue_head_t go; /* start crc update */
|
|
wait_queue_head_t done; /* crc update done */
|
|
u32 *crc32; /* points to handle's crc32 */
|
|
size_t *unc_len[CMP_THREADS]; /* uncompressed lengths */
|
|
unsigned char *unc[CMP_THREADS]; /* uncompressed data */
|
|
};
|
|
|
|
/**
|
|
* CRC32 update function that runs in its own thread.
|
|
*/
|
|
static int crc32_threadfn(void *data)
|
|
{
|
|
struct crc_data *d = data;
|
|
unsigned i;
|
|
|
|
while (1) {
|
|
wait_event(d->go, atomic_read_acquire(&d->ready) ||
|
|
kthread_should_stop());
|
|
if (kthread_should_stop()) {
|
|
d->thr = NULL;
|
|
atomic_set_release(&d->stop, 1);
|
|
wake_up(&d->done);
|
|
break;
|
|
}
|
|
atomic_set(&d->ready, 0);
|
|
|
|
for (i = 0; i < d->run_threads; i++)
|
|
*d->crc32 = crc32_le(*d->crc32,
|
|
d->unc[i], *d->unc_len[i]);
|
|
atomic_set_release(&d->stop, 1);
|
|
wake_up(&d->done);
|
|
}
|
|
return 0;
|
|
}
|
|
/**
|
|
* Structure used for data compression.
|
|
*/
|
|
struct cmp_data {
|
|
struct task_struct *thr; /* thread */
|
|
struct crypto_comp *cc; /* crypto compressor stream */
|
|
atomic_t ready; /* ready to start flag */
|
|
atomic_t stop; /* ready to stop flag */
|
|
int ret; /* return code */
|
|
wait_queue_head_t go; /* start compression */
|
|
wait_queue_head_t done; /* compression done */
|
|
size_t unc_len; /* uncompressed length */
|
|
size_t cmp_len; /* compressed length */
|
|
unsigned char unc[UNC_SIZE]; /* uncompressed buffer */
|
|
unsigned char cmp[CMP_SIZE]; /* compressed buffer */
|
|
};
|
|
|
|
/* Indicates the image size after compression */
|
|
static atomic_t compressed_size = ATOMIC_INIT(0);
|
|
|
|
/**
|
|
* Compression function that runs in its own thread.
|
|
*/
|
|
static int compress_threadfn(void *data)
|
|
{
|
|
struct cmp_data *d = data;
|
|
unsigned int cmp_len = 0;
|
|
|
|
while (1) {
|
|
wait_event(d->go, atomic_read_acquire(&d->ready) ||
|
|
kthread_should_stop());
|
|
if (kthread_should_stop()) {
|
|
d->thr = NULL;
|
|
d->ret = -1;
|
|
atomic_set_release(&d->stop, 1);
|
|
wake_up(&d->done);
|
|
break;
|
|
}
|
|
atomic_set(&d->ready, 0);
|
|
|
|
cmp_len = CMP_SIZE - CMP_HEADER;
|
|
d->ret = crypto_comp_compress(d->cc, d->unc, d->unc_len,
|
|
d->cmp + CMP_HEADER,
|
|
&cmp_len);
|
|
d->cmp_len = cmp_len;
|
|
|
|
atomic_set(&compressed_size, atomic_read(&compressed_size) + d->cmp_len);
|
|
atomic_set_release(&d->stop, 1);
|
|
wake_up(&d->done);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* save_compressed_image - Save the suspend image data after compression.
|
|
* @handle: Swap map handle to use for saving the image.
|
|
* @snapshot: Image to read data from.
|
|
* @nr_to_write: Number of pages to save.
|
|
*/
|
|
static int save_compressed_image(struct swap_map_handle *handle,
|
|
struct snapshot_handle *snapshot,
|
|
unsigned int nr_to_write)
|
|
{
|
|
unsigned int m;
|
|
int ret = 0;
|
|
int nr_pages;
|
|
int err2;
|
|
struct hib_bio_batch hb;
|
|
ktime_t start;
|
|
ktime_t stop;
|
|
size_t off;
|
|
unsigned thr, run_threads, nr_threads;
|
|
unsigned char *page = NULL;
|
|
struct cmp_data *data = NULL;
|
|
struct crc_data *crc = NULL;
|
|
|
|
hib_init_batch(&hb);
|
|
|
|
atomic_set(&compressed_size, 0);
|
|
|
|
/*
|
|
* We'll limit the number of threads for compression to limit memory
|
|
* footprint.
|
|
*/
|
|
nr_threads = num_online_cpus() - 1;
|
|
nr_threads = clamp_val(nr_threads, 1, CMP_THREADS);
|
|
|
|
page = (void *)__get_free_page(GFP_NOIO | __GFP_HIGH);
|
|
if (!page) {
|
|
pr_err("Failed to allocate %s page\n", hib_comp_algo);
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
data = vzalloc(array_size(nr_threads, sizeof(*data)));
|
|
if (!data) {
|
|
pr_err("Failed to allocate %s data\n", hib_comp_algo);
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
crc = kzalloc(sizeof(*crc), GFP_KERNEL);
|
|
if (!crc) {
|
|
pr_err("Failed to allocate crc\n");
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
/*
|
|
* Start the compression threads.
|
|
*/
|
|
for (thr = 0; thr < nr_threads; thr++) {
|
|
init_waitqueue_head(&data[thr].go);
|
|
init_waitqueue_head(&data[thr].done);
|
|
|
|
data[thr].cc = crypto_alloc_comp(hib_comp_algo, 0, 0);
|
|
if (IS_ERR_OR_NULL(data[thr].cc)) {
|
|
pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc));
|
|
ret = -EFAULT;
|
|
goto out_clean;
|
|
}
|
|
|
|
data[thr].thr = kthread_run(compress_threadfn,
|
|
&data[thr],
|
|
"image_compress/%u", thr);
|
|
if (IS_ERR(data[thr].thr)) {
|
|
data[thr].thr = NULL;
|
|
pr_err("Cannot start compression threads\n");
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Start the CRC32 thread.
|
|
*/
|
|
init_waitqueue_head(&crc->go);
|
|
init_waitqueue_head(&crc->done);
|
|
|
|
handle->crc32 = 0;
|
|
crc->crc32 = &handle->crc32;
|
|
for (thr = 0; thr < nr_threads; thr++) {
|
|
crc->unc[thr] = data[thr].unc;
|
|
crc->unc_len[thr] = &data[thr].unc_len;
|
|
}
|
|
|
|
crc->thr = kthread_run(crc32_threadfn, crc, "image_crc32");
|
|
if (IS_ERR(crc->thr)) {
|
|
crc->thr = NULL;
|
|
pr_err("Cannot start CRC32 thread\n");
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
/*
|
|
* Adjust the number of required free pages after all allocations have
|
|
* been done. We don't want to run out of pages when writing.
|
|
*/
|
|
handle->reqd_free_pages = reqd_free_pages();
|
|
|
|
pr_info("Using %u thread(s) for %s compression\n", nr_threads, hib_comp_algo);
|
|
pr_info("Compressing and saving image data (%u pages)...\n",
|
|
nr_to_write);
|
|
m = nr_to_write / 10;
|
|
if (!m)
|
|
m = 1;
|
|
nr_pages = 0;
|
|
start = ktime_get();
|
|
for (;;) {
|
|
for (thr = 0; thr < nr_threads; thr++) {
|
|
for (off = 0; off < UNC_SIZE; off += PAGE_SIZE) {
|
|
ret = snapshot_read_next(snapshot);
|
|
if (ret < 0)
|
|
goto out_finish;
|
|
|
|
if (!ret)
|
|
break;
|
|
|
|
memcpy(data[thr].unc + off,
|
|
data_of(*snapshot), PAGE_SIZE);
|
|
|
|
if (!(nr_pages % m))
|
|
pr_info("Image saving progress: %3d%%\n",
|
|
nr_pages / m * 10);
|
|
nr_pages++;
|
|
}
|
|
if (!off)
|
|
break;
|
|
|
|
data[thr].unc_len = off;
|
|
|
|
atomic_set_release(&data[thr].ready, 1);
|
|
wake_up(&data[thr].go);
|
|
}
|
|
|
|
if (!thr)
|
|
break;
|
|
|
|
crc->run_threads = thr;
|
|
atomic_set_release(&crc->ready, 1);
|
|
wake_up(&crc->go);
|
|
|
|
for (run_threads = thr, thr = 0; thr < run_threads; thr++) {
|
|
wait_event(data[thr].done,
|
|
atomic_read_acquire(&data[thr].stop));
|
|
atomic_set(&data[thr].stop, 0);
|
|
|
|
ret = data[thr].ret;
|
|
|
|
if (ret < 0) {
|
|
pr_err("%s compression failed\n", hib_comp_algo);
|
|
goto out_finish;
|
|
}
|
|
|
|
if (unlikely(!data[thr].cmp_len ||
|
|
data[thr].cmp_len >
|
|
bytes_worst_compress(data[thr].unc_len))) {
|
|
pr_err("Invalid %s compressed length\n", hib_comp_algo);
|
|
ret = -1;
|
|
goto out_finish;
|
|
}
|
|
|
|
*(size_t *)data[thr].cmp = data[thr].cmp_len;
|
|
|
|
/*
|
|
* Given we are writing one page at a time to disk, we
|
|
* copy that much from the buffer, although the last
|
|
* bit will likely be smaller than full page. This is
|
|
* OK - we saved the length of the compressed data, so
|
|
* any garbage at the end will be discarded when we
|
|
* read it.
|
|
*/
|
|
for (off = 0;
|
|
off < CMP_HEADER + data[thr].cmp_len;
|
|
off += PAGE_SIZE) {
|
|
memcpy(page, data[thr].cmp + off, PAGE_SIZE);
|
|
|
|
trace_android_vh_encrypt_page(page);
|
|
ret = swap_write_page(handle, page, &hb);
|
|
if (ret)
|
|
goto out_finish;
|
|
}
|
|
trace_android_vh_hibernate_save_cmp_len(data[thr].cmp_len + CMP_HEADER);
|
|
}
|
|
|
|
wait_event(crc->done, atomic_read_acquire(&crc->stop));
|
|
atomic_set(&crc->stop, 0);
|
|
}
|
|
|
|
out_finish:
|
|
err2 = hib_wait_io(&hb);
|
|
stop = ktime_get();
|
|
if (!ret)
|
|
ret = err2;
|
|
if (!ret)
|
|
pr_info("Image saving done\n");
|
|
swsusp_show_speed(start, stop, nr_to_write, "Wrote");
|
|
pr_info("Image size after compression: %d kbytes\n",
|
|
(atomic_read(&compressed_size) / 1024));
|
|
|
|
out_clean:
|
|
hib_finish_batch(&hb);
|
|
if (crc) {
|
|
if (crc->thr)
|
|
kthread_stop(crc->thr);
|
|
kfree(crc);
|
|
}
|
|
if (data) {
|
|
for (thr = 0; thr < nr_threads; thr++) {
|
|
if (data[thr].thr)
|
|
kthread_stop(data[thr].thr);
|
|
if (data[thr].cc)
|
|
crypto_free_comp(data[thr].cc);
|
|
}
|
|
vfree(data);
|
|
}
|
|
if (page) free_page((unsigned long)page);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* enough_swap - Make sure we have enough swap to save the image.
|
|
*
|
|
* Returns TRUE or FALSE after checking the total amount of swap
|
|
* space available from the resume partition.
|
|
*/
|
|
|
|
static int enough_swap(unsigned int nr_pages)
|
|
{
|
|
unsigned int free_swap = count_swap_pages(root_swap, 1);
|
|
unsigned int required;
|
|
|
|
pr_debug("Free swap pages: %u\n", free_swap);
|
|
|
|
required = PAGES_FOR_IO + nr_pages;
|
|
return free_swap > required;
|
|
}
|
|
|
|
/**
|
|
* swsusp_write - Write entire image and metadata.
|
|
* @flags: flags to pass to the "boot" kernel in the image header
|
|
*
|
|
* It is important _NOT_ to umount filesystems at this point. We want
|
|
* them synced (in case something goes wrong) but we DO not want to mark
|
|
* filesystem clean: it is not. (And it does not matter, if we resume
|
|
* correctly, we'll mark system clean, anyway.)
|
|
*/
|
|
|
|
int swsusp_write(unsigned int flags)
|
|
{
|
|
struct swap_map_handle handle;
|
|
struct snapshot_handle snapshot;
|
|
struct swsusp_info *header;
|
|
unsigned long pages;
|
|
int error = 0;
|
|
|
|
pages = snapshot_get_image_size();
|
|
|
|
/*
|
|
* The memory allocated by this vendor hook is later freed as part of
|
|
* PM_POST_HIBERNATION notifier call.
|
|
*/
|
|
trace_android_vh_hibernated_do_mem_alloc(pages, flags, &error);
|
|
if (error < 0) {
|
|
pr_err("Failed to allocate required memory\n");
|
|
return error;
|
|
}
|
|
|
|
error = get_swap_writer(&handle);
|
|
if (error) {
|
|
pr_err("Cannot get swap writer\n");
|
|
return error;
|
|
}
|
|
trace_android_vh_init_aes_encrypt(NULL);
|
|
if (flags & SF_NOCOMPRESS_MODE) {
|
|
if (!enough_swap(pages)) {
|
|
pr_err("Not enough free swap\n");
|
|
error = -ENOSPC;
|
|
goto out_finish;
|
|
}
|
|
}
|
|
memset(&snapshot, 0, sizeof(struct snapshot_handle));
|
|
error = snapshot_read_next(&snapshot);
|
|
if (error < (int)PAGE_SIZE) {
|
|
if (error >= 0)
|
|
error = -EFAULT;
|
|
|
|
goto out_finish;
|
|
}
|
|
header = (struct swsusp_info *)data_of(snapshot);
|
|
error = swap_write_page(&handle, header, NULL);
|
|
if (!error) {
|
|
error = (flags & SF_NOCOMPRESS_MODE) ?
|
|
save_image(&handle, &snapshot, pages - 1) :
|
|
save_compressed_image(&handle, &snapshot, pages - 1);
|
|
|
|
if (!error)
|
|
trace_android_vh_post_image_save(root_swap);
|
|
}
|
|
out_finish:
|
|
error = swap_writer_finish(&handle, flags, error);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* The following functions allow us to read data using a swap map
|
|
* in a file-alike way
|
|
*/
|
|
|
|
static void release_swap_reader(struct swap_map_handle *handle)
|
|
{
|
|
struct swap_map_page_list *tmp;
|
|
|
|
while (handle->maps) {
|
|
if (handle->maps->map)
|
|
free_page((unsigned long)handle->maps->map);
|
|
tmp = handle->maps;
|
|
handle->maps = handle->maps->next;
|
|
kfree(tmp);
|
|
}
|
|
handle->cur = NULL;
|
|
}
|
|
|
|
static int get_swap_reader(struct swap_map_handle *handle,
|
|
unsigned int *flags_p)
|
|
{
|
|
int error;
|
|
struct swap_map_page_list *tmp, *last;
|
|
sector_t offset;
|
|
|
|
*flags_p = swsusp_header->flags;
|
|
|
|
if (!swsusp_header->image) /* how can this happen? */
|
|
return -EINVAL;
|
|
|
|
handle->cur = NULL;
|
|
last = handle->maps = NULL;
|
|
offset = swsusp_header->image;
|
|
while (offset) {
|
|
tmp = kzalloc(sizeof(*handle->maps), GFP_KERNEL);
|
|
if (!tmp) {
|
|
release_swap_reader(handle);
|
|
return -ENOMEM;
|
|
}
|
|
if (!handle->maps)
|
|
handle->maps = tmp;
|
|
if (last)
|
|
last->next = tmp;
|
|
last = tmp;
|
|
|
|
tmp->map = (struct swap_map_page *)
|
|
__get_free_page(GFP_NOIO | __GFP_HIGH);
|
|
if (!tmp->map) {
|
|
release_swap_reader(handle);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
error = hib_submit_io(REQ_OP_READ, offset, tmp->map, NULL);
|
|
if (error) {
|
|
release_swap_reader(handle);
|
|
return error;
|
|
}
|
|
offset = tmp->map->next_swap;
|
|
}
|
|
handle->k = 0;
|
|
handle->cur = handle->maps->map;
|
|
return 0;
|
|
}
|
|
|
|
static int swap_read_page(struct swap_map_handle *handle, void *buf,
|
|
struct hib_bio_batch *hb)
|
|
{
|
|
sector_t offset;
|
|
int error;
|
|
struct swap_map_page_list *tmp;
|
|
|
|
if (!handle->cur)
|
|
return -EINVAL;
|
|
offset = handle->cur->entries[handle->k];
|
|
if (!offset)
|
|
return -EFAULT;
|
|
error = hib_submit_io(REQ_OP_READ, offset, buf, hb);
|
|
if (error)
|
|
return error;
|
|
if (++handle->k >= MAP_PAGE_ENTRIES) {
|
|
handle->k = 0;
|
|
free_page((unsigned long)handle->maps->map);
|
|
tmp = handle->maps;
|
|
handle->maps = handle->maps->next;
|
|
kfree(tmp);
|
|
if (!handle->maps)
|
|
release_swap_reader(handle);
|
|
else
|
|
handle->cur = handle->maps->map;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int swap_reader_finish(struct swap_map_handle *handle)
|
|
{
|
|
release_swap_reader(handle);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* load_image - load the image using the swap map handle
|
|
* @handle and the snapshot handle @snapshot
|
|
* (assume there are @nr_pages pages to load)
|
|
*/
|
|
|
|
static int load_image(struct swap_map_handle *handle,
|
|
struct snapshot_handle *snapshot,
|
|
unsigned int nr_to_read)
|
|
{
|
|
unsigned int m;
|
|
int ret = 0;
|
|
ktime_t start;
|
|
ktime_t stop;
|
|
struct hib_bio_batch hb;
|
|
int err2;
|
|
unsigned nr_pages;
|
|
|
|
hib_init_batch(&hb);
|
|
|
|
clean_pages_on_read = true;
|
|
pr_info("Loading image data pages (%u pages)...\n", nr_to_read);
|
|
m = nr_to_read / 10;
|
|
if (!m)
|
|
m = 1;
|
|
nr_pages = 0;
|
|
start = ktime_get();
|
|
for ( ; ; ) {
|
|
ret = snapshot_write_next(snapshot);
|
|
if (ret <= 0)
|
|
break;
|
|
ret = swap_read_page(handle, data_of(*snapshot), &hb);
|
|
if (ret)
|
|
break;
|
|
if (snapshot->sync_read)
|
|
ret = hib_wait_io(&hb);
|
|
if (ret)
|
|
break;
|
|
if (!(nr_pages % m))
|
|
pr_info("Image loading progress: %3d%%\n",
|
|
nr_pages / m * 10);
|
|
nr_pages++;
|
|
}
|
|
err2 = hib_wait_io(&hb);
|
|
hib_finish_batch(&hb);
|
|
stop = ktime_get();
|
|
if (!ret)
|
|
ret = err2;
|
|
if (!ret) {
|
|
pr_info("Image loading done\n");
|
|
snapshot_write_finalize(snapshot);
|
|
if (!snapshot_image_loaded(snapshot))
|
|
ret = -ENODATA;
|
|
}
|
|
swsusp_show_speed(start, stop, nr_to_read, "Read");
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Structure used for data decompression.
|
|
*/
|
|
struct dec_data {
|
|
struct task_struct *thr; /* thread */
|
|
struct crypto_comp *cc; /* crypto compressor stream */
|
|
atomic_t ready; /* ready to start flag */
|
|
atomic_t stop; /* ready to stop flag */
|
|
int ret; /* return code */
|
|
wait_queue_head_t go; /* start decompression */
|
|
wait_queue_head_t done; /* decompression done */
|
|
size_t unc_len; /* uncompressed length */
|
|
size_t cmp_len; /* compressed length */
|
|
unsigned char unc[UNC_SIZE]; /* uncompressed buffer */
|
|
unsigned char cmp[CMP_SIZE]; /* compressed buffer */
|
|
};
|
|
|
|
/**
|
|
* Decompression function that runs in its own thread.
|
|
*/
|
|
static int decompress_threadfn(void *data)
|
|
{
|
|
struct dec_data *d = data;
|
|
unsigned int unc_len = 0;
|
|
|
|
while (1) {
|
|
wait_event(d->go, atomic_read_acquire(&d->ready) ||
|
|
kthread_should_stop());
|
|
if (kthread_should_stop()) {
|
|
d->thr = NULL;
|
|
d->ret = -1;
|
|
atomic_set_release(&d->stop, 1);
|
|
wake_up(&d->done);
|
|
break;
|
|
}
|
|
atomic_set(&d->ready, 0);
|
|
|
|
unc_len = UNC_SIZE;
|
|
d->ret = crypto_comp_decompress(d->cc, d->cmp + CMP_HEADER, d->cmp_len,
|
|
d->unc, &unc_len);
|
|
d->unc_len = unc_len;
|
|
|
|
if (clean_pages_on_decompress)
|
|
flush_icache_range((unsigned long)d->unc,
|
|
(unsigned long)d->unc + d->unc_len);
|
|
|
|
atomic_set_release(&d->stop, 1);
|
|
wake_up(&d->done);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* load_compressed_image - Load compressed image data and decompress it.
|
|
* @handle: Swap map handle to use for loading data.
|
|
* @snapshot: Image to copy uncompressed data into.
|
|
* @nr_to_read: Number of pages to load.
|
|
*/
|
|
static int load_compressed_image(struct swap_map_handle *handle,
|
|
struct snapshot_handle *snapshot,
|
|
unsigned int nr_to_read)
|
|
{
|
|
unsigned int m;
|
|
int ret = 0;
|
|
int eof = 0;
|
|
struct hib_bio_batch hb;
|
|
ktime_t start;
|
|
ktime_t stop;
|
|
unsigned nr_pages;
|
|
size_t off;
|
|
unsigned i, thr, run_threads, nr_threads;
|
|
unsigned ring = 0, pg = 0, ring_size = 0,
|
|
have = 0, want, need, asked = 0;
|
|
unsigned long read_pages = 0;
|
|
unsigned char **page = NULL;
|
|
struct dec_data *data = NULL;
|
|
struct crc_data *crc = NULL;
|
|
|
|
hib_init_batch(&hb);
|
|
|
|
/*
|
|
* We'll limit the number of threads for decompression to limit memory
|
|
* footprint.
|
|
*/
|
|
nr_threads = num_online_cpus() - 1;
|
|
nr_threads = clamp_val(nr_threads, 1, CMP_THREADS);
|
|
|
|
page = vmalloc(array_size(CMP_MAX_RD_PAGES, sizeof(*page)));
|
|
if (!page) {
|
|
pr_err("Failed to allocate %s page\n", hib_comp_algo);
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
data = vzalloc(array_size(nr_threads, sizeof(*data)));
|
|
if (!data) {
|
|
pr_err("Failed to allocate %s data\n", hib_comp_algo);
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
crc = kzalloc(sizeof(*crc), GFP_KERNEL);
|
|
if (!crc) {
|
|
pr_err("Failed to allocate crc\n");
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
clean_pages_on_decompress = true;
|
|
|
|
/*
|
|
* Start the decompression threads.
|
|
*/
|
|
for (thr = 0; thr < nr_threads; thr++) {
|
|
init_waitqueue_head(&data[thr].go);
|
|
init_waitqueue_head(&data[thr].done);
|
|
|
|
data[thr].cc = crypto_alloc_comp(hib_comp_algo, 0, 0);
|
|
if (IS_ERR_OR_NULL(data[thr].cc)) {
|
|
pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc));
|
|
ret = -EFAULT;
|
|
goto out_clean;
|
|
}
|
|
|
|
data[thr].thr = kthread_run(decompress_threadfn,
|
|
&data[thr],
|
|
"image_decompress/%u", thr);
|
|
if (IS_ERR(data[thr].thr)) {
|
|
data[thr].thr = NULL;
|
|
pr_err("Cannot start decompression threads\n");
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Start the CRC32 thread.
|
|
*/
|
|
init_waitqueue_head(&crc->go);
|
|
init_waitqueue_head(&crc->done);
|
|
|
|
handle->crc32 = 0;
|
|
crc->crc32 = &handle->crc32;
|
|
for (thr = 0; thr < nr_threads; thr++) {
|
|
crc->unc[thr] = data[thr].unc;
|
|
crc->unc_len[thr] = &data[thr].unc_len;
|
|
}
|
|
|
|
crc->thr = kthread_run(crc32_threadfn, crc, "image_crc32");
|
|
if (IS_ERR(crc->thr)) {
|
|
crc->thr = NULL;
|
|
pr_err("Cannot start CRC32 thread\n");
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
}
|
|
|
|
/*
|
|
* Set the number of pages for read buffering.
|
|
* This is complete guesswork, because we'll only know the real
|
|
* picture once prepare_image() is called, which is much later on
|
|
* during the image load phase. We'll assume the worst case and
|
|
* say that none of the image pages are from high memory.
|
|
*/
|
|
if (low_free_pages() > snapshot_get_image_size())
|
|
read_pages = (low_free_pages() - snapshot_get_image_size()) / 2;
|
|
read_pages = clamp_val(read_pages, CMP_MIN_RD_PAGES, CMP_MAX_RD_PAGES);
|
|
|
|
for (i = 0; i < read_pages; i++) {
|
|
page[i] = (void *)__get_free_page(i < CMP_PAGES ?
|
|
GFP_NOIO | __GFP_HIGH :
|
|
GFP_NOIO | __GFP_NOWARN |
|
|
__GFP_NORETRY);
|
|
|
|
if (!page[i]) {
|
|
if (i < CMP_PAGES) {
|
|
ring_size = i;
|
|
pr_err("Failed to allocate %s pages\n", hib_comp_algo);
|
|
ret = -ENOMEM;
|
|
goto out_clean;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
want = ring_size = i;
|
|
|
|
pr_info("Using %u thread(s) for %s decompression\n", nr_threads, hib_comp_algo);
|
|
pr_info("Loading and decompressing image data (%u pages)...\n",
|
|
nr_to_read);
|
|
m = nr_to_read / 10;
|
|
if (!m)
|
|
m = 1;
|
|
nr_pages = 0;
|
|
start = ktime_get();
|
|
|
|
ret = snapshot_write_next(snapshot);
|
|
if (ret <= 0)
|
|
goto out_finish;
|
|
|
|
for(;;) {
|
|
for (i = 0; !eof && i < want; i++) {
|
|
ret = swap_read_page(handle, page[ring], &hb);
|
|
if (ret) {
|
|
/*
|
|
* On real read error, finish. On end of data,
|
|
* set EOF flag and just exit the read loop.
|
|
*/
|
|
if (handle->cur &&
|
|
handle->cur->entries[handle->k]) {
|
|
goto out_finish;
|
|
} else {
|
|
eof = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (++ring >= ring_size)
|
|
ring = 0;
|
|
}
|
|
asked += i;
|
|
want -= i;
|
|
|
|
/*
|
|
* We are out of data, wait for some more.
|
|
*/
|
|
if (!have) {
|
|
if (!asked)
|
|
break;
|
|
|
|
ret = hib_wait_io(&hb);
|
|
if (ret)
|
|
goto out_finish;
|
|
have += asked;
|
|
asked = 0;
|
|
if (eof)
|
|
eof = 2;
|
|
}
|
|
|
|
if (crc->run_threads) {
|
|
wait_event(crc->done, atomic_read_acquire(&crc->stop));
|
|
atomic_set(&crc->stop, 0);
|
|
crc->run_threads = 0;
|
|
}
|
|
|
|
for (thr = 0; have && thr < nr_threads; thr++) {
|
|
data[thr].cmp_len = *(size_t *)page[pg];
|
|
if (unlikely(!data[thr].cmp_len ||
|
|
data[thr].cmp_len >
|
|
bytes_worst_compress(UNC_SIZE))) {
|
|
pr_err("Invalid %s compressed length\n", hib_comp_algo);
|
|
ret = -1;
|
|
goto out_finish;
|
|
}
|
|
|
|
need = DIV_ROUND_UP(data[thr].cmp_len + CMP_HEADER,
|
|
PAGE_SIZE);
|
|
if (need > have) {
|
|
if (eof > 1) {
|
|
ret = -1;
|
|
goto out_finish;
|
|
}
|
|
break;
|
|
}
|
|
|
|
for (off = 0;
|
|
off < CMP_HEADER + data[thr].cmp_len;
|
|
off += PAGE_SIZE) {
|
|
memcpy(data[thr].cmp + off,
|
|
page[pg], PAGE_SIZE);
|
|
have--;
|
|
want++;
|
|
if (++pg >= ring_size)
|
|
pg = 0;
|
|
}
|
|
|
|
atomic_set_release(&data[thr].ready, 1);
|
|
wake_up(&data[thr].go);
|
|
}
|
|
|
|
/*
|
|
* Wait for more data while we are decompressing.
|
|
*/
|
|
if (have < CMP_PAGES && asked) {
|
|
ret = hib_wait_io(&hb);
|
|
if (ret)
|
|
goto out_finish;
|
|
have += asked;
|
|
asked = 0;
|
|
if (eof)
|
|
eof = 2;
|
|
}
|
|
|
|
for (run_threads = thr, thr = 0; thr < run_threads; thr++) {
|
|
wait_event(data[thr].done,
|
|
atomic_read_acquire(&data[thr].stop));
|
|
atomic_set(&data[thr].stop, 0);
|
|
|
|
ret = data[thr].ret;
|
|
|
|
if (ret < 0) {
|
|
pr_err("%s decompression failed\n", hib_comp_algo);
|
|
goto out_finish;
|
|
}
|
|
|
|
if (unlikely(!data[thr].unc_len ||
|
|
data[thr].unc_len > UNC_SIZE ||
|
|
data[thr].unc_len & (PAGE_SIZE - 1))) {
|
|
pr_err("Invalid %s uncompressed length\n", hib_comp_algo);
|
|
ret = -1;
|
|
goto out_finish;
|
|
}
|
|
|
|
for (off = 0;
|
|
off < data[thr].unc_len; off += PAGE_SIZE) {
|
|
memcpy(data_of(*snapshot),
|
|
data[thr].unc + off, PAGE_SIZE);
|
|
|
|
if (!(nr_pages % m))
|
|
pr_info("Image loading progress: %3d%%\n",
|
|
nr_pages / m * 10);
|
|
nr_pages++;
|
|
|
|
ret = snapshot_write_next(snapshot);
|
|
if (ret <= 0) {
|
|
crc->run_threads = thr + 1;
|
|
atomic_set_release(&crc->ready, 1);
|
|
wake_up(&crc->go);
|
|
goto out_finish;
|
|
}
|
|
}
|
|
}
|
|
|
|
crc->run_threads = thr;
|
|
atomic_set_release(&crc->ready, 1);
|
|
wake_up(&crc->go);
|
|
}
|
|
|
|
out_finish:
|
|
if (crc->run_threads) {
|
|
wait_event(crc->done, atomic_read_acquire(&crc->stop));
|
|
atomic_set(&crc->stop, 0);
|
|
}
|
|
stop = ktime_get();
|
|
if (!ret) {
|
|
pr_info("Image loading done\n");
|
|
snapshot_write_finalize(snapshot);
|
|
if (!snapshot_image_loaded(snapshot))
|
|
ret = -ENODATA;
|
|
if (!ret) {
|
|
if (swsusp_header->flags & SF_CRC32_MODE) {
|
|
if(handle->crc32 != swsusp_header->crc32) {
|
|
pr_err("Invalid image CRC32!\n");
|
|
ret = -ENODATA;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
swsusp_show_speed(start, stop, nr_to_read, "Read");
|
|
out_clean:
|
|
hib_finish_batch(&hb);
|
|
for (i = 0; i < ring_size; i++)
|
|
free_page((unsigned long)page[i]);
|
|
if (crc) {
|
|
if (crc->thr)
|
|
kthread_stop(crc->thr);
|
|
kfree(crc);
|
|
}
|
|
if (data) {
|
|
for (thr = 0; thr < nr_threads; thr++) {
|
|
if (data[thr].thr)
|
|
kthread_stop(data[thr].thr);
|
|
if (data[thr].cc)
|
|
crypto_free_comp(data[thr].cc);
|
|
}
|
|
vfree(data);
|
|
}
|
|
vfree(page);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* swsusp_read - read the hibernation image.
|
|
* @flags_p: flags passed by the "frozen" kernel in the image header should
|
|
* be written into this memory location
|
|
*/
|
|
|
|
int swsusp_read(unsigned int *flags_p)
|
|
{
|
|
int error;
|
|
struct swap_map_handle handle;
|
|
struct snapshot_handle snapshot;
|
|
struct swsusp_info *header;
|
|
|
|
memset(&snapshot, 0, sizeof(struct snapshot_handle));
|
|
error = snapshot_write_next(&snapshot);
|
|
if (error < (int)PAGE_SIZE)
|
|
return error < 0 ? error : -EFAULT;
|
|
header = (struct swsusp_info *)data_of(snapshot);
|
|
error = get_swap_reader(&handle, flags_p);
|
|
if (error)
|
|
goto end;
|
|
if (!error)
|
|
error = swap_read_page(&handle, header, NULL);
|
|
if (!error) {
|
|
error = (*flags_p & SF_NOCOMPRESS_MODE) ?
|
|
load_image(&handle, &snapshot, header->pages - 1) :
|
|
load_compressed_image(&handle, &snapshot, header->pages - 1);
|
|
}
|
|
swap_reader_finish(&handle);
|
|
end:
|
|
if (!error)
|
|
pr_debug("Image successfully loaded\n");
|
|
else
|
|
pr_debug("Error %d resuming\n", error);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* swsusp_check - Check for swsusp signature in the resume device
|
|
*/
|
|
|
|
int swsusp_check(void)
|
|
{
|
|
int error;
|
|
void *holder;
|
|
fmode_t mode = FMODE_READ;
|
|
|
|
if (snapshot_test)
|
|
mode |= FMODE_EXCL;
|
|
|
|
hib_resume_bdev = blkdev_get_by_dev(swsusp_resume_device,
|
|
mode, &holder);
|
|
if (!IS_ERR(hib_resume_bdev)) {
|
|
set_blocksize(hib_resume_bdev, PAGE_SIZE);
|
|
trace_android_vh_save_hib_resume_bdev(hib_resume_bdev);
|
|
clear_page(swsusp_header);
|
|
error = hib_submit_io(REQ_OP_READ, swsusp_resume_block,
|
|
swsusp_header, NULL);
|
|
if (error)
|
|
goto put;
|
|
|
|
if (!memcmp(HIBERNATE_SIG, swsusp_header->sig, 10)) {
|
|
memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10);
|
|
swsusp_header_flags = swsusp_header->flags;
|
|
/* Reset swap signature now */
|
|
error = hib_submit_io(REQ_OP_WRITE | REQ_SYNC,
|
|
swsusp_resume_block,
|
|
swsusp_header, NULL);
|
|
} else {
|
|
error = -EINVAL;
|
|
}
|
|
if (!error && swsusp_header->flags & SF_HW_SIG &&
|
|
swsusp_header->hw_sig != swsusp_hardware_signature) {
|
|
pr_info("Suspend image hardware signature mismatch (%08x now %08x); aborting resume.\n",
|
|
swsusp_header->hw_sig, swsusp_hardware_signature);
|
|
error = -EINVAL;
|
|
}
|
|
|
|
put:
|
|
if (error)
|
|
blkdev_put(hib_resume_bdev, mode);
|
|
else
|
|
pr_debug("Image signature found, resuming\n");
|
|
} else {
|
|
error = PTR_ERR(hib_resume_bdev);
|
|
}
|
|
|
|
if (error)
|
|
pr_debug("Image not found (code %d)\n", error);
|
|
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* swsusp_close - close swap device.
|
|
*/
|
|
|
|
void swsusp_close(fmode_t mode)
|
|
{
|
|
if (IS_ERR(hib_resume_bdev)) {
|
|
pr_debug("Image device not initialised\n");
|
|
return;
|
|
}
|
|
|
|
blkdev_put(hib_resume_bdev, mode);
|
|
}
|
|
|
|
/**
|
|
* swsusp_unmark - Unmark swsusp signature in the resume device
|
|
*/
|
|
|
|
#ifdef CONFIG_SUSPEND
|
|
int swsusp_unmark(void)
|
|
{
|
|
int error;
|
|
|
|
hib_submit_io(REQ_OP_READ, swsusp_resume_block,
|
|
swsusp_header, NULL);
|
|
if (!memcmp(HIBERNATE_SIG,swsusp_header->sig, 10)) {
|
|
memcpy(swsusp_header->sig,swsusp_header->orig_sig, 10);
|
|
error = hib_submit_io(REQ_OP_WRITE | REQ_SYNC,
|
|
swsusp_resume_block,
|
|
swsusp_header, NULL);
|
|
} else {
|
|
pr_err("Cannot find swsusp signature!\n");
|
|
error = -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* We just returned from suspend, we don't need the image any more.
|
|
*/
|
|
free_all_swap_pages(root_swap);
|
|
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
static int __init swsusp_header_init(void)
|
|
{
|
|
swsusp_header = (struct swsusp_header*) __get_free_page(GFP_KERNEL);
|
|
if (!swsusp_header)
|
|
panic("Could not allocate memory for swsusp_header\n");
|
|
return 0;
|
|
}
|
|
|
|
core_initcall(swsusp_header_init);
|