Merge branch 'android15-6.6' into android15-6.6-lts

Catch up with changes that have happened only in android15-6.6.  Changes
in here include the following:

* 04612a82bc ANDROID: GKI: Update symbol list for honor
* 1faa5b23e4 ANDROID: GKI: Add vendor hook define for Honor ogki
* 9ddbcd8d87 ANDROID: ABI: Update symbol list for sunxi
* 3d3a7b6271 ANDROID: KVM: arm64: Do not advertise FEAT_MOPS for protected guests
* e05b2ba1f7 UPSTREAM: f2fs: allow F2FS_IPU_NOCACHE for pinned file
* dc69980b98 UPSTREAM: f2fs: forcibly migrate to secure space for zoned device file pinning
* 1c5c2abbdb UPSTREAM: f2fs: remove unused parameters
* 3e4be6b959 UPSTREAM: f2fs: fix to don't panic system for no free segment fault injection
* 03bdac4b14 UPSTREAM: f2fs: fix to don't set SB_RDONLY in f2fs_handle_critical_error()
* 8c9ffe678f UPSTREAM: f2fs: add valid block ratio not to do excessive GC for one time GC
* 9e1830d502 UPSTREAM: f2fs: create gc_no_zoned_gc_percent and gc_boost_zoned_gc_percent
* 9c62a56a52 UPSTREAM: f2fs: do FG_GC when GC boosting is required for zoned devices
* 49c4757ce5 UPSTREAM: f2fs: increase BG GC migration window granularity when boosted for zoned devices
* 0dda3520ca UPSTREAM: f2fs: add reserved_segments sysfs node
* f908bb9137 UPSTREAM: f2fs: introduce migration_window_granularity
* c94d3a8c89 UPSTREAM: f2fs: make BG GC more aggressive for zoned devices
* b66647b69c UPSTREAM: f2fs: avoid unused block when dio write in LFS mode
* 403c8bf1c7 UPSTREAM: f2fs: fix to check atomic_file in f2fs ioctl interfaces
* 9dfaa77227 UPSTREAM: f2fs: get rid of online repaire on corrupted directory
* 57244e8b0b UPSTREAM: f2fs: prevent atomic file from being dirtied before commit
* 28af1ec99b UPSTREAM: f2fs: get rid of page->index
* 9df793d8c1 UPSTREAM: f2fs: convert read_node_page() to use folio
* 1e7662c3e9 UPSTREAM: f2fs: convert __write_node_page() to use folio
* 097af71193 UPSTREAM: f2fs: convert f2fs_write_data_page() to use folio
* 80064f1aea UPSTREAM: f2fs: convert f2fs_do_write_data_page() to use folio
* 1d834ca736 UPSTREAM: f2fs: convert f2fs_set_compressed_page() to use folio
* bd5de9c701 UPSTREAM: f2fs: convert f2fs_write_end() to use folio
* ddcf5fda13 UPSTREAM: f2fs: convert f2fs_write_begin() to use folio
* 4da60115e5 UPSTREAM: f2fs: convert f2fs_submit_page_read() to use folio
* fe2ff89b78 UPSTREAM: f2fs: convert f2fs_handle_page_eio() to use folio
* 8a40506fc4 UPSTREAM: f2fs: convert f2fs_read_multi_pages() to use folio
* 05b6e0e2c0 UPSTREAM: f2fs: convert __f2fs_write_meta_page() to use folio
* 0dcbe06c00 UPSTREAM: f2fs: convert f2fs_do_write_meta_page() to use folio
* 76c1121bee UPSTREAM: f2fs: convert f2fs_write_single_data_page() to use folio
* 8000d9726a UPSTREAM: f2fs: convert f2fs_write_inline_data() to use folio
* f2b8a6eca7 UPSTREAM: f2fs: convert f2fs_clear_page_cache_dirty_tag() to use folio
* 7c6ac5c0b4 UPSTREAM: f2fs: convert f2fs_vm_page_mkwrite() to use folio
* dea3620404 UPSTREAM: f2fs: convert f2fs_compress_ctx_add_page() to use folio
* ce066dfd49 UPSTREAM: f2fs: Use sysfs_emit_at() to simplify code
* c77db905d4 UPSTREAM: f2fs: atomic: fix to forbid dio in atomic_file
* ba0bda6226 UPSTREAM: f2fs: compress: don't redirty sparse cluster during {,de}compress
* b954d95bb4 UPSTREAM: f2fs: check discard support for conventional zones
* 134062099a UPSTREAM: f2fs: fix to avoid use-after-free in f2fs_stop_gc_thread()
* f0e72502d1 UPSTREAM: f2fs: atomic: fix to truncate pagecache before on-disk metadata truncation
* ca6dd95d54 UPSTREAM: f2fs: Create COW inode from parent dentry for atomic write
* 3e77e46479 UPSTREAM: f2fs: Require FMODE_WRITE for atomic write ioctls
* e3babdc728 UPSTREAM: f2fs: clean up val{>>,<<}F2FS_BLKSIZE_BITS
* 9753fbec2b UPSTREAM: f2fs: fix to use per-inode maxbytes and cleanup
* fc2897c972 UPSTREAM: f2fs: use f2fs_get_node_page when write inline data
* 8c7904a3b4 UPSTREAM: f2fs: sysfs: support atgc_enabled
* a5a1c5ee53 UPSTREAM: Revert "f2fs: use flush command instead of FUA for zoned device"
* 8e550a95e3 UPSTREAM: f2fs: get rid of buffer_head use
* 220ce4c4c9 UPSTREAM: f2fs: fix to avoid racing in between read and OPU dio write
* 540abf1e48 UPSTREAM: f2fs: fix to wait dio completion
* 65ed348355 UPSTREAM: f2fs: reduce expensive checkpoint trigger frequency
* 60f02a0bbc UPSTREAM: f2fs: atomic: fix to avoid racing w/ GC
* aa2602f6ab UPSTREAM: f2fs: fix macro definition stat_inc_cp_count
* 62ea1b3848 UPSTREAM: f2fs: fix macro definition on_f2fs_build_free_nids
* 4474e4a21c UPSTREAM: f2fs: add write priority option based on zone UFS
* 2a9cd790f9 UPSTREAM: f2fs: avoid potential int overflow in sanity_check_area_boundary()
* 063568a957 UPSTREAM: f2fs: fix several potential integer overflows in file offsets
* bb83cf0c84 UPSTREAM: f2fs: prevent possible int overflow in dir_block_index()
* 58c6526f60 UPSTREAM: f2fs: clean up data_blkaddr() and get_dnode_addr()
* 8474472108 UPSTREAM: f2fs: convert f2fs_clear_page_cache_dirty_tag to use a folio
* 4e714a2053 ANDROID: 16K: Fixup padding vm_flags bits on VMA splits
* 7b43073a77 ANDROID: 16K: Introduce pgsize_migration_inline.h
* c8d33244c4 ANDROID: GKI: Update `kernel_aarch64_16k` build config to match `kernel_aarch64`
* 707399e528 ANDROID: GKI: Update Pixel symbols
* 028a21e577 ANDROID: drivers/arm-smmu-v3-kvm: Respect kernel allocation flags
* 09b18ba6f8 ANDROID: KVM: arm64: Add function to topup allocator with flags
* f056913fae FROMGIT: virtio_pmem: Check device status before requesting flush
* 0df607efb3 ANDROID: Shorten the length of line in kunit/android/README.
* 96533d6ac8 ANDROID: scsi: ufs: core: Add 36-bit DMA addressing support
* 9b212a6305 UPSTREAM: wifi: nl80211: Extend del pmksa support for SAE and OWE security
* 5d2a426f2e ANDROID: abi_gki_aarch64_qcom: Add rtc symbol for ms feature
* 0775e8627c ANDROID: ABI: update symbol list for honor
* d893627ae9 ANDROID: GKI: export some I/O tracepoints
* 7143f5fde9 ANDROID: GKI: Update unisoc symbol list
* 4cdfe8c8c4 Merge tag 'android15-6.6.50_r00' into android15-6.6
* 00b7784bb6 ANDROID: GKI: update mtktv symbol
* a3c4f110f5 UPSTREAM: USB: gadget: core: create sysfs link between udc and gadget
* ff50f45b4c ANDROID: KVM: arm64: iommu: Remove spurious plus ("+") sign
* 2c173456ce ANDROID: Update the ABI symbol list
* 6fce95550e ANDROID: GKI: Add symbol list for Pixel Watch
* 5555d4db7c ANDROID: abi_gki_aarch64_qcom: update abi symbol list
* c2942e5bcd ANDROID: Update symbol list for mtk
* ab4754de1c ANDROID: vendor_hooks: Add vendor hook for GenieZone demand paging
* 72c0d01fd0 ANDROID: GKI: update symbol list file for xiaomi
* 9b44f2fb79 ANDROID: vendor_hooks:add restricted hook for logbuf
* 6d7177caf9 Revert "FROMLIST: scsi: ufs: core: Fix the code for entering hibernation"
* 28620a2c5e ANDROID: GKI: vivo add symbols to symbol list
* 5596cd79a7 ANDROID: vendor hooks: skip mem reclaim throttle to speed up mem alloc
* 1fa94a1407 ANDROID: virt: gunyah: Correct the nr outval in gunyah_gup_share_parcel
* 1d520bc926 ANDROID: virt: gunyah: Unpin, not folio_put, in gunyah_gup_reclaim_parcel
* 6099865c80 ANDROID: abi_gki_aarch64_qcom: Update abi symbol list
* abfc3e16e5 FROMGIT: mm: shrink skip folio mapped by an exiting process
* e15d57b7fd FROMGIT: wifi: cfg80211: avoid overriding direct/MBSSID BSS with per-STA profile BSS
* 931c124949 FROMGIT: wifi: cfg80211: skip indicating signal for per-STA profile BSSs
* 91c873e8a2 FROMGIT: wifi: cfg80211: make BSS source types public
* 86047bcbd5 ANDROID: abi_gki_aarch64_honor: whitelist symbols added for abort scan by order
* 9d48e18a60 ANDROID: mm: add vendor hook to abort scan by order
* 4082972733 UPSTREAM: mm: add docs for per-order mTHP split counters
* 496306aa4d BACKPORT: mm: add per-order mTHP split counters
* a1704797ee ANDROID: dm-bow: Fix empty else blocks
* facb781dc9 ANDROID: dm-bow: Pass through zero sized requests
* 4b5ae75dd7 ANDROID: dm-bow: Fix 5.15 compatibility issue
* 221a0b451c ANDROID: dm-bow: Fix up revert remove dm-bow
* 31c501de4a Revert "ANDROID: dm-bow: remove dm-bow"
* a338a543b3 UPSTREAM: usb: dwc3: Avoid waking up gadget during startxfer
* cd188cc0b5 FROMGIT: dt-bindings: firmware: arm,scmi: Introduce property max-rx-timeout-ms
* 5f1d1fffd7 ANDROID: GKI: update mtktv symbol
* 44a4fadafc ANDROID: gki_config: Disable CONFIG_DEBUG_STACK_USAGE
* 15860879f7 ANDROID: abi_gki_aarch64_qcom: update abi symbol list
* 3fb778c361 FROMGIT: f2fs: fix to wait page writeback before setting gcing flag
* 63aeacfc81 BACKPORT: FROMGIT: ksmbd: make __dir_empty() compatible with POSIX
* 59a0888e3c ANDROID: GKI: update mtktv symbol
* f31bdfa6dc ANDROID: GKI: Update symbol list for vivo
* 5d0f897171 ANDROID: vendor_hooks: Add vendor hooks in __swap_writepage
* 1839b89cb9 ANDROID: vendor_hooks: Add hook in shrink_node_memcgs
* d9f1ddef3a ANDROID: abi_gki_aarch64_honor: whitelist symbols added for mglru shrink
* 4105548575 ANDROID: mm: add vendor_hook for mglru shrink
* c0e21888b2 ANDROID: GKI: Update symbol list for Amlogic
* bf54351c6c ANDROID: GKI: Add symbol to symbol list for imx
* f5648c7ab4 ANDROID: KVM: arm64: Add missing icache sync before pKVM module loading
* f67656b74b ANDROID: KVM: arm64: Fix kmemleak for pKVM modules
* 73613a1daf ANDROID: ABI: update symbol list for galaxy
* 8936b3531c ANDROID: mm: add vendor hook for compaction
* a7d1ee788f ANDROID: KVM: arm64: Fix CPU type for swap_reader_tracing HVC
* 2c26bdc6d9 ANDROID: abi_gki_aarch64_honor: whitelist symbols added for cma retries
* e1f8da20f9 ANDROID: mm/cma: add vendor_hook in cma_alloc for retries
* 16fdc0cae4 ANDROID: GKI: Add symbol to symbol list for imx
* 5b96da8071 BACKPORT: scsi: ufs: sysfs: Make max_number_of_rtt read-write
* 7a9edc1b85 BACKPORT: scsi: ufs: core: Maximum RTT supported by the host driver
* 4bf609a0ed BACKPORT: scsi: ufs: core: Allow RTT negotiation
* bf58cbe86d ANDROID: GKI: Update qcom symbol list
* 0c48cd9b7f ANDROID: GKI: Update symbol list for vivo
* 1a958c7b34 ANDROID: vendor_hooks: add hook for boost free pages mapped by the exiting process
* 23a331c4df ANDROID: GKI: Update symbol list for Amlogic
* 1c6d3468c4 ANDROID: gki_defconfig: Enable CONFIG_RTC_HCTOSYS for x86
* a32bc6101c FROMLIST: sd: Retry START STOP UNIT commands
* d1d8ed2618 FROMLIST: scsi: core: Retry passthrough commands if SCMD_RETRY_PASSTHROUGH is set
* f17de5d46e ANDROID: ABI: update symbol list for galaxy
* fe38da361c ANDROID: fs: add vendor hook in ksys_umount
* c94375dfb7 ANDROID: Update the ABI symbol list
* eec02eddcd ANDROID: binder: fix KMI issues due to frozen notification
* 170220eae8 FROMGIT: binder: frozen notification binder_features flag
* 1031c9cac6 FROMGIT: binder: frozen notification
* 646280f379 ANDROID: GKI: Enable HIBERNATION for GKI on arm64 and x86
* 1ac0f4daf1 ANDROID: Open damon sysfs config
* afc93244e6 UPSTREAM: mm/damon/sysfs: handle 'state' file inputs for every sampling interval if possible
* 829cb56c6f UPSTREAM: mm/damon/sysfs: avoid empty scheme tried regions for large apply interval
* f9fe08bdf5 UPSTREAM: mm/damon/sysfs-schemes: do not update tried regions more than one DAMON snapshot
* 9980217de9 UPSTREAM: wqmm/damon/sysfs-schemes: support DAMOS apply interval
* 80b60d5dfa UPSTREAM: mm/damon/core: implement scheme-specific apply interval
* 05c5781adf ANDROID: Add CtsIncrementalInstallHostTestCases to the kernel-presubmit group
* e4a1f6cb28 ANDROID: oplus: Update the ABI xml and symbol list
* 8c69374243 UPSTREAM: erofs: fix out-of-bound access when z_erofs_gbuf_growsize() partially fails
* f7078dd6b7 ANDROID: GKI: Update symbol list for vivo
* fdf9c08933 ANDROID: vendor_hooks: add hook for adjusting a more suitable watermark
* cf502a22fe UPSTREAM: usb: dwc3: core: Skip setting event buffers for host only controllers
* 921a6d1cc4 ANDROID: KVM: arm64: Return to the host if fault is handled by the IOMMU
* 1a751af48e FROMGIT: mm: vmalloc: ensure vmap_block is initialised before adding to queue
* ee7395c4cb ANDROID: GKI: enable CONFIG_TCP_CONG_BBR
* 071d0e62e8 UPSTREAM: mseal: fix is_madv_discard()
* 1e298487b7 ANDROID: GKI: Update qcom symbol list
* 5ab08bfd89 ANDROID: ABI: update symbol list for honor
* 8b43dcfbfa ANDROID: GKI: net: add vendor hook for network quality estimation
* 3a629fdc4a FROMLIST: usb: typec: fix up incorrectly backported "usb: typec: tcpm: unregister existing source caps before re-registration"
* 89bc0b3e94 UPSTREAM: net: sched: sch_multiq: fix possible OOB write in multiq_tune()
* 38f92c9eb9 BACKPORT: mm: fix crashes from deferred split racing folio migration
* cee1f33644 BACKPORT: mm: memcg: fix split queue list crash when large folio migration
* 204e871505 BACKPORT: memcontrol: only transfer the memcg data for migration
* 3cc9e23956 ANDROID: abi_gki_aarch64_vivo: Update symbol list
* 9f6bd03727 ANDROID: vendor_hooks: add hook in __mutex_unlock_slowpath()
* 8a0f09e1bc ANDROID: Add CtsLibcoreLegacy22TestCases and high percen coverage CtsCameraTestCases to the kernel-presubmit group
* 2aab18f88e ANDROID: ABI: Update pixel symbol list
* d7a7845d1b ANDROID: Export symbols to enable kunit tests
* c4b2c04485 ANDROID: Add padding to kunit_resource struct
* 7c9ee87357 UPSTREAM: exfat: convert exfat_find_empty_entry() to use dentry cache
* 3d65f275d1 UPSTREAM: exfat: convert exfat_init_ext_entry() to use dentry cache
* 60f4c79d0c UPSTREAM: exfat: move free cluster out of exfat_init_ext_entry()
* 4e8cfeff96 UPSTREAM: exfat: convert exfat_remove_entries() to use dentry cache
* e92f4015c9 UPSTREAM: exfat: convert exfat_add_entry() to use dentry cache
* 85fd296aa4 UPSTREAM:  exfat: add exfat_get_empty_dentry_set() helper
* 2feec0a6bc UPSTREAM: exfat: add __exfat_get_dentry_set() helper
* 46e92d59b6 UPSTREAM: exfat: move freeing sbi, upcase table and dropping nls into rcu-delayed helper
* eed2903ac1 UPSTREAM: exfat: fix appending discontinuous clusters to empty file
* 0bcb24636d UPSTREAM:  exfat: fix zero the unwritten part for dio read
* 26c23077d8 UPSTREAM: exfat: do not zero the extended part
* 83600dccb8 UPSTREAM: exfat: change to get file size from DataLength
* 1175cf316b UPSTREAM: exfat: using hweight instead of internal logic
* e2a390ccd4 UPSTREAM: exfat: support create zero-size directory
* 4cdb6f5a3c UPSTREAM: exfat: add ioctls for accessing attributes
* 5cc4dca606 UPSTREAM: exfat: convert to new timestamp accessors
* 3569b05351 ANDROID: fuse: Update file size in fuse_passthrough_splice_write

Change-Id: I44d2f763a1a6e5ed20a0ebd511c20e36f223b537
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman 2024-09-27 11:40:14 +00:00
commit 886869d11e
172 changed files with 16167 additions and 1138 deletions

View File

@ -106,11 +106,14 @@ filegroup(
"android/abi_gki_aarch64_galaxy",
"android/abi_gki_aarch64_honor",
"android/abi_gki_aarch64_imx",
"android/abi_gki_aarch64_kunit",
"android/abi_gki_aarch64_lenovo",
"android/abi_gki_aarch64_mtk",
"android/abi_gki_aarch64_mtktv",
"android/abi_gki_aarch64_nothing",
"android/abi_gki_aarch64_oplus",
"android/abi_gki_aarch64_pixel",
"android/abi_gki_aarch64_pixel_watch",
"android/abi_gki_aarch64_qcom",
"android/abi_gki_aarch64_sunxi",
"android/abi_gki_aarch64_tcl",
@ -141,10 +144,19 @@ define_common_kernels(target_configs = {
],
},
"kernel_aarch64_16k": {
"kmi_symbol_list_strict_mode": False,
"kmi_symbol_list_strict_mode": True,
"kmi_symbol_list": "android/abi_gki_aarch64",
"additional_kmi_symbol_lists": [":aarch64_additional_kmi_symbol_lists"],
"trim_nonlisted_kmi": True,
"protected_exports_list": "android/abi_gki_protected_exports_aarch64",
"protected_modules_list": "android/gki_aarch64_protected_modules",
"module_implicit_outs": get_gki_modules_list("arm64") + get_kunit_modules_list("arm64"),
"make_goals": _GKI_AARCH64_MAKE_GOALS,
"extra_dist": [":test_mappings_zip"],
"ddk_headers_archive": ":kernel_aarch64_ddk_headers_archive",
"extra_dist": [
":test_mappings_zip",
":tests_zip_arm64",
],
},
"kernel_x86_64": {
"kmi_symbol_list_strict_mode": False,

View File

@ -920,14 +920,16 @@ Description: This file shows whether the configuration descriptor is locked.
What: /sys/bus/platform/drivers/ufshcd/*/attributes/max_number_of_rtt
What: /sys/bus/platform/devices/*.ufs/attributes/max_number_of_rtt
Date: February 2018
Contact: Stanislav Nijnikov <stanislav.nijnikov@wdc.com>
Date: May 2024
Contact: Avri Altman <avri.altman@wdc.com>
Description: This file provides the maximum current number of
outstanding RTTs in device that is allowed. The full
information about the attribute could be found at
UFS specifications 2.1.
outstanding RTTs in device that is allowed. bMaxNumOfRTT is a
read-write persistent attribute and is equal to two after device
manufacturing. It shall not be set to a value greater than
bDeviceRTTCap value, and it may be set only when the hw queues are
empty.
The file is read only.
The file is read write.
What: /sys/bus/platform/drivers/ufshcd/*/attributes/exception_event_control
What: /sys/bus/platform/devices/*.ufs/attributes/exception_event_control

View File

@ -579,6 +579,12 @@ Description: When ATGC is on, it controls age threshold to bypass GCing young
candidates whose age is not beyond the threshold, by default it was
initialized as 604800 seconds (equals to 7 days).
What: /sys/fs/f2fs/<disk>/atgc_enabled
Date: Feb 2024
Contact: "Jinbao Liu" <liujinbao1@xiaomi.com>
Description: It represents whether ATGC is on or off. The value is 1 which
indicates that ATGC is on, and 0 indicates that it is off.
What: /sys/fs/f2fs/<disk>/gc_reclaimed_segments
Date: July 2021
Contact: "Daeho Jeong" <daehojeong@google.com>
@ -763,3 +769,53 @@ Date: November 2023
Contact: "Chao Yu" <chao@kernel.org>
Description: It controls to enable/disable IO aware feature for background discard.
By default, the value is 1 which indicates IO aware is on.
What: /sys/fs/f2fs/<disk>/blkzone_alloc_policy
Date: July 2024
Contact: "Yuanhong Liao" <liaoyuanhong@vivo.com>
Description: The zone UFS we are currently using consists of two parts:
conventional zones and sequential zones. It can be used to control which part
to prioritize for writes, with a default value of 0.
======================== =========================================
value description
blkzone_alloc_policy = 0 Prioritize writing to sequential zones
blkzone_alloc_policy = 1 Only allow writing to sequential zones
blkzone_alloc_policy = 2 Prioritize writing to conventional zones
======================== =========================================
What: /sys/fs/f2fs/<disk>/migration_window_granularity
Date: September 2024
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: Controls migration window granularity of garbage collection on large
section. it can control the scanning window granularity for GC migration
in a unit of segment, while migration_granularity controls the number
of segments which can be migrated at the same turn.
What: /sys/fs/f2fs/<disk>/reserved_segments
Date: September 2024
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: In order to fine tune GC behavior, we can control the number of
reserved segments.
What: /sys/fs/f2fs/<disk>/gc_no_zoned_gc_percent
Date: September 2024
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: If the percentage of free sections over total sections is above this
number, F2FS do not garbage collection for zoned devices through the
background GC thread. the default number is "60".
What: /sys/fs/f2fs/<disk>/gc_boost_zoned_gc_percent
Date: September 2024
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: If the percentage of free sections over total sections is under this
number, F2FS boosts garbage collection for zoned devices through the
background GC thread. the default number is "25".
What: /sys/fs/f2fs/<disk>/gc_valid_thresh_ratio
Date: September 2024
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: It controls the valid block ratio threshold not to trigger excessive GC
for zoned deivces. The initial value of it is 95(%). F2FS will stop the
background GC thread from intiating GC for sections having valid blocks
exceeding the ratio.

View File

@ -343,10 +343,6 @@ also applies to the regions registered in khugepaged.
Monitoring usage
================
.. note::
Currently the below counters only record events relating to
PMD-sized THP. Events relating to other THP sizes are not included.
The number of PMD-sized anonymous transparent huge pages currently used by the
system is available by reading the AnonHugePages field in ``/proc/meminfo``.
To identify what applications are using PMD-sized anonymous transparent huge
@ -475,6 +471,21 @@ swpout_fallback
Usually because failed to allocate some continuous swap space
for the huge page.
split
is incremented every time a huge page is successfully split into
smaller orders. This can happen for a variety of reasons but a
common reason is that a huge page is old and is being reclaimed.
split_failed
is incremented if kernel fails to split huge
page. This can happen if the page was pinned by somebody.
split_deferred
is incremented when a huge page is put onto split queue.
This happens when a huge page is partially unmapped and splitting
it would free up some memory. Pages on split queue are going to
be split under memory pressure, if splitting is possible.
As the system ages, allocating huge pages may be expensive as the
system uses memory compaction to copy data around memory to free a
huge page for use. There are some counters in ``/proc/vmstat`` to help

View File

@ -0,0 +1,99 @@
dm_bow (backup on write)
========================
dm_bow is a device mapper driver that uses the free space on a device to back up
data that is overwritten. The changes can then be committed by a simple state
change, or rolled back by removing the dm_bow device and running a command line
utility over the underlying device.
dm_bow has three states, set by writing 1 or 2 to /sys/block/dm-?/bow/state.
It is only possible to go from state 0 (initial state) to state 1, and then from
state 1 to state 2.
State 0: dm_bow collects all trims to the device and assumes that these mark
free space on the overlying file system that can be safely used. Typically the
mount code would create the dm_bow device, mount the file system, call the
FITRIM ioctl on the file system then switch to state 1. These trims are not
propagated to the underlying device.
State 1: All writes to the device cause the underlying data to be backed up to
the free (trimmed) area as needed in such a way as they can be restored.
However, the writes, with one exception, then happen exactly as they would
without dm_bow, so the device is always in a good final state. The exception is
that sector 0 is used to keep a log of the latest changes, both to indicate that
we are in this state and to allow rollback. See below for all details. If there
isn't enough free space, writes are failed with -ENOSPC.
State 2: The transition to state 2 triggers replacing the special sector 0 with
the normal sector 0, and the freeing of all state information. dm_bow then
becomes a pass-through driver, allowing the device to continue to be used with
minimal performance impact.
Usage
=====
dm-bow takes one command line parameter, the name of the underlying device.
dm-bow will typically be used in the following way. dm-bow will be loaded with a
suitable underlying device and the resultant device will be mounted. A file
system trim will be issued via the FITRIM ioctl, then the device will be
switched to state 1. The file system will now be used as normal. At some point,
the changes can either be committed by switching to state 2, or rolled back by
unmounting the file system, removing the dm-bow device and running the command
line utility. Note that rebooting the device will be equivalent to unmounting
and removing, but the command line utility must still be run
Details of operation in state 1
===============================
dm_bow maintains a type for all sectors. A sector can be any of:
SECTOR0
SECTOR0_CURRENT
UNCHANGED
FREE
CHANGED
BACKUP
SECTOR0 is the first sector on the device, and is used to hold the log of
changes. This is the one exception.
SECTOR0_CURRENT is a sector picked from the FREE sectors, and is where reads and
writes from the true sector zero are redirected to. Note that like any backup
sector, if the sector is written to directly, it must be moved again.
UNCHANGED means that the sector has not been changed since we entered state 1.
Thus if it is written to or trimmed, the contents must first be backed up.
FREE means that the sector was trimmed in state 0 and has not yet been written
to or used for backup. On being written to, a FREE sector is changed to CHANGED.
CHANGED means that the sector has been modified, and can be further modified
without further backup.
BACKUP means that this is a free sector being used as a backup. On being written
to, the contents must first be backed up again.
All backup operations are logged to the first sector. The log sector has the
format:
--------------------------------------------------------
| Magic | Count | Sequence | Log entry | Log entry | …
--------------------------------------------------------
Magic is a magic number. Count is the number of log entries. Sequence is 0
initially. A log entry is
-----------------------------------
| Source | Dest | Size | Checksum |
-----------------------------------
When SECTOR0 is full, the log sector is backed up and another empty log sector
created with sequence number one higher. The first entry in any log entry with
sequence > 0 therefore must be the log of the backing up of the previous log
sector. Note that sequence is not strictly needed, but is a useful sanity check
and potentially limits the time spent trying to restore a corrupted snapshot.
On entering state 1, dm_bow has a list of free sectors. All other sectors are
unchanged. Sector0_current is selected from the free sectors and the contents of
sector 0 are copied there. The sector 0 is backed up, which triggers the first
log entry to be written.

View File

@ -118,6 +118,13 @@ properties:
atomic mode of operation, even if requested.
default: 0
max-rx-timeout-ms:
description:
An optional time value, expressed in milliseconds, representing the
transport maximum timeout value for the receive channel. The value should
be a non-zero value if set.
minimum: 1
arm,smc-id:
$ref: /schemas/types.yaml#/definitions/uint32
description:

File diff suppressed because it is too large Load Diff

View File

@ -357,6 +357,7 @@
__devm_add_action
devm_alloc_etherdev_mqs
devm_blk_crypto_profile_init
devm_clk_bulk_get
devm_clk_bulk_get_all
devm_clk_get
devm_clk_get_optional
@ -435,6 +436,7 @@
dev_pm_domain_attach_by_id
dev_pm_domain_attach_by_name
dev_pm_domain_detach
dev_pm_genpd_set_performance_state
dev_pm_opp_find_freq_ceil
dev_pm_opp_free_cpufreq_table
dev_pm_opp_get_opp_count
@ -1200,6 +1202,7 @@
kset_unregister
kstrdup
kstrdup_const
kstrndup
kstrtobool
kstrtobool_from_user
kstrtoint
@ -1463,6 +1466,7 @@
of_get_phy_mode
of_get_property
of_get_regulator_init_data
of_get_required_opp_performance_state
of_graph_get_remote_node
of_graph_get_remote_port_parent
of_graph_is_present
@ -2075,6 +2079,7 @@
sock_release
sock_wfree
sort
spi_add_device
__spi_alloc_controller
spi_alloc_device
spi_controller_resume
@ -2088,6 +2093,7 @@
__spi_register_driver
spi_setup
spi_sync
spi_unregister_device
split_page
sprintf
sprint_symbol

View File

@ -73,6 +73,7 @@
__traceiter_android_rvh_do_el1_undef
__traceiter_android_rvh_do_sea
__traceiter_android_rvh_do_sp_pc_abort
__traceiter_android_rvh_ksys_umount
__traceiter_android_rvh_panic_unhandled
__traceiter_android_rvh_process_madvise_bypass
__traceiter_android_rvh_report_bug
@ -117,6 +118,7 @@
__traceiter_android_vh_smaps_pte_entry
__traceiter_android_vh_smaps_swap_shared
__traceiter_android_vh_show_smap_swap_shared
__traceiter_android_vh_suitable_migration_target_bypass
__traceiter_android_vh_split_large_folio_bypass
__traceiter_android_vh_try_to_freeze_todo
__traceiter_android_vh_try_to_freeze_todo_unfrozen
@ -141,6 +143,7 @@
__tracepoint_android_rvh_do_el1_undef
__tracepoint_android_rvh_do_sea
__tracepoint_android_rvh_do_sp_pc_abort
__tracepoint_android_rvh_ksys_umount
__tracepoint_android_rvh_panic_unhandled
__tracepoint_android_rvh_process_madvise_bypass
__tracepoint_android_rvh_report_bug
@ -186,6 +189,7 @@
__tracepoint_android_vh_smaps_swap_shared
__tracepoint_android_vh_show_smap_swap_shared
__tracepoint_android_vh_split_large_folio_bypass
__tracepoint_android_vh_suitable_migration_target_bypass
__tracepoint_android_vh_try_to_freeze_todo
__tracepoint_android_vh_try_to_freeze_todo_unfrozen
__tracepoint_android_vh_tune_scan_control

View File

@ -94,6 +94,10 @@
__traceiter_android_vh_slab_free
__traceiter_android_vh_should_fault_around
__tracepoint_android_vh_should_fault_around
__traceiter_android_vh_mglru_should_abort_scan
__tracepoint_android_vh_mglru_should_abort_scan
__traceiter_android_vh_mglru_should_abort_scan_order
__tracepoint_android_vh_mglru_should_abort_scan_order
__traceiter_android_vh_tcp_connect
__tracepoint_android_vh_tcp_connect
__traceiter_android_vh_tcp_write_timeout_estab_retrans
@ -104,6 +108,10 @@
__tracepoint_android_vh_tcp_clean_rtx_queue
__traceiter_android_vh_tcp_rcv_synack
__tracepoint_android_vh_tcp_rcv_synack
__traceiter_android_vh_udp_unicast_rcv_skb
__tracepoint_android_vh_udp_unicast_rcv_skb
__traceiter_android_vh_udp6_unicast_rcv_skb
__tracepoint_android_vh_udp6_unicast_rcv_skb
__tracepoint_android_vh_si_mem_available_adjust
__traceiter_android_vh_si_mem_available_adjust
__tracepoint_android_vh_si_meminfo_adjust
@ -138,11 +146,17 @@
__tracepoint_android_vh_sk_free
__traceiter_android_vh_sk_clone_lock
__tracepoint_android_vh_sk_clone_lock
__traceiter_android_vh_cma_alloc_retry
__tracepoint_android_vh_cma_alloc_retry
__traceiter_android_rvh_ogki_vfree_bypass
__traceiter_android_rvh_ogki_vmalloc_node_bypass
__traceiter_android_vh_ogki_async_psi_bypass
__traceiter_android_vh_ogki_f2fs_dsm
__traceiter_android_vh_ogki_f2fs_dsm_get
__tracepoint_android_vh_ogki_f2fs_create
__traceiter_android_vh_ogki_f2fs_create
__tracepoint_android_vh_ogki_f2fs_submit_write_page
__traceiter_android_vh_ogki_f2fs_submit_write_page
__traceiter_android_vh_ogki_check_vip_status
__traceiter_android_vh_ogki_cma_alloc_retry
__traceiter_android_vh_ogki_ufs_dsm
@ -154,6 +168,8 @@
__tracepoint_android_vh_ogki_check_vip_status
__tracepoint_android_vh_ogki_cma_alloc_retry
__tracepoint_android_vh_ogki_ufs_dsm
__tracepoint_scsi_dispatch_cmd_start
__traceiter_scsi_dispatch_cmd_start
__traceiter_android_vh_ogki_tcp_srtt_estimator
__tracepoint_android_vh_ogki_tcp_srtt_estimator
__traceiter_android_vh_ogki_tcp_rcv_estab_fastpath

View File

@ -412,6 +412,7 @@
devm_mipi_dsi_attach
devm_mipi_dsi_device_register_full
devm_nvmem_device_get
devm_nvmem_device_put
devm_nvmem_register
devm_of_clk_add_hw_provider
devm_of_iomap
@ -436,6 +437,7 @@
devm_rc_allocate_device
devm_rc_register_device
devm_register_sys_off_handler
devm_regmap_add_irq_chip
devm_regmap_field_alloc
__devm_regmap_init
__devm_regmap_init_i2c
@ -1904,6 +1906,7 @@
put_pid
__put_task_struct
put_unused_fd
pwm_apply_might_sleep
pwm_apply_state
pwmchip_add
pwmchip_remove
@ -1988,6 +1991,7 @@
regmap_field_update_bits_base
regmap_get_device
__regmap_init_mmio_clk
regmap_irq_get_domain
regmap_irq_get_virq
regmap_multi_reg_write
regmap_noinc_read
@ -2004,6 +2008,7 @@
regulator_bulk_enable
regulator_bulk_free
regulator_bulk_get
regulator_desc_list_voltage_linear
regulator_desc_list_voltage_linear_range
regulator_disable
regulator_disable_regmap
@ -2011,11 +2016,13 @@
regulator_enable_regmap
regulator_get_optional
regulator_get_voltage
regulator_get_voltage_sel_pickable_regmap
regulator_get_voltage_sel_regmap
regulator_is_enabled
regulator_is_enabled_regmap
regulator_list_voltage_linear
regulator_list_voltage_linear_range
regulator_list_voltage_pickable_linear_range
regulator_list_voltage_table
regulator_map_voltage_ascend
regulator_put
@ -2023,6 +2030,7 @@
regulator_set_load
regulator_set_ramp_delay_regmap
regulator_set_voltage
regulator_set_voltage_sel_pickable_regmap
regulator_set_voltage_sel_regmap
regulator_set_voltage_time_sel
release_firmware

View File

@ -0,0 +1,38 @@
[abi_symbol_list]
# required by drivers
kunit_hooks
kunit_running
# required by tests
__kunit_abort
__kunit_activate_static_stub
kunit_add_action
kunit_add_action_or_reset
__kunit_add_resource
kunit_assert_prologue
kunit_binary_assert_format
kunit_binary_ptr_assert_format
kunit_binary_str_assert_format
kunit_cleanup
kunit_deactivate_static_stub
kunit_destroy_resource
__kunit_do_failed_assertion
kunit_fail_assert_format
kunit_init_test
kunit_kfree
kunit_kmalloc_array
kunit_log_append
kunit_mem_assert_format
kunit_ptr_not_err_assert_format
kunit_release_action
kunit_remove_action
kunit_remove_resource
kunit_run_tests
kunit_suite_has_succeeded
kunit_suite_num_test_cases
kunit_test_case_num
__kunit_test_suites_exit
__kunit_test_suites_init
kunit_try_catch_run
kunit_try_catch_throw
kunit_unary_assert_format

View File

@ -2977,6 +2977,9 @@
__traceiter_android_vh_freq_qos_add_request
__traceiter_android_vh_freq_qos_remove_request
__traceiter_android_vh_freq_qos_update_request
__traceiter_android_vh_gzvm_destroy_vm_post_process
__traceiter_android_vh_gzvm_handle_demand_page_post
__traceiter_android_vh_gzvm_handle_demand_page_pre
__traceiter_android_vh_gzvm_vcpu_exit_reason
__traceiter_android_vh_iommu_iovad_alloc_iova
__traceiter_android_vh_iommu_iovad_free_iova
@ -3095,6 +3098,9 @@
__tracepoint_android_vh_freq_qos_add_request
__tracepoint_android_vh_freq_qos_remove_request
__tracepoint_android_vh_freq_qos_update_request
__tracepoint_android_vh_gzvm_destroy_vm_post_process
__tracepoint_android_vh_gzvm_handle_demand_page_post
__tracepoint_android_vh_gzvm_handle_demand_page_pre
__tracepoint_android_vh_gzvm_vcpu_exit_reason
__tracepoint_android_vh_iommu_iovad_alloc_iova
__tracepoint_android_vh_iommu_iovad_free_iova

File diff suppressed because it is too large Load Diff

View File

@ -519,3 +519,13 @@
xt_register_match
xt_unregister_match
zero_pfn
crypto_register_scomp
crypto_unregister_scomp
zstd_get_params
zstd_compress_cctx
zstd_is_error
zstd_decompress_dctx
zstd_cctx_workspace_bound
zstd_init_cctx
zstd_dctx_workspace_bound
zstd_init_dctx

View File

@ -42,6 +42,7 @@
argv_split
arm64_use_ng_mappings
__arm_smccc_smc
__arm_smccc_sve_check
async_schedule_node_domain
async_synchronize_full_domain
atomic_notifier_call_chain
@ -57,6 +58,7 @@
__bitmap_andnot
__bitmap_clear
__bitmap_equal
bitmap_find_free_region
bitmap_find_next_zero_area_off
bitmap_free
bitmap_from_arr32
@ -65,6 +67,7 @@
bitmap_parselist
bitmap_parse_user
bitmap_print_to_pagebuf
bitmap_release_region
__bitmap_set
bitmap_to_arr32
__bitmap_weight
@ -502,6 +505,7 @@
dma_direct_free
dmaengine_unmap_put
dma_fence_add_callback
dma_fence_allocate_private_stub
dma_fence_array_ops
dma_fence_context_alloc
dma_fence_default_wait
@ -857,6 +861,7 @@
fwnode_property_present
fwnode_property_read_string
fwnode_property_read_u32_array
fwnode_typec_mux_get
fwnode_typec_switch_get
gcd
generic_file_llseek
@ -1252,6 +1257,8 @@
kvfree
kvfree_call_rcu
kvmalloc_node
kvm_iommu_init_hyp
kvm_iommu_register_driver
led_classdev_register_ext
led_classdev_unregister
led_init_default_state_get
@ -1275,6 +1282,7 @@
mbox_controller_unregister
mbox_free_channel
mbox_request_channel
mbox_request_channel_byname
mbox_send_message
memchr
memchr_inv
@ -1576,8 +1584,10 @@
pktgen_xfrm_outer_mode_output
pkvm_iommu_resume
pkvm_iommu_suspend
__pkvm_load_el2_module
__pkvm_topup_hyp_alloc
__pkvm_topup_hyp_alloc_mgt
__pkvm_topup_hyp_alloc_mgt_gfp
platform_bus_type
platform_device_add
platform_device_add_data
@ -1669,7 +1679,12 @@
__put_task_struct
put_unused_fd
put_vaddr_frames
pwm_apply_might_sleep
pwm_apply_state
pwmchip_add
pwmchip_remove
pwm_get_chip_data
pwm_set_chip_data
queue_delayed_work_on
queue_work_on
radix_tree_insert
@ -2406,6 +2421,7 @@
tty_port_tty_get
typec_mux_get_drvdata
typec_mux_register
typec_mux_set
typec_switch_get_drvdata
typec_switch_register
typec_switch_set

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,9 @@
__balance_callbacks
balance_push_callback
bcmp
bio_endio
bio_end_io_acct_remapped
bio_start_io_acct
bitmap_allocate_region
__bitmap_andnot
__bitmap_clear
@ -58,11 +61,19 @@
bitmap_release_region
__bitmap_set
bitmap_zalloc
__blk_alloc_disk
blk_execute_rq_nowait
blk_get_queue
blk_mq_free_request
blk_mq_rq_cpu
blk_put_queue
blk_queue_flag_set
blk_queue_io_min
blk_queue_io_opt
blk_queue_logical_block_size
blk_queue_max_discard_sectors
blk_queue_max_write_zeroes_sectors
blk_queue_physical_block_size
blk_rq_map_user_io
blk_rq_unmap_user
blocking_notifier_call_chain
@ -105,14 +116,17 @@
check_preempt_curr
check_zeroed_user
class_create
class_create_file_ns
class_destroy
class_find_device
class_for_each_device
class_interface_unregister
class_register
class_remove_file_ns
class_unregister
cleanup_srcu_struct
clear_page
__ClearPageMovable
clk_bulk_disable
clk_bulk_enable
clk_bulk_prepare
@ -198,6 +212,8 @@
__cpuhp_remove_state
__cpuhp_setup_state
__cpuhp_setup_state_cpuslocked
__cpuhp_state_add_instance
__cpuhp_state_remove_instance
cpuidle_governor_latency_req
cpuidle_register_governor
cpu_irqtime
@ -229,9 +245,13 @@
crypto_ahash_setkey
crypto_alloc_aead
crypto_alloc_ahash
crypto_alloc_base
crypto_alloc_shash
crypto_alloc_skcipher
crypto_comp_compress
crypto_comp_decompress
crypto_destroy_tfm
crypto_has_alg
__crypto_memneq
crypto_register_aead
crypto_register_rng
@ -269,9 +289,11 @@
debugfs_remove
debugfs_rename
dec_node_page_state
dec_zone_page_state
default_llseek
deferred_free
delayed_work_timer_fn
del_gendisk
destroy_workqueue
dev_addr_mod
dev_alloc_name
@ -294,6 +316,7 @@
dev_get_regmap
dev_get_stats
device_add
device_add_disk
device_add_groups
device_create
device_create_file
@ -636,12 +659,14 @@
finish_wait
firmware_request_nowarn
flow_keys_dissector
flush_dcache_page
flush_delayed_fput
flush_delayed_work
flush_work
__flush_workqueue
__folio_batch_release
__folio_put
folio_wait_bit
fortify_panic
fput
free_io_pgtable_ops
@ -845,6 +870,7 @@
iio_write_channel_raw
in4_pton
inc_node_page_state
inc_zone_page_state
in_egroup_p
__inet6_lookup_established
inet_csk_get_port
@ -1106,6 +1132,7 @@
memmove
memory_block_size_bytes
memory_read_from_buffer
memparse
mempool_alloc
mempool_alloc_slab
mempool_create
@ -1115,6 +1142,7 @@
memremap
mem_section
memset
memset64
__memset_io
memstart_addr
memunmap
@ -1323,6 +1351,7 @@
panic_notifier_list
panic_timeout
param_array_ops
param_get_bool
param_get_int
param_get_string
param_get_uint
@ -1473,6 +1502,7 @@
proc_dointvec
proc_dointvec_minmax
proc_dostring
proc_douintvec
proc_douintvec_minmax
proc_set_user
proto_register
@ -1481,6 +1511,7 @@
__pskb_pull_tail
___pskb_trim
put_device
put_disk
put_iova_domain
__put_net
put_sg_io_hdr
@ -1549,6 +1580,7 @@
refcount_warn_saturate
regcache_mark_dirty
regcache_sync
__register_blkdev
__register_chrdev
register_chrdev_region
register_console
@ -1666,6 +1698,7 @@
rproc_report_crash
rproc_set_firmware
rproc_shutdown
rtc_add_group
rtc_time64_to_tm
rtc_tm_to_time64
rtc_update_irq
@ -1733,7 +1766,10 @@
seq_read
seq_release
seq_vprintf
set_capacity
set_capacity_and_notify
set_next_entity
__SetPageMovable
set_task_cpu
sg_alloc_table
sg_alloc_table_from_pages_segment
@ -1873,6 +1909,7 @@
stop_one_cpu_nowait
strcmp
strcpy
strcspn
strim
strlen
strncasecmp
@ -1893,6 +1930,7 @@
__sw_hweight32
__sw_hweight64
__sw_hweight8
sync_blockdev
synchronize_irq
synchronize_net
synchronize_rcu
@ -1986,6 +2024,7 @@
__traceiter_android_rvh_enqueue_task
__traceiter_android_rvh_find_busiest_queue
__traceiter_android_rvh_find_lowest_rq
__traceiter_android_rvh_find_new_ilb
__traceiter_android_rvh_flush_task
__traceiter_android_rvh_get_nohz_timer_target
__traceiter_android_rvh_gic_v3_set_affinity
@ -2089,6 +2128,7 @@
__tracepoint_android_rvh_enqueue_task
__tracepoint_android_rvh_find_busiest_queue
__tracepoint_android_rvh_find_lowest_rq
__tracepoint_android_rvh_find_new_ilb
__tracepoint_android_rvh_flush_task
__tracepoint_android_rvh_get_nohz_timer_target
__tracepoint_android_rvh_gic_v3_set_affinity
@ -2244,7 +2284,9 @@
ufshcd_uic_hibern8_exit
__uio_register_device
uio_unregister_device
unlock_page
unpin_user_pages
unregister_blkdev
__unregister_chrdev
unregister_chrdev_region
unregister_console

View File

@ -17,12 +17,18 @@
drm_mode_create_tv_properties_legacy
drm_mode_legacy_fb_format
drm_property_create_object
drm_vblank_work_init
drm_vblank_work_schedule
drm_vblank_work_cancel_sync
drm_vblank_work_flush
hid_hw_request
iio_map_array_register
iio_map_array_unregister
init_on_alloc
ioport_resource
irq_setup_alt_chip
ir_raw_event_store
ir_raw_event_handle
kstrtos16
ktime_add_safe
lookup_user_key
@ -51,6 +57,9 @@
stop_tty
clk_hw_register_clkdev
reset_control_status
rc_map_register
rc_map_unregister
rc_keydown
gpiochip_line_is_irq
drm_warn_on_modeset_not_all_locked
__media_entity_setup_link
@ -61,6 +70,7 @@
v4l2_i2c_new_subdev
vb2_dma_contig_set_max_seg_size
usb_serial_port_softint
usb_role_switch_get_role
ppp_channel_index
ppp_input
ppp_input_error

View File

@ -603,6 +603,12 @@
# required by sysdump.ko
node_states
# required by torture.ko
cpu_is_hotpluggable
ftrace_dump
rcu_cpu_stall_suppress
rcu_inkernel_boot_has_ended
# required by ufs_sprd.ko
__scsi_add_device
scsi_test_unit_ready

View File

@ -124,6 +124,7 @@
__traceiter_android_vh_configfs_uevent_work
__traceiter_android_vh_count_workingset_refault
__traceiter_android_vh_do_anonymous_page
__traceiter_android_vh_do_group_exit
__traceiter_android_vh_do_new_mount_fc
__traceiter_android_vh_do_swap_page
__traceiter_android_vh_do_wp_page
@ -134,6 +135,7 @@
__traceiter_android_vh_free_task
__traceiter_android_vh_free_unref_page_bypass
__traceiter_android_vh_fuse_request_end
__traceiter_android_vh_init_adjust_zone_wmark
__traceiter_android_vh_inode_lru_isolate
__traceiter_android_vh_invalidate_mapping_pagevec
__traceiter_android_vh_irqtime_account_process_tick
@ -145,6 +147,7 @@
__traceiter_android_vh_mmap_region
__traceiter_android_vh_mutex_init
__traceiter_android_vh_mutex_unlock_slowpath
__traceiter_android_vh_mutex_unlock_slowpath_before_wakeq
__traceiter_android_vh_mutex_wait_finish
__traceiter_android_vh_mutex_wait_start
__traceiter_android_vh_oom_swapmem_gather_finish
@ -171,14 +174,17 @@
__traceiter_android_vh_shmem_swapin_folio
__traceiter_android_vh_should_alloc_pages_retry
__traceiter_android_vh_shrink_folio_list
__traceiter_android_vh_shrink_node_memcgs
__traceiter_android_vh_sk_alloc
__traceiter_android_vh_sk_free
__traceiter_android_vh_swapmem_gather_add_bypass
__traceiter_android_vh_swapmem_gather_finish
__traceiter_android_vh_swapmem_gather_init
__traceiter_android_vh_swap_writepage
__traceiter_android_vh_sync_txn_recvd
__traceiter_android_vh_tcp_rtt_estimator
__traceiter_android_vh_test_clear_look_around_ref
__traceiter_android_vh_throttle_direct_reclaim_bypass
__traceiter_android_vh_try_to_unmap_one
__traceiter_android_vh_tune_mmap_readaround
__traceiter_android_vh_udp_enqueue_schedule_skb
@ -242,6 +248,7 @@
__tracepoint_android_vh_configfs_uevent_work
__tracepoint_android_vh_count_workingset_refault
__tracepoint_android_vh_do_anonymous_page
__tracepoint_android_vh_do_group_exit
__tracepoint_android_vh_do_new_mount_fc
__tracepoint_android_vh_do_swap_page
__tracepoint_android_vh_do_wp_page
@ -252,6 +259,7 @@
__tracepoint_android_vh_free_task
__tracepoint_android_vh_free_unref_page_bypass
__tracepoint_android_vh_fuse_request_end
__tracepoint_android_vh_init_adjust_zone_wmark
__tracepoint_android_vh_inode_lru_isolate
__tracepoint_android_vh_invalidate_mapping_pagevec
__tracepoint_android_vh_irqtime_account_process_tick
@ -263,6 +271,7 @@
__tracepoint_android_vh_mmap_region
__tracepoint_android_vh_mutex_init
__tracepoint_android_vh_mutex_unlock_slowpath
__tracepoint_android_vh_mutex_unlock_slowpath_before_wakeq
__tracepoint_android_vh_mutex_wait_finish
__tracepoint_android_vh_mutex_wait_start
__tracepoint_android_vh_oom_swapmem_gather_finish
@ -289,14 +298,17 @@
__tracepoint_android_vh_shmem_swapin_folio
__tracepoint_android_vh_should_alloc_pages_retry
__tracepoint_android_vh_shrink_folio_list
__tracepoint_android_vh_shrink_node_memcgs
__tracepoint_android_vh_sk_alloc
__tracepoint_android_vh_sk_free
__tracepoint_android_vh_swapmem_gather_add_bypass
__tracepoint_android_vh_swapmem_gather_finish
__tracepoint_android_vh_swapmem_gather_init
__tracepoint_android_vh_swap_writepage
__tracepoint_android_vh_sync_txn_recvd
__tracepoint_android_vh_tcp_rtt_estimator
__tracepoint_android_vh_test_clear_look_around_ref
__tracepoint_android_vh_throttle_direct_reclaim_bypass
__tracepoint_android_vh_try_to_unmap_one
__tracepoint_android_vh_tune_mmap_readaround
__tracepoint_android_vh_udp_enqueue_schedule_skb
@ -319,3 +331,4 @@
ufshcd_query_descriptor_retry
unlock_buffer
__wait_on_buffer
zs_lookup_class_index

View File

@ -1,5 +1,7 @@
[abi_symbol_list]
# commonly used symbols
__traceiter_android_rvh_logbuf
__tracepoint_android_rvh_logbuf
__traceiter_android_vh_logbuf
__tracepoint_android_vh_logbuf
tracepoint_probe_register

View File

@ -104,6 +104,7 @@ hci_cmd_sync
hci_cmd_sync_cancel
hci_cmd_sync_cancel_sync
hci_cmd_sync_queue
hci_cmd_sync_status
hci_cmd_sync_submit
hci_conn_check_secure
hci_conn_security

View File

@ -70,6 +70,7 @@ CONFIG_UNWIND_PATCH_PAC_INTO_SCS=y
CONFIG_CMDLINE="console=ttynull stack_depot_disable=on cgroup_disable=pressure kasan.stacktrace=off kvm-arm.mode=protected bootconfig ioremap_guard"
CONFIG_CMDLINE_EXTEND=y
# CONFIG_DMI is not set
CONFIG_HIBERNATION=y
CONFIG_PM_WAKELOCKS=y
CONFIG_PM_WAKELOCKS_LIMIT=0
# CONFIG_PM_WAKELOCKS_GC is not set
@ -131,6 +132,9 @@ CONFIG_ANON_VMA_NAME=y
CONFIG_USERFAULTFD=y
CONFIG_LRU_GEN=y
CONFIG_LRU_GEN_ENABLED=y
CONFIG_DAMON=y
CONFIG_DAMON_VADDR=y
CONFIG_DAMON_SYSFS=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_XFRM_USER=y
@ -149,6 +153,11 @@ CONFIG_NET_IPVTI=y
CONFIG_INET_ESP=y
CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_TCP_CONG_ADVANCED=y
# CONFIG_TCP_CONG_BIC is not set
# CONFIG_TCP_CONG_WESTWOOD is not set
# CONFIG_TCP_CONG_HTCP is not set
CONFIG_TCP_CONG_BBR=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
@ -339,6 +348,7 @@ CONFIG_DM_SNAPSHOT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
CONFIG_DM_BOW=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_WIREGUARD=y
@ -752,7 +762,6 @@ CONFIG_UBSAN_TRAP=y
# CONFIG_UBSAN_ENUM is not set
CONFIG_PAGE_OWNER=y
CONFIG_PAGE_PINNER=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_KASAN=y
CONFIG_KASAN_HW_TAGS=y

View File

@ -154,6 +154,8 @@ static inline void __free_hyp_memcache(struct kvm_hyp_memcache *mc,
void free_hyp_memcache(struct kvm_hyp_memcache *mc);
int topup_hyp_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages, unsigned long order);
int topup_hyp_memcache_gfp(struct kvm_hyp_memcache *mc, unsigned long min_pages,
unsigned long order, gfp_t gfp);
static inline void init_hyp_memcache(struct kvm_hyp_memcache *mc)
{
@ -1346,5 +1348,6 @@ int kvm_iommu_register_driver(struct kvm_iommu_driver *kern_ops);
unsigned long __pkvm_reclaim_hyp_alloc_mgt(unsigned long nr_pages);
int __pkvm_topup_hyp_alloc_mgt(unsigned long id, unsigned long nr_pages,
unsigned long sz_alloc);
int __pkvm_topup_hyp_alloc_mgt_gfp(unsigned long id, unsigned long nr_pages,
unsigned long sz_alloc, gfp_t gfp);
#endif /* __ARM64_KVM_HOST_H__ */

View File

@ -236,7 +236,6 @@ void pkvm_host_reclaim_page(struct kvm *host_kvm, phys_addr_t ipa);
#define PVM_ID_AA64ISAR2_ALLOW (\
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_ATS1A) | \
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3) | \
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_MOPS) | \
FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3), ID_AA64ISAR2_EL1_APA3_PAuth) \
)

View File

@ -32,7 +32,7 @@ struct pkvm_el2_module {
int (*init)(const struct pkvm_module_ops *ops);
};
void kvm_apply_hyp_module_relocations(void *mod_start, void *hyp_va,
void kvm_apply_hyp_module_relocations(struct pkvm_el2_module *mod,
kvm_nvhe_reloc_t *begin,
kvm_nvhe_reloc_t *end);

View File

@ -31,7 +31,7 @@ int register_hyp_event_ids(unsigned long start, unsigned long end);
int __pkvm_load_tracing(unsigned long desc_va, size_t desc_size);
void __pkvm_teardown_tracing(void);
int __pkvm_enable_tracing(bool enable);
int __pkvm_swap_reader_tracing(int cpu);
int __pkvm_swap_reader_tracing(unsigned int cpu);
int __pkvm_enable_event(unsigned short id, bool enable);
extern char __hyp_printk_fmts_start[];
@ -81,7 +81,7 @@ static inline int register_hyp_event_ids(unsigned long start, unsigned long end)
static inline int __pkvm_load_tracing(unsigned long desc_va, size_t desc_size) { return -ENODEV; }
static inline void __pkvm_teardown_tracing(void) { }
static inline int __pkvm_enable_tracing(bool enable) { return -ENODEV; }
static inline int __pkvm_swap_reader_tracing(int cpu) { return -ENODEV; }
static inline int __pkvm_swap_reader_tracing(unsigned int cpu) { return -ENODEV; }
static inline int __pkvm_enable_event(unsigned short id, bool enable) { return -ENODEV; }
#define trace_hyp_printk(fmt, ...)
#endif

View File

@ -1411,7 +1411,7 @@ static void handle___pkvm_enable_tracing(struct kvm_cpu_context *host_ctxt)
static void handle___pkvm_swap_reader_tracing(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(int, cpu, host_ctxt, 1);
DECLARE_REG(unsigned int, cpu, host_ctxt, 1);
cpu_reg(host_ctxt, 1) = __pkvm_swap_reader_tracing(cpu);
}

View File

@ -944,7 +944,7 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt)
if (is_dabt(esr) && !addr_is_memory(addr) &&
kvm_iommu_host_dabt_handler(host_ctxt, esr, addr))
ret = 0;
goto return_to_host;
/* If not handled, attempt to map the page. */
if (ret == -EPERM)

View File

@ -424,9 +424,9 @@ err:
return ret;
}
int __pkvm_swap_reader_tracing(int cpu)
int __pkvm_swap_reader_tracing(unsigned int cpu)
{
struct hyp_rb_per_cpu *cpu_buffer = per_cpu_ptr(&trace_rb, cpu);
struct hyp_rb_per_cpu *cpu_buffer;
int ret = 0;
hyp_spin_lock(&trace_rb_lock);

View File

@ -1177,6 +1177,11 @@ static void *hyp_mc_alloc_fn(void *flags, unsigned long order)
return addr;
}
static void *hyp_mc_alloc_gfp_fn(void *flags, unsigned long order)
{
return (void *)__get_free_pages(*(gfp_t *)flags, order);
}
void free_hyp_memcache(struct kvm_hyp_memcache *mc)
{
unsigned long flags = mc->flags;
@ -1203,6 +1208,21 @@ int topup_hyp_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages,
}
EXPORT_SYMBOL(topup_hyp_memcache);
int topup_hyp_memcache_gfp(struct kvm_hyp_memcache *mc, unsigned long min_pages,
unsigned long order, gfp_t gfp)
{
void *flags = &gfp;
if (!is_protected_kvm_enabled())
return 0;
if (order > PAGE_SHIFT)
return -E2BIG;
return __topup_hyp_memcache(mc, min_pages, hyp_mc_alloc_gfp_fn,
kvm_host_pa, flags, order);
}
/**
* kvm_phys_addr_ioremap - map a device range to guest IPA
*

View File

@ -970,13 +970,7 @@ int __pkvm_load_el2_module(struct module *this, unsigned long *token)
mod->sections.end = end;
endrel = (void *)mod->relocs + mod->nr_relocs * sizeof(*endrel);
kvm_apply_hyp_module_relocations(start, hyp_va, mod->relocs, endrel);
/*
* Exclude EL2 module sections from kmemleak before making them
* inaccessible.
*/
kmemleak_free_part(start, size);
kvm_apply_hyp_module_relocations(mod, mod->relocs, endrel);
ret = hyp_trace_init_mod_events(mod->hyp_events,
mod->event_ids.start,
@ -984,6 +978,17 @@ int __pkvm_load_el2_module(struct module *this, unsigned long *token)
if (ret)
kvm_err("Failed to init module events: %d\n", ret);
/*
* Sadly we have also to disable kmemleak for EL1 sections: we can't
* reset created scan area and therefore we can't create a finer grain
* scan excluding only EL2 sections.
*/
if (this) {
kmemleak_no_scan(this->mem[MOD_TEXT].base);
kmemleak_no_scan(this->mem[MOD_DATA].base);
kmemleak_no_scan(this->mem[MOD_RODATA].base);
}
ret = pkvm_map_module_sections(secs_map + secs_first, hyp_va,
ARRAY_SIZE(secs_map) - secs_first);
if (ret) {
@ -1014,14 +1019,15 @@ int __pkvm_register_el2_call(unsigned long hfn_hyp_va)
EXPORT_SYMBOL(__pkvm_register_el2_call);
#endif /* CONFIG_MODULES */
int __pkvm_topup_hyp_alloc_mgt(unsigned long id, unsigned long nr_pages, unsigned long sz_alloc)
int __pkvm_topup_hyp_alloc_mgt_gfp(unsigned long id, unsigned long nr_pages,
unsigned long sz_alloc, gfp_t gfp)
{
struct kvm_hyp_memcache mc;
int ret;
init_hyp_memcache(&mc);
ret = topup_hyp_memcache(&mc, nr_pages, get_order(sz_alloc));
ret = topup_hyp_memcache_gfp(&mc, nr_pages, get_order(sz_alloc), gfp);
if (ret)
return ret;
@ -1032,6 +1038,12 @@ int __pkvm_topup_hyp_alloc_mgt(unsigned long id, unsigned long nr_pages, unsigne
return ret;
}
EXPORT_SYMBOL(__pkvm_topup_hyp_alloc_mgt_gfp);
int __pkvm_topup_hyp_alloc_mgt(unsigned long id, unsigned long nr_pages, unsigned long sz_alloc)
{
return __pkvm_topup_hyp_alloc_mgt_gfp(id, nr_pages, sz_alloc, GFP_KERNEL);
}
EXPORT_SYMBOL(__pkvm_topup_hyp_alloc_mgt);
int __pkvm_topup_hyp_alloc(unsigned long nr_pages)

View File

@ -110,10 +110,11 @@ __init void kvm_apply_hyp_relocations(void)
}
}
void kvm_apply_hyp_module_relocations(void *mod_start, void *hyp_va,
void kvm_apply_hyp_module_relocations(struct pkvm_el2_module *mod,
kvm_nvhe_reloc_t *begin,
kvm_nvhe_reloc_t *end)
{
void *hyp_va = (void *)mod->token;
kvm_nvhe_reloc_t *rel;
for (rel = begin; rel < end; ++rel) {
@ -130,8 +131,11 @@ void kvm_apply_hyp_module_relocations(void *mod_start, void *hyp_va,
/* Convert the module VA of the reloc to a hyp VA */
WARN_ON(aarch64_insn_write_literal_u64(ptr,
(u64)(((void *)va - mod_start) + hyp_va)));
(u64)(((void *)va - mod->sections.start) + hyp_va)));
}
sync_icache_aliases((unsigned long)mod->text.start,
(unsigned long)mod->text.end);
}
static u32 compute_instruction(int n, u32 rd, u32 rn)

View File

@ -65,6 +65,7 @@ CONFIG_NR_CPUS=32
CONFIG_EFI=y
CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="console=ttynull stack_depot_disable=on cgroup_disable=pressure bootconfig"
CONFIG_HIBERNATION=y
CONFIG_PM_WAKELOCKS=y
CONFIG_PM_WAKELOCKS_LIMIT=0
# CONFIG_PM_WAKELOCKS_GC is not set
@ -122,6 +123,9 @@ CONFIG_ANON_VMA_NAME=y
CONFIG_USERFAULTFD=y
CONFIG_LRU_GEN=y
CONFIG_LRU_GEN_ENABLED=y
CONFIG_DAMON=y
CONFIG_DAMON_VADDR=y
CONFIG_DAMON_SYSFS=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_XFRM_USER=y
@ -140,6 +144,11 @@ CONFIG_NET_IPVTI=y
CONFIG_INET_ESP=y
CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_TCP_CONG_ADVANCED=y
# CONFIG_TCP_CONG_BIC is not set
# CONFIG_TCP_CONG_WESTWOOD is not set
# CONFIG_TCP_CONG_HTCP is not set
CONFIG_TCP_CONG_BBR=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
@ -319,6 +328,7 @@ CONFIG_DM_SNAPSHOT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
CONFIG_DM_BOW=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_WIREGUARD=y
@ -520,7 +530,6 @@ CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_TRANSIENT=y
CONFIG_EDAC=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_HCTOSYS is not set
CONFIG_RTC_LIB_KUNIT_TEST=m
CONFIG_DMABUF_HEAPS=y
CONFIG_DMABUF_SYSFS_STATS=y
@ -684,7 +693,6 @@ CONFIG_UBSAN_TRAP=y
# CONFIG_UBSAN_ENUM is not set
CONFIG_PAGE_OWNER=y
CONFIG_PAGE_PINNER=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_KFENCE=y
CONFIG_KFENCE_SAMPLE_INTERVAL=500

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -255,5 +255,15 @@
}
]
}
],
"kernel-presubmit": [
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
}
]
}

View File

@ -272,6 +272,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -1533,6 +1533,7 @@ static void binder_free_ref(struct binder_ref *ref)
if (ref->node)
binder_free_node(ref->node);
kfree(ref->death);
kfree(ref->freeze);
kfree(ref);
}
@ -4064,6 +4065,154 @@ err_invalid_target_handle:
}
}
static int
binder_request_freeze_notification(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_handle_cookie *handle_cookie)
{
struct binder_ref_freeze *freeze;
struct binder_ref *ref;
bool is_frozen;
freeze = kzalloc(sizeof(*freeze), GFP_KERNEL);
if (!freeze)
return -ENOMEM;
binder_proc_lock(proc);
ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
if (!ref) {
binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION invalid ref %d\n",
proc->pid, thread->pid, handle_cookie->handle);
binder_proc_unlock(proc);
kfree(freeze);
return -EINVAL;
}
binder_node_lock(ref->node);
if (ref->freeze || !ref->node->proc) {
binder_user_error("%d:%d invalid BC_REQUEST_FREEZE_NOTIFICATION %s\n",
proc->pid, thread->pid,
ref->freeze ? "already set" : "dead node");
binder_node_unlock(ref->node);
binder_proc_unlock(proc);
kfree(freeze);
return -EINVAL;
}
binder_inner_proc_lock(ref->node->proc);
is_frozen = ref->node->proc->is_frozen;
binder_inner_proc_unlock(ref->node->proc);
INIT_LIST_HEAD(&freeze->work.entry);
freeze->cookie = handle_cookie->cookie;
freeze->work.type = BINDER_WORK_FROZEN_BINDER;
freeze->is_frozen = is_frozen;
ref->freeze = freeze;
binder_inner_proc_lock(proc);
binder_enqueue_work_ilocked(&ref->freeze->work, &proc->todo);
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
binder_node_unlock(ref->node);
binder_proc_unlock(proc);
return 0;
}
static int
binder_clear_freeze_notification(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_handle_cookie *handle_cookie)
{
struct binder_ref_freeze *freeze;
struct binder_ref *ref;
binder_proc_lock(proc);
ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
if (!ref) {
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION invalid ref %d\n",
proc->pid, thread->pid, handle_cookie->handle);
binder_proc_unlock(proc);
return -EINVAL;
}
binder_node_lock(ref->node);
if (!ref->freeze) {
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n",
proc->pid, thread->pid);
binder_node_unlock(ref->node);
binder_proc_unlock(proc);
return -EINVAL;
}
freeze = ref->freeze;
binder_inner_proc_lock(proc);
if (freeze->cookie != handle_cookie->cookie) {
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch %016llx != %016llx\n",
proc->pid, thread->pid, (u64)freeze->cookie,
(u64)handle_cookie->cookie);
binder_inner_proc_unlock(proc);
binder_node_unlock(ref->node);
binder_proc_unlock(proc);
return -EINVAL;
}
ref->freeze = NULL;
/*
* Take the existing freeze object and overwrite its work type. There are three cases here:
* 1. No pending notification. In this case just add the work to the queue.
* 2. A notification was sent and is pending an ack from userspace. Once an ack arrives, we
* should resend with the new work type.
* 3. A notification is pending to be sent. Since the work is already in the queue, nothing
* needs to be done here.
*/
freeze->work.type = BINDER_WORK_CLEAR_FREEZE_NOTIFICATION;
if (list_empty(&freeze->work.entry)) {
binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
binder_wakeup_proc_ilocked(proc);
} else if (freeze->sent) {
freeze->resend = true;
}
binder_inner_proc_unlock(proc);
binder_node_unlock(ref->node);
binder_proc_unlock(proc);
return 0;
}
static int
binder_freeze_notification_done(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t cookie)
{
struct binder_ref_freeze *freeze = NULL;
struct binder_work *w;
binder_inner_proc_lock(proc);
list_for_each_entry(w, &proc_wrapper(proc)->delivered_freeze, entry) {
struct binder_ref_freeze *tmp_freeze =
container_of(w, struct binder_ref_freeze, work);
if (tmp_freeze->cookie == cookie) {
freeze = tmp_freeze;
break;
}
}
if (!freeze) {
binder_user_error("%d:%d BC_FREEZE_NOTIFICATION_DONE %016llx not found\n",
proc->pid, thread->pid, (u64)cookie);
binder_inner_proc_unlock(proc);
return -EINVAL;
}
binder_dequeue_work_ilocked(&freeze->work);
freeze->sent = false;
if (freeze->resend) {
freeze->resend = false;
binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
binder_wakeup_proc_ilocked(proc);
}
binder_inner_proc_unlock(proc);
return 0;
}
/**
* binder_free_buf() - free the specified buffer
* @proc: binder proc that owns buffer
@ -4557,6 +4706,44 @@ static int binder_thread_write(struct binder_proc *proc,
binder_inner_proc_unlock(proc);
} break;
case BC_REQUEST_FREEZE_NOTIFICATION: {
struct binder_handle_cookie handle_cookie;
int error;
if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
return -EFAULT;
ptr += sizeof(handle_cookie);
error = binder_request_freeze_notification(proc, thread,
&handle_cookie);
if (error)
return error;
} break;
case BC_CLEAR_FREEZE_NOTIFICATION: {
struct binder_handle_cookie handle_cookie;
int error;
if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
return -EFAULT;
ptr += sizeof(handle_cookie);
error = binder_clear_freeze_notification(proc, thread, &handle_cookie);
if (error)
return error;
} break;
case BC_FREEZE_NOTIFICATION_DONE: {
binder_uintptr_t cookie;
int error;
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(cookie);
error = binder_freeze_notification_done(proc, thread, cookie);
if (error)
return error;
} break;
default:
pr_err("%d:%d unknown command %u\n",
proc->pid, thread->pid, cmd);
@ -4959,6 +5146,45 @@ skip:
if (cmd == BR_DEAD_BINDER)
goto done; /* DEAD_BINDER notifications can cause transactions */
} break;
case BINDER_WORK_FROZEN_BINDER: {
struct binder_ref_freeze *freeze;
struct binder_frozen_state_info info;
memset(&info, 0, sizeof(info));
freeze = container_of(w, struct binder_ref_freeze, work);
info.is_frozen = freeze->is_frozen;
info.cookie = freeze->cookie;
freeze->sent = true;
binder_enqueue_work_ilocked(w, &proc_wrapper(proc)->delivered_freeze);
binder_inner_proc_unlock(proc);
if (put_user(BR_FROZEN_BINDER, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &info, sizeof(info)))
return -EFAULT;
ptr += sizeof(info);
binder_stat_br(proc, thread, BR_FROZEN_BINDER);
goto done; /* BR_FROZEN_BINDER notifications can cause transactions */
} break;
case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: {
struct binder_ref_freeze *freeze =
container_of(w, struct binder_ref_freeze, work);
binder_uintptr_t cookie = freeze->cookie;
binder_inner_proc_unlock(proc);
kfree(freeze);
if (put_user(BR_CLEAR_FREEZE_NOTIFICATION_DONE, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (put_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
binder_stat_br(proc, thread, BR_CLEAR_FREEZE_NOTIFICATION_DONE);
} break;
default:
binder_inner_proc_unlock(proc);
pr_err("%d:%d: bad work type %d\n",
@ -5576,6 +5802,48 @@ static bool binder_txns_pending_ilocked(struct binder_proc *proc)
return false;
}
static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen)
{
struct rb_node *n;
struct binder_ref *ref;
binder_inner_proc_lock(proc);
for (n = rb_first(&proc->nodes); n; n = rb_next(n)) {
struct binder_node *node;
node = rb_entry(n, struct binder_node, rb_node);
binder_inner_proc_unlock(proc);
binder_node_lock(node);
hlist_for_each_entry(ref, &node->refs, node_entry) {
/*
* Need the node lock to synchronize
* with new notification requests and the
* inner lock to synchronize with queued
* freeze notifications.
*/
binder_inner_proc_lock(ref->proc);
if (!ref->freeze) {
binder_inner_proc_unlock(ref->proc);
continue;
}
ref->freeze->work.type = BINDER_WORK_FROZEN_BINDER;
if (list_empty(&ref->freeze->work.entry)) {
ref->freeze->is_frozen = is_frozen;
binder_enqueue_work_ilocked(&ref->freeze->work, &ref->proc->todo);
binder_wakeup_proc_ilocked(ref->proc);
} else {
if (ref->freeze->sent && ref->freeze->is_frozen != is_frozen)
ref->freeze->resend = true;
ref->freeze->is_frozen = is_frozen;
}
binder_inner_proc_unlock(ref->proc);
}
binder_node_unlock(node);
binder_inner_proc_lock(proc);
}
binder_inner_proc_unlock(proc);
}
static int binder_ioctl_freeze(struct binder_freeze_info *info,
struct binder_proc *target_proc)
{
@ -5587,6 +5855,7 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
target_proc->async_recv = false;
target_proc->is_frozen = false;
binder_inner_proc_unlock(target_proc);
binder_add_freeze_work(target_proc, false);
return 0;
}
@ -5619,6 +5888,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
binder_inner_proc_lock(target_proc);
target_proc->is_frozen = false;
binder_inner_proc_unlock(target_proc);
} else {
binder_add_freeze_work(target_proc, true);
}
return ret;
@ -6004,6 +6275,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
binder_stats_created(BINDER_STAT_PROC);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc_wrapper(proc)->delivered_freeze);
INIT_LIST_HEAD(&proc->waiting_threads);
filp->private_data = proc;
@ -6563,7 +6835,7 @@ static const char * const binder_return_strings[] = {
"BR_FAILED_REPLY",
"BR_FROZEN_REPLY",
"BR_ONEWAY_SPAM_SUSPECT",
"BR_TRANSACTION_PENDING_FROZEN"
"BR_TRANSACTION_PENDING_FROZEN",
};
static const char * const binder_command_strings[] = {
@ -6595,7 +6867,7 @@ static const char * const binder_objstat_strings[] = {
"ref",
"death",
"transaction",
"transaction_complete"
"transaction_complete",
};
static void print_binder_stats(struct seq_file *m, const char *prefix,

View File

@ -161,6 +161,10 @@ struct binder_work {
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
#ifndef __GENKSYMS__
BINDER_WORK_FROZEN_BINDER,
BINDER_WORK_CLEAR_FREEZE_NOTIFICATION,
#endif
} type;
ANDROID_OEM_DATA(1);
@ -284,6 +288,14 @@ struct binder_ref_death {
binder_uintptr_t cookie;
};
struct binder_ref_freeze {
struct binder_work work;
binder_uintptr_t cookie;
bool is_frozen:1;
bool sent:1;
bool resend:1;
};
/**
* struct binder_ref_data - binder_ref counts and id
* @debug_id: unique ID for the ref
@ -316,6 +328,8 @@ struct binder_ref_data {
* @node indicates the node must be freed
* @death: pointer to death notification (ref_death) if requested
* (protected by @node->lock)
* @freeze: pointer to freeze notification (ref_freeze) if requested
* (protected by @node->lock)
*
* Structure to track references from procA to target node (on procB). This
* structure is unsafe to access without holding @proc->outer_lock.
@ -332,6 +346,7 @@ struct binder_ref {
struct binder_proc *proc;
struct binder_node *node;
struct binder_ref_death *death;
struct binder_ref_freeze *freeze;
};
/**
@ -471,10 +486,13 @@ struct binder_proc {
* @proc: binder_proc being wrapped
* @dmap dbitmap to manage available reference descriptors
* (protected by @proc.outer_lock)
* @delivered_freeze: list of delivered freeze notification
* (protected by @inner_lock)
*/
struct binder_proc_wrap {
struct binder_proc proc;
struct dbitmap dmap;
struct list_head delivered_freeze;
};
static inline

View File

@ -59,6 +59,7 @@ enum binderfs_stats_mode {
struct binder_features {
bool oneway_spam_detection;
bool extended_error;
bool freeze_notification;
};
static const struct constant_table binderfs_param_stats[] = {
@ -75,6 +76,7 @@ static const struct fs_parameter_spec binderfs_fs_parameters[] = {
static struct binder_features binder_features = {
.oneway_spam_detection = true,
.extended_error = true,
.freeze_notification = true,
};
static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb)
@ -609,6 +611,12 @@ static int init_binder_features(struct super_block *sb)
if (IS_ERR(dentry))
return PTR_ERR(dentry);
dentry = binderfs_create_file(dir, "freeze_notification",
&binder_features_fops,
&binder_features.freeze_notification);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
return 0;
}

View File

@ -168,6 +168,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ufs_send_uic_command);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ufs_send_tm_command);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ufs_check_int_errors);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ufs_update_sdev);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ogki_f2fs_create);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ogki_f2fs_submit_write_page);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_cgroup_attach);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_iommu_setup_dma_ops);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_iommu_alloc_insert_iova);
@ -291,8 +293,12 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_do_el1_fpac);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_handle_bad_stack);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_panic_unhandled);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_gzvm_vcpu_exit_reason);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_gzvm_handle_demand_page_pre);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_gzvm_handle_demand_page_post);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_gzvm_destroy_vm_post_process);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_alter_mutex_list_add);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mutex_unlock_slowpath);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mutex_unlock_slowpath_before_wakeq);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_save_track_hash);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_should_fault_around);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mem_cgroup_id_remove);
@ -322,6 +328,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_binder_looper_exited);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_binder_spawn_new_thread);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_binder_has_special_work_ilocked);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_logbuf);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_logbuf);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_do_shrink_slab);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_get_page_wmark);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_page_add_new_anon_rmap);
@ -334,11 +341,14 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_binder_read_done);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_modify_scan_control);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_should_continue_reclaim);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_file_is_tiny_bypass);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mglru_should_abort_scan);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_mglru_should_abort_scan_order);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_exit_signal);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_check_folio_look_around_ref);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_look_around);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_look_around_migrate_folio);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_test_clear_look_around_ref);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_throttle_direct_reclaim_bypass);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ufs_perf_huristic_ctrl);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ufs_send_command_post_change);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ufs_abort_success_ctrl);
@ -359,6 +369,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_tcp_connect);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_inet_csk_clone_lock);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_tcp_clean_rtx_queue);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_tcp_rcv_synack);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_udp_unicast_rcv_skb);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_udp6_unicast_rcv_skb);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_try_to_unmap_one);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_record_rwsem_reader_owned);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_clear_rwsem_reader_owned);
@ -537,3 +549,10 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ogki_udp6_unicast_rcv_skb);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_ogki_kmem_cache_create_usercopy);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_customize_thp_pcp_order);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_customize_thp_gfp_orders);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_init_adjust_zone_wmark);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_cma_alloc_retry);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_ksys_umount);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_do_group_exit);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_suitable_migration_target_bypass);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_shrink_node_memcgs);
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_swap_writepage);

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -213,6 +213,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -237,6 +237,22 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -264,6 +264,17 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
}
]
}

View File

@ -72,7 +72,7 @@ extern struct kvm_iommu_ops kvm_nvhe_sym(smmu_ops);
static int atomic_pages;
module_param(atomic_pages, int, 0);
static int kvm_arm_smmu_topup_memcache(struct arm_smccc_res *res)
static int kvm_arm_smmu_topup_memcache(struct arm_smccc_res *res, gfp_t gfp)
{
struct kvm_hyp_req req;
@ -89,8 +89,10 @@ static int kvm_arm_smmu_topup_memcache(struct arm_smccc_res *res)
}
if (req.mem.dest == REQ_MEM_DEST_HYP_IOMMU) {
return __pkvm_topup_hyp_alloc_mgt(HYP_ALLOC_MGT_IOMMU_ID,
+ req.mem.nr_pages, req.mem.sz_alloc);
return __pkvm_topup_hyp_alloc_mgt_gfp(HYP_ALLOC_MGT_IOMMU_ID,
req.mem.nr_pages,
req.mem.sz_alloc,
gfp);
} else if (req.mem.dest == REQ_MEM_DEST_HYP_ALLOC) {
/* Fill hyp alloc*/
return __pkvm_topup_hyp_alloc(req.mem.nr_pages);
@ -108,7 +110,7 @@ static int kvm_arm_smmu_topup_memcache(struct arm_smccc_res *res)
struct arm_smccc_res __res; \
do { \
__res = kvm_call_hyp_nvhe_smccc(__VA_ARGS__); \
} while (__res.a1 && !kvm_arm_smmu_topup_memcache(&__res));\
} while (__res.a1 && !kvm_arm_smmu_topup_memcache(&__res, GFP_KERNEL));\
__res.a1; \
})
@ -395,7 +397,7 @@ static int kvm_arm_smmu_map_pages(struct iommu_domain *domain,
WARN_ON(mapped > pgcount * pgsize);
pgcount -= mapped / pgsize;
*total_mapped += mapped;
} while (*total_mapped < size && !kvm_arm_smmu_topup_memcache(&res));
} while (*total_mapped < size && !kvm_arm_smmu_topup_memcache(&res, gfp));
if (*total_mapped < size)
return -EINVAL;
@ -430,7 +432,7 @@ static size_t kvm_arm_smmu_unmap_pages(struct iommu_domain *domain,
* block mapping.
*/
} while (total_unmapped < size &&
(unmapped || !kvm_arm_smmu_topup_memcache(&res)));
(unmapped || !kvm_arm_smmu_topup_memcache(&res, GFP_ATOMIC)));
return total_unmapped;
}

View File

@ -689,6 +689,18 @@ config DM_AUDIT
Enables audit logging of several security relevant events in the
particular device-mapper targets, especially the integrity target.
config DM_BOW
tristate "Backup block device"
depends on BLK_DEV_DM
select DM_BUFIO
help
This device-mapper target takes a device and keeps a log of all
changes using free blocks identified by issuing a trim command.
This can then be restored by running a command line utility,
or committed by simply replacing the target.
If unsure, say N.
config DM_USER
tristate "Block device in userspace"
depends on BLK_DEV_DM

View File

@ -85,6 +85,7 @@ obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
obj-$(CONFIG_DM_ZONED) += dm-zoned.o
obj-$(CONFIG_DM_WRITECACHE) += dm-writecache.o
obj-$(CONFIG_SECURITY_LOADPIN_VERITY) += dm-verity-loadpin.o
obj-$(CONFIG_DM_BOW) += dm-bow.o
obj-$(CONFIG_DM_USER) += dm-user.o
ifeq ($(CONFIG_DM_INIT),y)

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

1309
drivers/md/dm-bow.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -44,6 +44,15 @@ static int virtio_pmem_flush(struct nd_region *nd_region)
unsigned long flags;
int err, err1;
/*
* Don't bother to submit the request to the device if the device is
* not activated.
*/
if (vdev->config->get_status(vdev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
dev_info(&vdev->dev, "virtio pmem device needs a reset\n");
return -EIO;
}
might_sleep();
req_data = kmalloc(sizeof(*req_data), GFP_KERNEL);
if (!req_data)

View File

@ -1854,7 +1854,10 @@ check_type:
* assume caller has checked sense and determined
* the check condition was retryable.
*/
if (req->cmd_flags & REQ_FAILFAST_DEV || blk_rq_is_passthrough(req))
if (req->cmd_flags & REQ_FAILFAST_DEV)
return true;
if (blk_rq_is_passthrough(req) &&
!(scmd->flags & SCMD_RETRY_PASSTHROUGH))
return true;
return false;

View File

@ -53,6 +53,8 @@
#define SCSI_INLINE_SG_CNT 2
#endif
EXPORT_TRACEPOINT_SYMBOL_GPL(scsi_dispatch_cmd_start);
static struct kmem_cache *scsi_sense_cache;
static DEFINE_MUTEX(scsi_sense_cache_mutex);

View File

@ -3840,6 +3840,7 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
const struct scsi_exec_args exec_args = {
.sshdr = &sshdr,
.req_flags = BLK_MQ_REQ_PM,
.scmd_flags = SCMD_RETRY_PASSTHROUGH,
};
struct scsi_device *sdp = sdkp->device;
int res;

View File

@ -1141,6 +1141,77 @@ static const struct attribute_group ufs_sysfs_flags_group = {
.attrs = ufs_sysfs_device_flags,
};
static ssize_t max_number_of_rtt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
u32 rtt;
int ret;
down(&hba->host_sem);
if (!ufshcd_is_user_access_allowed(hba)) {
up(&hba->host_sem);
return -EBUSY;
}
ufshcd_rpm_get_sync(hba);
ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
QUERY_ATTR_IDN_MAX_NUM_OF_RTT, 0, 0, &rtt);
ufshcd_rpm_put_sync(hba);
if (ret)
goto out;
ret = sysfs_emit(buf, "0x%08X\n", rtt);
out:
up(&hba->host_sem);
return ret;
}
static ssize_t max_number_of_rtt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
struct scsi_device *sdev;
unsigned int rtt;
int ret;
if (kstrtouint(buf, 0, &rtt))
return -EINVAL;
if (rtt > to_hba_priv(hba)->rtt_cap) {
dev_err(dev, "rtt can be at most bDeviceRTTCap\n");
return -EINVAL;
}
down(&hba->host_sem);
if (!ufshcd_is_user_access_allowed(hba)) {
ret = -EBUSY;
goto out;
}
ufshcd_rpm_get_sync(hba);
shost_for_each_device(sdev, hba->host)
blk_mq_freeze_queue(sdev->request_queue);
ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
QUERY_ATTR_IDN_MAX_NUM_OF_RTT, 0, 0, &rtt);
shost_for_each_device(sdev, hba->host)
blk_mq_unfreeze_queue(sdev->request_queue);
ufshcd_rpm_put_sync(hba);
out:
up(&hba->host_sem);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(max_number_of_rtt);
static inline bool ufshcd_is_wb_attrs(enum attr_idn idn)
{
return idn >= QUERY_ATTR_IDN_WB_FLUSH_STATUS &&
@ -1188,7 +1259,6 @@ UFS_ATTRIBUTE(max_data_in_size, _MAX_DATA_IN);
UFS_ATTRIBUTE(max_data_out_size, _MAX_DATA_OUT);
UFS_ATTRIBUTE(reference_clock_frequency, _REF_CLK_FREQ);
UFS_ATTRIBUTE(configuration_descriptor_lock, _CONF_DESC_LOCK);
UFS_ATTRIBUTE(max_number_of_rtt, _MAX_NUM_OF_RTT);
UFS_ATTRIBUTE(exception_event_control, _EE_CONTROL);
UFS_ATTRIBUTE(exception_event_status, _EE_STATUS);
UFS_ATTRIBUTE(ffu_status, _FFU_STATUS);

View File

@ -6,6 +6,21 @@
#include <linux/pm_runtime.h>
#include <ufs/ufshcd.h>
/*
* @rtt_cap - bDeviceRTTCap
* @nortt - Max outstanding RTTs supported by controller
*/
struct ufs_hba_priv {
struct ufs_hba hba;
u8 rtt_cap;
int nortt;
};
static inline struct ufs_hba_priv *to_hba_priv(struct ufs_hba *hba)
{
return container_of(hba, struct ufs_hba_priv, hba);
}
static inline bool ufshcd_is_user_access_allowed(struct ufs_hba *hba)
{
return !hba->shutting_down;

View File

@ -103,6 +103,9 @@ enum {
/* Polling time to wait for fDeviceInit */
#define FDEVICEINIT_COMPL_TIMEOUT 1500 /* millisecs */
/* bMaxNumOfRTT is equal to two after device manufacturing */
#define DEFAULT_MAX_NUM_RTT 2
/* UFSHC 4.0 compliant HC support this mode. */
static bool use_mcq_mode = true;
@ -2302,6 +2305,8 @@ static inline int ufshcd_hba_capabilities(struct ufs_hba *hba)
((hba->capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
hba->reserved_slot = hba->nutrs - 1;
to_hba_priv(hba)->nortt = FIELD_GET(MASK_NUMBER_OUTSTANDING_RTT, hba->capabilities) + 1;
/* Read crypto capabilities */
err = ufshcd_hba_init_crypto_capabilities(hba);
if (err) {
@ -2459,7 +2464,6 @@ int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
ufshcd_hold(hba);
mutex_lock(&hba->uic_cmd_mutex);
ufshcd_add_delay_before_dme_cmd(hba);
WARN_ON(hba->uic_async_done);
ret = __ufshcd_send_uic_cmd(hba, uic_cmd, true);
if (!ret)
@ -4164,6 +4168,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
unsigned long flags;
u8 status;
int ret;
bool reenable_intr = false;
mutex_lock(&hba->uic_cmd_mutex);
ufshcd_add_delay_before_dme_cmd(hba);
@ -4174,6 +4179,15 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
goto out_unlock;
}
hba->uic_async_done = &uic_async_done;
if (ufshcd_readl(hba, REG_INTERRUPT_ENABLE) & UIC_COMMAND_COMPL) {
ufshcd_disable_intr(hba, UIC_COMMAND_COMPL);
/*
* Make sure UIC command completion interrupt is disabled before
* issuing UIC command.
*/
wmb();
reenable_intr = true;
}
spin_unlock_irqrestore(hba->host->host_lock, flags);
ret = __ufshcd_send_uic_cmd(hba, cmd, false);
if (ret) {
@ -4217,6 +4231,8 @@ out:
spin_lock_irqsave(hba->host->host_lock, flags);
hba->active_uic_cmd = NULL;
hba->uic_async_done = NULL;
if (reenable_intr)
ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
if (ret) {
ufshcd_set_link_broken(hba);
ufshcd_schedule_eh_work(hba);
@ -5383,12 +5399,11 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status);
if (intr_status & UIC_COMMAND_COMPL && cmd) {
if (!hba->uic_async_done) {
cmd->argument2 |= ufshcd_get_uic_cmd_result(hba);
cmd->argument3 = ufshcd_get_dme_attr_val(hba);
cmd->argument2 |= ufshcd_get_uic_cmd_result(hba);
cmd->argument3 = ufshcd_get_dme_attr_val(hba);
if (!hba->uic_async_done)
cmd->cmd_active = 0;
complete(&cmd->done);
}
complete(&cmd->done);
retval = IRQ_HANDLED;
}
@ -8151,6 +8166,39 @@ out:
dev_info->b_ext_iid_en = ext_iid_en;
}
static void ufshcd_set_rtt(struct ufs_hba *hba)
{
struct ufs_dev_info *dev_info = &hba->dev_info;
u32 rtt = 0;
u32 dev_rtt = 0;
const int max_num_rtt = hba->vops && hba->vops->name &&
strcmp(hba->vops->name, "mediatek.ufshci") == 0 ? 2 : 0;
int host_rtt_cap = max_num_rtt ? max_num_rtt : to_hba_priv(hba)->nortt;
/* RTT override makes sense only for UFS-4.0 and above */
if (dev_info->wspecversion < 0x400)
return;
if (ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
QUERY_ATTR_IDN_MAX_NUM_OF_RTT, 0, 0, &dev_rtt)) {
dev_err(hba->dev, "failed reading bMaxNumOfRTT\n");
return;
}
/* do not override if it was already written */
if (dev_rtt != DEFAULT_MAX_NUM_RTT)
return;
rtt = min_t(int, to_hba_priv(hba)->rtt_cap, host_rtt_cap);
if (rtt == dev_rtt)
return;
if (ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
QUERY_ATTR_IDN_MAX_NUM_OF_RTT, 0, 0, &rtt))
dev_err(hba->dev, "failed writing bMaxNumOfRTT\n");
}
void ufshcd_fixup_dev_quirks(struct ufs_hba *hba,
const struct ufs_dev_quirk *fixups)
{
@ -8213,6 +8261,8 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
desc_buf[DEVICE_DESC_PARAM_SPEC_VER + 1];
dev_info->bqueuedepth = desc_buf[DEVICE_DESC_PARAM_Q_DPTH];
to_hba_priv(hba)->rtt_cap = desc_buf[DEVICE_DESC_PARAM_RTT_CAP];
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
err = ufshcd_read_string_desc(hba, model_index,
@ -8545,6 +8595,8 @@ static int ufshcd_device_params_init(struct ufs_hba *hba)
goto out;
}
ufshcd_set_rtt(hba);
ufshcd_get_ref_clk_gating_wait(hba);
if (!ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_READ_FLAG,
@ -10271,6 +10323,8 @@ EXPORT_SYMBOL_GPL(ufshcd_dealloc_host);
*/
static int ufshcd_set_dma_mask(struct ufs_hba *hba)
{
if (hba->android_quirks & UFSHCD_ANDROID_QUIRK_36BIT_ADDRESS_DMA)
return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(36));
if (hba->capabilities & MASK_64_ADDRESSING_SUPPORT) {
if (!dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(64)))
return 0;
@ -10299,7 +10353,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
}
host = scsi_host_alloc(&ufshcd_driver_template,
sizeof(struct ufs_hba));
sizeof(struct ufs_hba_priv));
if (!host) {
dev_err(dev, "scsi_host_alloc failed\n");
err = -ENOMEM;

View File

@ -565,6 +565,9 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
if (!(reg & DWC3_DSTS_DEVCTRLHLT))
return;
if (!dwc->ev_buf)
return;
evt = dwc->ev_buf;
evt->lpos = 0;

View File

@ -1419,8 +1419,16 @@ int usb_add_gadget(struct usb_gadget *gadget)
if (ret)
goto err_free_id;
ret = sysfs_create_link(&udc->dev.kobj,
&gadget->dev.kobj, "gadget");
if (ret)
goto err_del_gadget;
return 0;
err_del_gadget:
device_del(&gadget->dev);
err_free_id:
ida_free(&gadget_id_numbers, gadget->id_number);
@ -1529,6 +1537,7 @@ void usb_del_gadget(struct usb_gadget *gadget)
mutex_unlock(&udc_lock);
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
sysfs_remove_link(&udc->dev.kobj, "gadget");
flush_work(&gadget->work);
device_del(&gadget->dev);
ida_free(&gadget_id_numbers, gadget->id_number);

View File

@ -4,6 +4,7 @@
*/
#include <linux/soc/mediatek/gzvm_drv.h>
#include <trace/hooks/gzvm.h>
static int cmp_ppages(struct rb_node *node, const struct rb_node *parent)
{
@ -160,10 +161,14 @@ static int handle_single_demand_page(struct gzvm *vm, int memslot_id, u64 gfn)
if (unlikely(ret))
return -EFAULT;
trace_android_vh_gzvm_handle_demand_page_pre(vm, memslot_id, pfn, gfn, 1);
ret = gzvm_arch_map_guest(vm->vm_id, memslot_id, pfn, gfn, 1);
if (unlikely(ret))
return -EFAULT;
trace_android_vh_gzvm_handle_demand_page_post(vm, memslot_id, pfn, gfn, 1);
return ret;
}
@ -200,6 +205,8 @@ static int handle_block_demand_page(struct gzvm *vm, int memslot_id, u64 gfn)
vm->demand_page_buffer[i] = pfn;
}
trace_android_vh_gzvm_handle_demand_page_pre(vm, memslot_id, 0, gfn, nr_entries);
ret = gzvm_arch_map_guest_block(vm->vm_id, memslot_id, start_gfn,
nr_entries);
if (unlikely(ret)) {
@ -207,6 +214,8 @@ static int handle_block_demand_page(struct gzvm *vm, int memslot_id, u64 gfn)
goto err_unlock;
}
trace_android_vh_gzvm_handle_demand_page_post(vm, memslot_id, 0, gfn, nr_entries);
err_unlock:
mutex_unlock(&vm->demand_paging_lock);

View File

@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/soc/mediatek/gzvm_drv.h>
#include <linux/debugfs.h>
#include <trace/hooks/gzvm.h>
#include "gzvm_common.h"
static DEFINE_MUTEX(gzvm_list_lock);
@ -375,6 +376,8 @@ static void gzvm_destroy_vm(struct gzvm *gzvm)
mutex_unlock(&gzvm->lock);
trace_android_vh_gzvm_destroy_vm_post_process(gzvm);
/* No need to lock here becauese it's single-threaded execution */
gzvm_destroy_all_ppage(gzvm);

View File

@ -955,7 +955,8 @@ int gunyah_gup_reclaim_parcel(struct gunyah_vm *ghvm,
if (folio_test_private(folio))
gunyah_folio_host_reclaim(folio);
folio_put(folio);
unpin_user_page(folio_page(folio, 0));
account_locked_vm(ghvm->mm_s, 1, false);
}
kfree(parcel->mem_entries);

View File

@ -557,6 +557,7 @@ int gunyah_gup_share_parcel(struct gunyah_vm *ghvm, struct gunyah_rm_mem_parcel
}
folio = page_folio(pages[0]);
*gfn -= folio_page_idx(folio, pages[0]);
*nr = folio_nr_pages(folio);
parcel->mem_entries[0].size = cpu_to_le64(folio_size(folio));
parcel->mem_entries[0].phys_addr = cpu_to_le64(PFN_PHYS(folio_pfn(folio)));
@ -567,6 +568,7 @@ int gunyah_gup_share_parcel(struct gunyah_vm *ghvm, struct gunyah_rm_mem_parcel
cpu_to_le64(folio_size(folio));
parcel->mem_entries[entries].phys_addr =
cpu_to_le64(PFN_PHYS(folio_pfn(folio)));
*nr += folio_nr_pages(folio);
entries++;
} else {
unpin_user_page(pages[i]);

View File

@ -264,6 +264,25 @@
"include-filter": "android.hardware.camera2.cts.FastBasicsTest"
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
}
]
}

View File

@ -250,6 +250,25 @@
}
]
},
{
"name": "CtsIncrementalInstallHostTestCases",
"options": [
{
"include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
},
{
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
}
]
},
{
"name": "CtsLibcoreLegacy22TestCases",
"options": [
{
"include-filter": "android.util.cts.FloatMathTest"
}
]
},
{
"name": "libdm_test"
},

View File

@ -110,7 +110,8 @@ int z_erofs_gbuf_growsize(unsigned int nrpages)
out:
if (i < z_erofs_gbuf_count && tmp_pages) {
for (j = 0; j < nrpages; ++j)
if (tmp_pages[j] && tmp_pages[j] != gbuf->pages[j])
if (tmp_pages[j] && (j >= gbuf->nrpages ||
tmp_pages[j] != gbuf->pages[j]))
__free_page(tmp_pages[j]);
kfree(tmp_pages);
}

View File

@ -5,11 +5,22 @@
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/bitmap.h>
#include <linux/buffer_head.h>
#include "exfat_raw.h"
#include "exfat_fs.h"
#if BITS_PER_LONG == 32
#define __le_long __le32
#define lel_to_cpu(A) le32_to_cpu(A)
#elif BITS_PER_LONG == 64
#define __le_long __le64
#define lel_to_cpu(A) le64_to_cpu(A)
#else
#error "BITS_PER_LONG not 32 or 64"
#endif
static const unsigned char free_bit[] = {
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/
@ -26,22 +37,6 @@ static const unsigned char free_bit[] = {
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/
};
static const unsigned char used_bit[] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/
2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/
2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/
4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/
3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/
4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/
3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/
4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/
3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/
5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/
};
/*
* Allocation Bitmap Management Functions
*/
@ -244,25 +239,24 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
unsigned int count = 0;
unsigned int i, map_i = 0, map_b = 0;
unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi);
unsigned int last_mask = total_clus & BITS_PER_BYTE_MASK;
unsigned char clu_bits;
const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011,
0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111};
unsigned int last_mask = total_clus & (BITS_PER_LONG - 1);
unsigned long *bitmap, clu_bits;
total_clus &= ~last_mask;
for (i = 0; i < total_clus; i += BITS_PER_BYTE) {
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
count += used_bit[clu_bits];
if (++map_b >= (unsigned int)sb->s_blocksize) {
for (i = 0; i < total_clus; i += BITS_PER_LONG) {
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
count += hweight_long(*bitmap);
map_b += sizeof(long);
if (map_b >= (unsigned int)sb->s_blocksize) {
map_i++;
map_b = 0;
}
}
if (last_mask) {
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
clu_bits &= last_bit_mask[last_mask];
count += used_bit[clu_bits];
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
clu_bits = lel_to_cpu(*(__le_long *)bitmap);
count += hweight_long(clu_bits & BITMAP_LAST_WORD_MASK(last_mask));
}
*ret_count = count;

View File

@ -287,7 +287,7 @@ get_new:
mutex_unlock(&EXFAT_SB(sb)->s_lock);
if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
(de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
(de.attr & EXFAT_ATTR_SUBDIR) ? DT_DIR : DT_REG))
goto out;
ctx->pos = cpos;
goto get_new;
@ -359,7 +359,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
if (ep->type == EXFAT_VOLUME)
return TYPE_VOLUME;
if (ep->type == EXFAT_FILE) {
if (le16_to_cpu(ep->dentry.file.attr) & ATTR_SUBDIR)
if (le16_to_cpu(ep->dentry.file.attr) & EXFAT_ATTR_SUBDIR)
return TYPE_DIR;
return TYPE_FILE;
}
@ -410,19 +410,21 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type)
ep->type = EXFAT_VOLUME;
} else if (type == TYPE_DIR) {
ep->type = EXFAT_FILE;
ep->dentry.file.attr = cpu_to_le16(ATTR_SUBDIR);
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_SUBDIR);
} else if (type == TYPE_FILE) {
ep->type = EXFAT_FILE;
ep->dentry.file.attr = cpu_to_le16(ATTR_ARCHIVE);
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_ARCHIVE);
}
}
static void exfat_init_stream_entry(struct exfat_dentry *ep,
unsigned char flags, unsigned int start_clu,
unsigned long long size)
unsigned int start_clu, unsigned long long size)
{
exfat_set_entry_type(ep, TYPE_STREAM);
ep->dentry.stream.flags = flags;
if (size == 0)
ep->dentry.stream.flags = ALLOC_FAT_CHAIN;
else
ep->dentry.stream.flags = ALLOC_NO_FAT_CHAIN;
ep->dentry.stream.start_clu = cpu_to_le32(start_clu);
ep->dentry.stream.valid_size = cpu_to_le64(size);
ep->dentry.stream.size = cpu_to_le64(size);
@ -446,55 +448,34 @@ static void exfat_init_name_entry(struct exfat_dentry *ep,
}
}
int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
int entry, unsigned int type, unsigned int start_clu,
unsigned long long size)
void exfat_init_dir_entry(struct exfat_entry_set_cache *es,
unsigned int type, unsigned int start_clu,
unsigned long long size, struct timespec64 *ts)
{
struct super_block *sb = inode->i_sb;
struct super_block *sb = es->sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct timespec64 ts = current_time(inode);
struct exfat_dentry *ep;
struct buffer_head *bh;
/*
* We cannot use exfat_get_dentry_set here because file ep is not
* initialized yet.
*/
ep = exfat_get_dentry(sb, p_dir, entry, &bh);
if (!ep)
return -EIO;
ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
exfat_set_entry_type(ep, type);
exfat_set_entry_time(sbi, &ts,
exfat_set_entry_time(sbi, ts,
&ep->dentry.file.create_tz,
&ep->dentry.file.create_time,
&ep->dentry.file.create_date,
&ep->dentry.file.create_time_cs);
exfat_set_entry_time(sbi, &ts,
exfat_set_entry_time(sbi, ts,
&ep->dentry.file.modify_tz,
&ep->dentry.file.modify_time,
&ep->dentry.file.modify_date,
&ep->dentry.file.modify_time_cs);
exfat_set_entry_time(sbi, &ts,
exfat_set_entry_time(sbi, ts,
&ep->dentry.file.access_tz,
&ep->dentry.file.access_time,
&ep->dentry.file.access_date,
NULL);
exfat_update_bh(bh, IS_DIRSYNC(inode));
brelse(bh);
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh);
if (!ep)
return -EIO;
exfat_init_stream_entry(ep,
(type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN,
start_clu, size);
exfat_update_bh(bh, IS_DIRSYNC(inode));
brelse(bh);
return 0;
ep = exfat_get_dentry_cached(es, ES_IDX_STREAM);
exfat_init_stream_entry(ep, start_clu, size);
}
int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
@ -551,73 +532,46 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode,
exfat_free_cluster(inode, &dir);
}
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
int entry, int num_entries, struct exfat_uni_name *p_uniname)
void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries,
struct exfat_uni_name *p_uniname)
{
struct super_block *sb = inode->i_sb;
int i;
unsigned short *uniname = p_uniname->name;
struct exfat_dentry *ep;
struct buffer_head *bh;
int sync = IS_DIRSYNC(inode);
ep = exfat_get_dentry(sb, p_dir, entry, &bh);
if (!ep)
return -EIO;
ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
ep->dentry.file.num_ext = (unsigned char)(num_entries - 1);
exfat_update_bh(bh, sync);
brelse(bh);
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh);
if (!ep)
return -EIO;
ep = exfat_get_dentry_cached(es, ES_IDX_STREAM);
ep->dentry.stream.name_len = p_uniname->name_len;
ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash);
exfat_update_bh(bh, sync);
brelse(bh);
for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
if (!ep)
return -EIO;
if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
exfat_free_benign_secondary_clusters(inode, ep);
for (i = ES_IDX_FIRST_FILENAME; i < num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
exfat_init_name_entry(ep, uniname);
exfat_update_bh(bh, sync);
brelse(bh);
uniname += EXFAT_FILE_NAME_LEN;
}
exfat_update_dir_chksum(inode, p_dir, entry);
return 0;
exfat_update_dir_chksum_with_entry_set(es);
}
int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
int entry, int order, int num_entries)
void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es,
int order)
{
struct super_block *sb = inode->i_sb;
int i;
struct exfat_dentry *ep;
struct buffer_head *bh;
for (i = order; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
if (!ep)
return -EIO;
for (i = order; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
exfat_free_benign_secondary_clusters(inode, ep);
exfat_set_entry_type(ep, TYPE_DELETED);
exfat_update_bh(bh, IS_DIRSYNC(inode));
brelse(bh);
}
return 0;
if (order < es->num_entries)
es->modified = true;
}
void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
@ -775,7 +729,6 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
}
enum exfat_validate_dentry_mode {
ES_MODE_STARTED,
ES_MODE_GET_FILE_ENTRY,
ES_MODE_GET_STRM_ENTRY,
ES_MODE_GET_NAME_ENTRY,
@ -790,11 +743,6 @@ static bool exfat_validate_entry(unsigned int type,
return false;
switch (*mode) {
case ES_MODE_STARTED:
if (type != TYPE_FILE && type != TYPE_DIR)
return false;
*mode = ES_MODE_GET_FILE_ENTRY;
break;
case ES_MODE_GET_FILE_ENTRY:
if (type != TYPE_STREAM)
return false;
@ -834,7 +782,7 @@ struct exfat_dentry *exfat_get_dentry_cached(
}
/*
* Returns a set of dentries for a file or dir.
* Returns a set of dentries.
*
* Note It provides a direct pointer to bh->data via exfat_get_dentry_cached().
* User should call exfat_get_dentry_set() after setting 'modified' to apply
@ -842,22 +790,24 @@ struct exfat_dentry *exfat_get_dentry_cached(
*
* in:
* sb+p_dir+entry: indicates a file/dir
* type: specifies how many dentries should be included.
* num_entries: specifies how many dentries should be included.
* It will be set to es->num_entries if it is not 0.
* If num_entries is 0, es->num_entries will be obtained
* from the first dentry.
* out:
* es: pointer of entry set on success.
* return:
* pointer of entry set on success,
* NULL on failure.
* 0 on success
* -error code on failure
*/
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
static int __exfat_get_dentry_set(struct exfat_entry_set_cache *es,
struct super_block *sb, struct exfat_chain *p_dir, int entry,
unsigned int type)
unsigned int num_entries)
{
int ret, i, num_bh;
unsigned int off;
sector_t sec;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_dentry *ep;
int num_entries;
enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
struct buffer_head *bh;
if (p_dir->dir == DIR_DELETED) {
@ -880,12 +830,18 @@ int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
return -EIO;
es->bh[es->num_bh++] = bh;
ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
goto put_es;
if (num_entries == ES_ALL_ENTRIES) {
struct exfat_dentry *ep;
ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
if (ep->type != EXFAT_FILE) {
brelse(bh);
return -EIO;
}
num_entries = ep->dentry.file.num_ext + 1;
}
num_entries = type == ES_ALL_ENTRIES ?
ep->dentry.file.num_ext + 1 : type;
es->num_entries = num_entries;
num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
@ -918,8 +874,27 @@ int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
es->bh[es->num_bh++] = bh;
}
return 0;
put_es:
exfat_put_dentry_set(es, false);
return -EIO;
}
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
struct super_block *sb, struct exfat_chain *p_dir,
int entry, unsigned int num_entries)
{
int ret, i;
struct exfat_dentry *ep;
enum exfat_validate_dentry_mode mode = ES_MODE_GET_FILE_ENTRY;
ret = __exfat_get_dentry_set(es, sb, p_dir, entry, num_entries);
if (ret < 0)
return ret;
/* validate cached dentries */
for (i = ES_IDX_STREAM; i < num_entries; i++) {
for (i = ES_IDX_STREAM; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
goto put_es;
@ -931,6 +906,85 @@ put_es:
return -EIO;
}
static int exfat_validate_empty_dentry_set(struct exfat_entry_set_cache *es)
{
struct exfat_dentry *ep;
struct buffer_head *bh;
int i, off;
bool unused_hit = false;
/*
* ONLY UNUSED OR DELETED DENTRIES ARE ALLOWED:
* Although it violates the specification for a deleted entry to
* follow an unused entry, some exFAT implementations could work
* like this. Therefore, to improve compatibility, let's allow it.
*/
for (i = 0; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
if (ep->type == EXFAT_UNUSED) {
unused_hit = true;
} else if (!IS_EXFAT_DELETED(ep->type)) {
if (unused_hit)
goto err_used_follow_unused;
i++;
goto count_skip_entries;
}
}
return 0;
err_used_follow_unused:
off = es->start_off + (i << DENTRY_SIZE_BITS);
bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)];
exfat_fs_error(es->sb,
"in sector %lld, dentry %d should be unused, but 0x%x",
bh->b_blocknr, off >> DENTRY_SIZE_BITS, ep->type);
return -EIO;
count_skip_entries:
es->num_entries = EXFAT_B_TO_DEN(EXFAT_BLK_TO_B(es->num_bh, es->sb) - es->start_off);
for (; i < es->num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
if (IS_EXFAT_DELETED(ep->type))
break;
}
return i;
}
/*
* Get an empty dentry set.
*
* in:
* sb+p_dir+entry: indicates the empty dentry location
* num_entries: specifies how many empty dentries should be included.
* out:
* es: pointer of empty dentry set on success.
* return:
* 0 : on success
* >0 : the dentries are not empty, the return value is the number of
* dentries to be skipped for the next lookup.
* <0 : on failure
*/
int exfat_get_empty_dentry_set(struct exfat_entry_set_cache *es,
struct super_block *sb, struct exfat_chain *p_dir,
int entry, unsigned int num_entries)
{
int ret;
ret = __exfat_get_dentry_set(es, sb, p_dir, entry, num_entries);
if (ret < 0)
return ret;
ret = exfat_validate_empty_dentry_set(es);
if (ret)
exfat_put_dentry_set(es, false);
return ret;
}
static inline void exfat_reset_empty_hint(struct exfat_hint_femp *hint_femp)
{
hint_femp->eidx = EXFAT_HINT_NONE;

View File

@ -208,6 +208,7 @@ struct exfat_dir_entry {
unsigned char flags;
unsigned short attr;
loff_t size;
loff_t valid_size;
unsigned int num_subdirs;
struct timespec64 atime;
struct timespec64 mtime;
@ -234,6 +235,8 @@ struct exfat_mount_options {
discard:1, /* Issue discard requests on deletions */
keep_last_dots:1; /* Keep trailing periods in paths */
int time_offset; /* Offset of timestamps from UTC (in minutes) */
/* Support creating zero-size directory, default: false */
bool zero_size_dir;
};
/*
@ -273,6 +276,7 @@ struct exfat_sb_info {
spinlock_t inode_hash_lock;
struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
struct rcu_head rcu;
};
#define EXFAT_CACHE_VALID 0
@ -315,6 +319,7 @@ struct exfat_inode_info {
loff_t i_size_aligned;
/* on-disk position of directory entry or 0 */
loff_t i_pos;
loff_t valid_size;
/* hash by i_location */
struct hlist_node i_hash_fat;
/* protect bmap against truncate */
@ -357,10 +362,10 @@ static inline int exfat_mode_can_hold_ro(struct inode *inode)
static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
unsigned short attr, mode_t mode)
{
if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
if ((attr & EXFAT_ATTR_READONLY) && !(attr & EXFAT_ATTR_SUBDIR))
mode &= ~0222;
if (attr & ATTR_SUBDIR)
if (attr & EXFAT_ATTR_SUBDIR)
return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
return (mode & ~sbi->options.fs_fmask) | S_IFREG;
@ -372,18 +377,18 @@ static inline unsigned short exfat_make_attr(struct inode *inode)
unsigned short attr = EXFAT_I(inode)->attr;
if (S_ISDIR(inode->i_mode))
attr |= ATTR_SUBDIR;
attr |= EXFAT_ATTR_SUBDIR;
if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222))
attr |= ATTR_READONLY;
attr |= EXFAT_ATTR_READONLY;
return attr;
}
static inline void exfat_save_attr(struct inode *inode, unsigned short attr)
{
if (exfat_mode_can_hold_ro(inode))
EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY);
EXFAT_I(inode)->attr = attr & (EXFAT_ATTR_RWMASK | EXFAT_ATTR_READONLY);
else
EXFAT_I(inode)->attr = attr & ATTR_RWMASK;
EXFAT_I(inode)->attr = attr & EXFAT_ATTR_RWMASK;
}
static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,
@ -476,13 +481,13 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
extern const struct inode_operations exfat_dir_inode_operations;
extern const struct file_operations exfat_dir_operations;
unsigned int exfat_get_entry_type(struct exfat_dentry *p_entry);
int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
int entry, unsigned int type, unsigned int start_clu,
unsigned long long size);
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
int entry, int num_entries, struct exfat_uni_name *p_uniname);
int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
int entry, int order, int num_entries);
void exfat_init_dir_entry(struct exfat_entry_set_cache *es,
unsigned int type, unsigned int start_clu,
unsigned long long size, struct timespec64 *ts);
void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries,
struct exfat_uni_name *p_uniname);
void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es,
int order);
int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
int entry);
void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
@ -497,7 +502,10 @@ struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
int num);
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
struct super_block *sb, struct exfat_chain *p_dir, int entry,
unsigned int type);
unsigned int num_entries);
int exfat_get_empty_dentry_set(struct exfat_entry_set_cache *es,
struct super_block *sb, struct exfat_chain *p_dir, int entry,
unsigned int num_entries);
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync);
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
@ -549,6 +557,7 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 tz, __le16 time, __le16 date, u8 time_cs);
void exfat_truncate_atime(struct timespec64 *ts);
void exfat_truncate_inode_atime(struct inode *inode);
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);

View File

@ -64,15 +64,16 @@
#define CS_DEFAULT 2
/* file attributes */
#define ATTR_READONLY 0x0001
#define ATTR_HIDDEN 0x0002
#define ATTR_SYSTEM 0x0004
#define ATTR_VOLUME 0x0008
#define ATTR_SUBDIR 0x0010
#define ATTR_ARCHIVE 0x0020
#define EXFAT_ATTR_READONLY 0x0001
#define EXFAT_ATTR_HIDDEN 0x0002
#define EXFAT_ATTR_SYSTEM 0x0004
#define EXFAT_ATTR_VOLUME 0x0008
#define EXFAT_ATTR_SUBDIR 0x0010
#define EXFAT_ATTR_ARCHIVE 0x0020
#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
ATTR_SUBDIR | ATTR_ARCHIVE)
#define EXFAT_ATTR_RWMASK (EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | \
EXFAT_ATTR_VOLUME | EXFAT_ATTR_SUBDIR | \
EXFAT_ATTR_ARCHIVE)
#define BOOTSEC_JUMP_BOOT_LEN 3
#define BOOTSEC_FS_NAME_LEN 8

View File

@ -8,37 +8,86 @@
#include <linux/cred.h>
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
#include <linux/fsnotify.h>
#include <linux/security.h>
#include <linux/msdos_fs.h>
#include <linux/writeback.h>
#include "exfat_raw.h"
#include "exfat_fs.h"
static int exfat_cont_expand(struct inode *inode, loff_t size)
{
struct address_space *mapping = inode->i_mapping;
loff_t start = i_size_read(inode), count = size - i_size_read(inode);
int err, err2;
int ret;
unsigned int num_clusters, new_num_clusters, last_clu;
struct exfat_inode_info *ei = EXFAT_I(inode);
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_chain clu;
err = generic_cont_expand_simple(inode, size);
if (err)
return err;
ret = inode_newsize_ok(inode, size);
if (ret)
return ret;
num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi);
if (new_num_clusters == num_clusters)
goto out;
if (num_clusters) {
exfat_chain_set(&clu, ei->start_clu, num_clusters, ei->flags);
ret = exfat_find_last_cluster(sb, &clu, &last_clu);
if (ret)
return ret;
clu.dir = last_clu + 1;
} else {
last_clu = EXFAT_EOF_CLUSTER;
clu.dir = EXFAT_EOF_CLUSTER;
}
clu.size = 0;
clu.flags = ei->flags;
ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters,
&clu, IS_DIRSYNC(inode));
if (ret)
return ret;
/* Append new clusters to chain */
if (num_clusters) {
if (clu.flags != ei->flags)
if (exfat_chain_cont_cluster(sb, ei->start_clu, num_clusters))
goto free_clu;
if (clu.flags == ALLOC_FAT_CHAIN)
if (exfat_ent_set(sb, last_clu, clu.dir))
goto free_clu;
} else
ei->start_clu = clu.dir;
ei->flags = clu.flags;
out:
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
/* Expanded range not zeroed, do not update valid_size */
i_size_write(inode, size);
ei->i_size_aligned = round_up(size, sb->s_blocksize);
ei->i_size_ondisk = ei->i_size_aligned;
inode->i_blocks = round_up(size, sbi->cluster_size) >> 9;
if (IS_DIRSYNC(inode))
return write_inode_now(inode, 1);
inode->i_mtime = inode_set_ctime_current(inode);
mark_inode_dirty(inode);
if (!IS_SYNC(inode))
return 0;
return 0;
err = filemap_fdatawrite_range(mapping, start, start + count - 1);
err2 = sync_mapping_buffers(mapping);
if (!err)
err = err2;
err2 = write_inode_now(inode, 1);
if (!err)
err = err2;
if (err)
return err;
return filemap_fdatawait_range(mapping, start, start + count - 1);
free_clu:
exfat_free_cluster(inode, &clu);
return -EIO;
}
static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode)
@ -143,8 +192,11 @@ int __exfat_truncate(struct inode *inode)
ei->start_clu = EXFAT_EOF_CLUSTER;
}
if (i_size_read(inode) < ei->valid_size)
ei->valid_size = i_size_read(inode);
if (ei->type == TYPE_FILE)
ei->attr |= ATTR_ARCHIVE;
ei->attr |= EXFAT_ATTR_ARCHIVE;
/*
* update the directory entry
@ -290,10 +342,9 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
}
if (attr->ia_valid & ATTR_SIZE)
inode->i_mtime = inode_set_ctime_current(inode);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
setattr_copy(&nop_mnt_idmap, inode, attr);
exfat_truncate_atime(&inode->i_atime);
exfat_truncate_inode_atime(inode);
if (attr->ia_valid & ATTR_SIZE) {
error = exfat_block_truncate_page(inode, attr->ia_size);
@ -316,6 +367,93 @@ out:
return error;
}
/*
* modified ioctls from fat/file.c by Welmer Almesberger
*/
static int exfat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
{
u32 attr;
inode_lock_shared(inode);
attr = exfat_make_attr(inode);
inode_unlock_shared(inode);
return put_user(attr, user_attr);
}
static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
{
struct inode *inode = file_inode(file);
struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
int is_dir = S_ISDIR(inode->i_mode);
u32 attr, oldattr;
struct iattr ia;
int err;
err = get_user(attr, user_attr);
if (err)
goto out;
err = mnt_want_write_file(file);
if (err)
goto out;
inode_lock(inode);
oldattr = exfat_make_attr(inode);
/*
* Mask attributes so we don't set reserved fields.
*/
attr &= (EXFAT_ATTR_READONLY | EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM |
EXFAT_ATTR_ARCHIVE);
attr |= (is_dir ? EXFAT_ATTR_SUBDIR : 0);
/* Equivalent to a chmod() */
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
ia.ia_ctime = current_time(inode);
if (is_dir)
ia.ia_mode = exfat_make_mode(sbi, attr, 0777);
else
ia.ia_mode = exfat_make_mode(sbi, attr, 0666 | (inode->i_mode & 0111));
/* The root directory has no attributes */
if (inode->i_ino == EXFAT_ROOT_INO && attr != EXFAT_ATTR_SUBDIR) {
err = -EINVAL;
goto out_unlock_inode;
}
if (((attr | oldattr) & EXFAT_ATTR_SYSTEM) &&
!capable(CAP_LINUX_IMMUTABLE)) {
err = -EPERM;
goto out_unlock_inode;
}
/*
* The security check is questionable... We single
* out the RO attribute for checking by the security
* module, just because it maps to a file mode.
*/
err = security_inode_setattr(file_mnt_idmap(file),
file->f_path.dentry, &ia);
if (err)
goto out_unlock_inode;
/* This MUST be done before doing anything irreversible... */
err = exfat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia);
if (err)
goto out_unlock_inode;
fsnotify_change(file->f_path.dentry, ia.ia_valid);
exfat_save_attr(inode, attr);
mark_inode_dirty(inode);
out_unlock_inode:
inode_unlock(inode);
mnt_drop_write_file(file);
out:
return err;
}
static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
{
struct fstrim_range range;
@ -346,8 +484,13 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
u32 __user *user_attr = (u32 __user *)arg;
switch (cmd) {
case FAT_IOCTL_GET_ATTRIBUTES:
return exfat_ioctl_get_attributes(inode, user_attr);
case FAT_IOCTL_SET_ATTRIBUTES:
return exfat_ioctl_set_attributes(filp, user_attr);
case FITRIM:
return exfat_ioctl_fitrim(inode, arg);
default:
@ -379,15 +522,124 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
return blkdev_issue_flush(inode->i_sb->s_bdev);
}
static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
{
int err;
struct inode *inode = file_inode(file);
struct address_space *mapping = inode->i_mapping;
const struct address_space_operations *ops = mapping->a_ops;
while (start < end) {
u32 zerofrom, len;
struct page *page = NULL;
zerofrom = start & (PAGE_SIZE - 1);
len = PAGE_SIZE - zerofrom;
if (start + len > end)
len = end - start;
err = ops->write_begin(file, mapping, start, len, &page, NULL);
if (err)
goto out;
zero_user_segment(page, zerofrom, zerofrom + len);
err = ops->write_end(file, mapping, start, len, len, page, NULL);
if (err < 0)
goto out;
start += len;
balance_dirty_pages_ratelimited(mapping);
cond_resched();
}
out:
return err;
}
static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
{
ssize_t ret;
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct exfat_inode_info *ei = EXFAT_I(inode);
loff_t pos = iocb->ki_pos;
loff_t valid_size;
inode_lock(inode);
valid_size = ei->valid_size;
ret = generic_write_checks(iocb, iter);
if (ret < 0)
goto unlock;
if (pos > valid_size) {
ret = exfat_file_zeroed_range(file, valid_size, pos);
if (ret < 0 && ret != -ENOSPC) {
exfat_err(inode->i_sb,
"write: fail to zero from %llu to %llu(%zd)",
valid_size, pos, ret);
}
if (ret < 0)
goto unlock;
}
ret = __generic_file_write_iter(iocb, iter);
if (ret < 0)
goto unlock;
inode_unlock(inode);
if (pos > valid_size)
pos = valid_size;
if (iocb_is_dsync(iocb) && iocb->ki_pos > pos) {
ssize_t err = vfs_fsync_range(file, pos, iocb->ki_pos - 1,
iocb->ki_flags & IOCB_SYNC);
if (err < 0)
return err;
}
return ret;
unlock:
inode_unlock(inode);
return ret;
}
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
{
int ret;
struct inode *inode = file_inode(file);
struct exfat_inode_info *ei = EXFAT_I(inode);
loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
loff_t end = min_t(loff_t, i_size_read(inode),
start + vma->vm_end - vma->vm_start);
if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
ret = exfat_file_zeroed_range(file, ei->valid_size, end);
if (ret < 0) {
exfat_err(inode->i_sb,
"mmap: fail to zero from %llu to %llu(%d)",
start, end, ret);
return ret;
}
}
return generic_file_mmap(file, vma);
}
const struct file_operations exfat_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.write_iter = exfat_file_write_iter,
.unlocked_ioctl = exfat_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = exfat_compat_ioctl,
#endif
.mmap = generic_file_mmap,
.mmap = exfat_file_mmap,
.fsync = exfat_file_fsync,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,

View File

@ -26,6 +26,7 @@ int __exfat_write_inode(struct inode *inode, int sync)
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
bool is_dir = (ei->type == TYPE_DIR) ? true : false;
struct timespec64 ts;
if (inode->i_ino == EXFAT_ROOT_INO)
return 0;
@ -55,16 +56,18 @@ int __exfat_write_inode(struct inode *inode, int sync)
&ep->dentry.file.create_time,
&ep->dentry.file.create_date,
&ep->dentry.file.create_time_cs);
exfat_set_entry_time(sbi, &inode->i_mtime,
&ep->dentry.file.modify_tz,
&ep->dentry.file.modify_time,
&ep->dentry.file.modify_date,
&ep->dentry.file.modify_time_cs);
exfat_set_entry_time(sbi, &inode->i_atime,
&ep->dentry.file.access_tz,
&ep->dentry.file.access_time,
&ep->dentry.file.access_date,
NULL);
exfat_set_entry_time(sbi, &ts,
&ep->dentry.file.modify_tz,
&ep->dentry.file.modify_time,
&ep->dentry.file.modify_date,
&ep->dentry.file.modify_time_cs);
inode_set_mtime_to_ts(inode, ts);
exfat_set_entry_time(sbi, &ts,
&ep->dentry.file.access_tz,
&ep->dentry.file.access_time,
&ep->dentry.file.access_date,
NULL);
inode_set_atime_to_ts(inode, ts);
/* File size should be zero if there is no cluster allocated */
on_disk_size = i_size_read(inode);
@ -72,8 +75,17 @@ int __exfat_write_inode(struct inode *inode, int sync)
if (ei->start_clu == EXFAT_EOF_CLUSTER)
on_disk_size = 0;
ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
ep2->dentry.stream.size = cpu_to_le64(on_disk_size);
/*
* mmap write does not use exfat_write_end(), valid_size may be
* extended to the sector-aligned length in exfat_get_block().
* So we need to fixup valid_size to the writren length.
*/
if (on_disk_size < ei->valid_size)
ep2->dentry.stream.valid_size = ep2->dentry.stream.size;
else
ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size);
if (on_disk_size) {
ep2->dentry.stream.flags = ei->flags;
ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu);
@ -275,6 +287,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
unsigned int cluster, sec_offset;
sector_t last_block;
sector_t phys = 0;
sector_t valid_blks;
loff_t pos;
mutex_lock(&sbi->s_lock);
@ -303,17 +316,32 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
mapped_blocks = sbi->sect_per_clus - sec_offset;
max_blocks = min(mapped_blocks, max_blocks);
/* Treat newly added block / cluster */
if (iblock < last_block)
create = 0;
if (create || buffer_delay(bh_result)) {
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
if ((create && iblock >= last_block) || buffer_delay(bh_result)) {
if (ei->i_size_ondisk < pos)
ei->i_size_ondisk = pos;
}
map_bh(bh_result, sb, phys);
if (buffer_delay(bh_result))
clear_buffer_delay(bh_result);
if (create) {
valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb);
if (iblock + max_blocks < valid_blks) {
/* The range has been written, map it */
goto done;
} else if (iblock < valid_blks) {
/*
* The range has been partially written,
* map the written part.
*/
max_blocks = valid_blks - iblock;
goto done;
}
/* The area has not been written, map and mark as new. */
err = exfat_map_new_buffer(ei, bh_result, pos);
if (err) {
exfat_fs_error(sb,
@ -321,11 +349,58 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
pos, ei->i_size_aligned);
goto unlock_ret;
}
}
if (buffer_delay(bh_result))
clear_buffer_delay(bh_result);
map_bh(bh_result, sb, phys);
ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb);
mark_inode_dirty(inode);
} else {
valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb);
if (iblock + max_blocks < valid_blks) {
/* The range has been written, map it */
goto done;
} else if (iblock < valid_blks) {
/*
* The area has been partially written,
* map the written part.
*/
max_blocks = valid_blks - iblock;
goto done;
} else if (iblock == valid_blks &&
(ei->valid_size & (sb->s_blocksize - 1))) {
/*
* The block has been partially written,
* zero the unwritten part and map the block.
*/
loff_t size, off;
max_blocks = 1;
/*
* For direct read, the unwritten part will be zeroed in
* exfat_direct_IO()
*/
if (!bh_result->b_folio)
goto done;
pos -= sb->s_blocksize;
size = ei->valid_size - pos;
off = pos & (PAGE_SIZE - 1);
folio_set_bh(bh_result, bh_result->b_folio, off);
err = bh_read(bh_result, 0);
if (err < 0)
goto unlock_ret;
folio_zero_segment(bh_result->b_folio, off + size,
off + sb->s_blocksize);
} else {
/*
* The range has not been written, clear the mapped flag
* to only zero the cache and do not read from disk.
*/
clear_buffer_mapped(bh_result);
}
}
done:
bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb);
unlock_ret:
@ -340,6 +415,17 @@ static int exfat_read_folio(struct file *file, struct folio *folio)
static void exfat_readahead(struct readahead_control *rac)
{
struct address_space *mapping = rac->mapping;
struct inode *inode = mapping->host;
struct exfat_inode_info *ei = EXFAT_I(inode);
loff_t pos = readahead_pos(rac);
/* Range cross valid_size, read it page by page. */
if (ei->valid_size < i_size_read(inode) &&
pos <= ei->valid_size &&
ei->valid_size < pos + readahead_length(rac))
return;
mpage_readahead(rac, exfat_get_block);
}
@ -355,7 +441,7 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
if (to > i_size_read(inode)) {
truncate_pagecache(inode, i_size_read(inode));
inode->i_mtime = inode_set_ctime_current(inode);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
exfat_truncate(inode);
}
}
@ -367,9 +453,7 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
int ret;
*pagep = NULL;
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
exfat_get_block,
&EXFAT_I(mapping->host)->i_size_ondisk);
ret = block_write_begin(mapping, pos, len, pagep, exfat_get_block);
if (ret < 0)
exfat_write_failed(mapping, pos+len);
@ -397,9 +481,14 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
if (err < len)
exfat_write_failed(mapping, pos+len);
if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) {
inode->i_mtime = inode_set_ctime_current(inode);
ei->attr |= ATTR_ARCHIVE;
if (!(err < 0) && pos + err > ei->valid_size) {
ei->valid_size = pos + err;
mark_inode_dirty(inode);
}
if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) {
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
ei->attr |= EXFAT_ATTR_ARCHIVE;
mark_inode_dirty(inode);
}
@ -410,7 +499,9 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
struct address_space *mapping = iocb->ki_filp->f_mapping;
struct inode *inode = mapping->host;
loff_t size = iocb->ki_pos + iov_iter_count(iter);
struct exfat_inode_info *ei = EXFAT_I(inode);
loff_t pos = iocb->ki_pos;
loff_t size = pos + iov_iter_count(iter);
int rw = iov_iter_rw(iter);
ssize_t ret;
@ -433,8 +524,20 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
* condition of exfat_get_block() and ->truncate().
*/
ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block);
if (ret < 0 && (rw & WRITE))
exfat_write_failed(mapping, size);
if (ret < 0) {
if (rw == WRITE && ret != -EIOCBQUEUED)
exfat_write_failed(mapping, size);
return ret;
} else
size = pos + ret;
/* zero the unwritten part in the partially written block */
if (rw == READ && pos < ei->valid_size && ei->valid_size < size) {
iov_iter_revert(iter, size - ei->valid_size);
iov_iter_zero(size - ei->valid_size, iter);
}
return ret;
}
@ -534,6 +637,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
ei->start_clu = info->start_clu;
ei->flags = info->flags;
ei->type = info->type;
ei->valid_size = info->valid_size;
ei->version = 0;
ei->hint_stat.eidx = 0;
@ -547,7 +651,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
inode_inc_iversion(inode);
inode->i_generation = get_random_u32();
if (info->attr & ATTR_SUBDIR) { /* directory */
if (info->attr & EXFAT_ATTR_SUBDIR) { /* directory */
inode->i_generation &= ~1;
inode->i_mode = exfat_make_mode(sbi, info->attr, 0777);
inode->i_op = &exfat_dir_inode_operations;
@ -576,10 +680,10 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
exfat_save_attr(inode, info->attr);
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
inode->i_mtime = info->mtime;
inode_set_mtime_to_ts(inode, info->mtime);
inode_set_ctime_to_ts(inode, info->mtime);
ei->i_crtime = info->crtime;
inode->i_atime = info->atime;
inode_set_atime_to_ts(inode, info->atime);
return 0;
}

View File

@ -126,6 +126,14 @@ void exfat_truncate_atime(struct timespec64 *ts)
ts->tv_nsec = 0;
}
void exfat_truncate_inode_atime(struct inode *inode)
{
struct timespec64 atime = inode_get_atime(inode);
exfat_truncate_atime(&atime);
inode_set_atime_to_ts(inode, atime);
}
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
{
int i;

View File

@ -204,21 +204,16 @@ const struct dentry_operations exfat_utf8_dentry_ops = {
.d_compare = exfat_utf8_d_cmp,
};
/* used only in search empty_slot() */
#define CNT_UNUSED_NOHIT (-1)
#define CNT_UNUSED_HIT (-2)
/* search EMPTY CONTINUOUS "num_entries" entries */
static int exfat_search_empty_slot(struct super_block *sb,
struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir,
int num_entries)
int num_entries, struct exfat_entry_set_cache *es)
{
int i, dentry, num_empty = 0;
int i, dentry, ret;
int dentries_per_clu;
unsigned int type;
struct exfat_chain clu;
struct exfat_dentry *ep;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct buffer_head *bh;
int total_entries = EXFAT_CLU_TO_DEN(p_dir->size, sbi);
dentries_per_clu = sbi->dentries_per_clu;
@ -231,7 +226,7 @@ static int exfat_search_empty_slot(struct super_block *sb,
* Otherwise, and if "dentry + hint_famp->count" is also equal
* to "p_dir->size * dentries_per_clu", it means ENOSPC.
*/
if (dentry + hint_femp->count == p_dir->size * dentries_per_clu &&
if (dentry + hint_femp->count == total_entries &&
num_entries > hint_femp->count)
return -ENOSPC;
@ -242,69 +237,41 @@ static int exfat_search_empty_slot(struct super_block *sb,
dentry = 0;
}
while (clu.dir != EXFAT_EOF_CLUSTER) {
while (dentry + num_entries < total_entries &&
clu.dir != EXFAT_EOF_CLUSTER) {
i = dentry & (dentries_per_clu - 1);
for (; i < dentries_per_clu; i++, dentry++) {
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
type = exfat_get_entry_type(ep);
brelse(bh);
ret = exfat_get_empty_dentry_set(es, sb, &clu, i, num_entries);
if (ret < 0)
return ret;
else if (ret == 0)
return dentry;
if (type == TYPE_UNUSED || type == TYPE_DELETED) {
num_empty++;
if (hint_femp->eidx == EXFAT_HINT_NONE) {
hint_femp->eidx = dentry;
hint_femp->count = CNT_UNUSED_NOHIT;
exfat_chain_set(&hint_femp->cur,
clu.dir, clu.size, clu.flags);
}
dentry += ret;
i += ret;
if (type == TYPE_UNUSED &&
hint_femp->count != CNT_UNUSED_HIT)
hint_femp->count = CNT_UNUSED_HIT;
while (i >= dentries_per_clu) {
if (clu.flags == ALLOC_NO_FAT_CHAIN) {
if (--clu.size > 0)
clu.dir++;
else
clu.dir = EXFAT_EOF_CLUSTER;
} else {
if (hint_femp->eidx != EXFAT_HINT_NONE &&
hint_femp->count == CNT_UNUSED_HIT) {
/* unused empty group means
* an empty group which includes
* unused dentry
*/
exfat_fs_error(sb,
"found bogus dentry(%d) beyond unused empty group(%d) (start_clu : %u, cur_clu : %u)",
dentry, hint_femp->eidx,
p_dir->dir, clu.dir);
if (exfat_get_next_cluster(sb, &clu.dir))
return -EIO;
}
num_empty = 0;
hint_femp->eidx = EXFAT_HINT_NONE;
}
if (num_empty >= num_entries) {
/* found and invalidate hint_femp */
hint_femp->eidx = EXFAT_HINT_NONE;
return (dentry - (num_entries - 1));
}
}
if (clu.flags == ALLOC_NO_FAT_CHAIN) {
if (--clu.size > 0)
clu.dir++;
else
clu.dir = EXFAT_EOF_CLUSTER;
} else {
if (exfat_get_next_cluster(sb, &clu.dir))
return -EIO;
i -= dentries_per_clu;
}
}
hint_femp->eidx = p_dir->size * dentries_per_clu - num_empty;
hint_femp->count = num_empty;
if (num_empty == 0)
hint_femp->eidx = dentry;
hint_femp->count = 0;
if (dentry == total_entries || clu.dir == EXFAT_EOF_CLUSTER)
exfat_chain_set(&hint_femp->cur, EXFAT_EOF_CLUSTER, 0,
clu.flags);
else
hint_femp->cur = clu;
return -ENOSPC;
}
@ -325,7 +292,8 @@ static int exfat_check_max_dentries(struct inode *inode)
* if there isn't any empty slot, expand cluster chain.
*/
static int exfat_find_empty_entry(struct inode *inode,
struct exfat_chain *p_dir, int num_entries)
struct exfat_chain *p_dir, int num_entries,
struct exfat_entry_set_cache *es)
{
int dentry;
unsigned int ret, last_clu;
@ -344,7 +312,7 @@ static int exfat_find_empty_entry(struct inode *inode,
}
while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir,
num_entries)) < 0) {
num_entries, es)) < 0) {
if (dentry == -EIO)
break;
@ -406,6 +374,7 @@ static int exfat_find_empty_entry(struct inode *inode,
i_size_write(inode, size);
ei->i_size_ondisk += sbi->cluster_size;
ei->i_size_aligned += sbi->cluster_size;
ei->valid_size += sbi->cluster_size;
ei->flags = p_dir->flags;
inode->i_blocks += sbi->cluster_size >> 9;
}
@ -498,6 +467,8 @@ static int exfat_add_entry(struct inode *inode, const char *path,
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_uni_name uniname;
struct exfat_chain clu;
struct timespec64 ts = current_time(inode);
struct exfat_entry_set_cache es;
int clu_size = 0;
unsigned int start_clu = EXFAT_FREE_CLUSTER;
@ -512,16 +483,18 @@ static int exfat_add_entry(struct inode *inode, const char *path,
}
/* exfat_find_empty_entry must be called before alloc_cluster() */
dentry = exfat_find_empty_entry(inode, p_dir, num_entries);
dentry = exfat_find_empty_entry(inode, p_dir, num_entries, &es);
if (dentry < 0) {
ret = dentry; /* -EIO or -ENOSPC */
goto out;
}
if (type == TYPE_DIR) {
if (type == TYPE_DIR && !sbi->options.zero_size_dir) {
ret = exfat_alloc_new_dir(inode, &clu);
if (ret)
if (ret) {
exfat_put_dentry_set(&es, false);
goto out;
}
start_clu = clu.dir;
clu_size = sbi->cluster_size;
}
@ -530,12 +503,10 @@ static int exfat_add_entry(struct inode *inode, const char *path,
/* fill the dos name directory entry information of the created file.
* the first cluster is not determined yet. (0)
*/
ret = exfat_init_dir_entry(inode, p_dir, dentry, type,
start_clu, clu_size);
if (ret)
goto out;
exfat_init_dir_entry(&es, type, start_clu, clu_size, &ts);
exfat_init_ext_entry(&es, num_entries, &uniname);
ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname);
ret = exfat_put_dentry_set(&es, IS_DIRSYNC(inode));
if (ret)
goto out;
@ -545,16 +516,21 @@ static int exfat_add_entry(struct inode *inode, const char *path,
info->type = type;
if (type == TYPE_FILE) {
info->attr = ATTR_ARCHIVE;
info->attr = EXFAT_ATTR_ARCHIVE;
info->start_clu = EXFAT_EOF_CLUSTER;
info->size = 0;
info->num_subdirs = 0;
} else {
info->attr = ATTR_SUBDIR;
info->start_clu = start_clu;
info->attr = EXFAT_ATTR_SUBDIR;
if (sbi->options.zero_size_dir)
info->start_clu = EXFAT_EOF_CLUSTER;
else
info->start_clu = start_clu;
info->size = clu_size;
info->num_subdirs = EXFAT_MIN_SUBDIR;
}
info->valid_size = info->size;
memset(&info->crtime, 0, sizeof(info->crtime));
memset(&info->mtime, 0, sizeof(info->mtime));
memset(&info->atime, 0, sizeof(info->atime));
@ -580,7 +556,7 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
goto unlock;
inode_inc_iversion(dir);
dir->i_mtime = inode_set_ctime_current(dir);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -593,8 +569,9 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
goto unlock;
inode_inc_iversion(inode);
inode->i_mtime = inode->i_atime = EXFAT_I(inode)->i_crtime = inode_set_ctime_current(inode);
exfat_truncate_atime(&inode->i_atime);
EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode);
exfat_truncate_inode_atime(inode);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@ -656,6 +633,8 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
info->type = exfat_get_entry_type(ep);
info->attr = le16_to_cpu(ep->dentry.file.attr);
info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size);
info->size = le64_to_cpu(ep2->dentry.stream.size);
if (info->size == 0) {
info->flags = ALLOC_NO_FAT_CHAIN;
info->start_clu = EXFAT_EOF_CLUSTER;
@ -786,12 +765,11 @@ unlock:
static int exfat_unlink(struct inode *dir, struct dentry *dentry)
{
struct exfat_chain cdir;
struct exfat_dentry *ep;
struct super_block *sb = dir->i_sb;
struct inode *inode = dentry->d_inode;
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
int num_entries, entry, err = 0;
struct exfat_entry_set_cache es;
int entry, err = 0;
mutex_lock(&EXFAT_SB(sb)->s_lock);
exfat_chain_dup(&cdir, &ei->dir);
@ -802,41 +780,35 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
goto unlock;
}
ep = exfat_get_dentry(sb, &cdir, entry, &bh);
if (!ep) {
err = exfat_get_dentry_set(&es, sb, &cdir, entry, ES_ALL_ENTRIES);
if (err) {
err = -EIO;
goto unlock;
}
num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep);
if (num_entries < 0) {
err = -EIO;
brelse(bh);
goto unlock;
}
num_entries++;
brelse(bh);
exfat_set_volume_dirty(sb);
/* update the directory entry */
if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) {
err = -EIO;
exfat_remove_entries(inode, &es, ES_IDX_FILE);
err = exfat_put_dentry_set(&es, IS_DIRSYNC(inode));
if (err)
goto unlock;
}
/* This doesn't modify ei */
ei->dir.dir = DIR_DELETED;
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = inode_set_ctime_current(dir);
exfat_truncate_atime(&dir->i_atime);
simple_inode_init_ts(dir);
exfat_truncate_inode_atime(dir);
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
mark_inode_dirty(dir);
clear_nlink(inode);
inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
exfat_truncate_atime(&inode->i_atime);
simple_inode_init_ts(inode);
exfat_truncate_inode_atime(inode);
exfat_unhash_inode(inode);
exfat_d_version_set(dentry, inode_query_iversion(dir));
unlock:
@ -862,7 +834,7 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
goto unlock;
inode_inc_iversion(dir);
dir->i_mtime = inode_set_ctime_current(dir);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -876,8 +848,8 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
goto unlock;
inode_inc_iversion(inode);
inode->i_mtime = inode->i_atime = EXFAT_I(inode)->i_crtime = inode_set_ctime_current(inode);
exfat_truncate_atime(&inode->i_atime);
EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode);
exfat_truncate_inode_atime(inode);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@ -937,13 +909,12 @@ static int exfat_check_dir_empty(struct super_block *sb,
static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct exfat_dentry *ep;
struct exfat_chain cdir, clu_to_free;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
int num_entries, entry, err;
struct exfat_entry_set_cache es;
int entry, err;
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
@ -967,32 +938,25 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
goto unlock;
}
ep = exfat_get_dentry(sb, &cdir, entry, &bh);
if (!ep) {
err = exfat_get_dentry_set(&es, sb, &cdir, entry, ES_ALL_ENTRIES);
if (err) {
err = -EIO;
goto unlock;
}
num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep);
if (num_entries < 0) {
err = -EIO;
brelse(bh);
goto unlock;
}
num_entries++;
brelse(bh);
exfat_set_volume_dirty(sb);
err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries);
if (err) {
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
exfat_remove_entries(inode, &es, ES_IDX_FILE);
err = exfat_put_dentry_set(&es, IS_DIRSYNC(dir));
if (err)
goto unlock;
}
ei->dir.dir = DIR_DELETED;
inode_inc_iversion(dir);
dir->i_mtime = dir->i_atime = inode_set_ctime_current(dir);
exfat_truncate_atime(&dir->i_atime);
simple_inode_init_ts(dir);
exfat_truncate_inode_atime(dir);
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -1000,8 +964,8 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
drop_nlink(dir);
clear_nlink(inode);
inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
exfat_truncate_atime(&inode->i_atime);
simple_inode_init_ts(inode);
exfat_truncate_inode_atime(inode);
exfat_unhash_inode(inode);
exfat_d_version_set(dentry, inode_query_iversion(dir));
unlock:
@ -1013,153 +977,125 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
int oldentry, struct exfat_uni_name *p_uniname,
struct exfat_inode_info *ei)
{
int ret, num_old_entries, num_new_entries;
int ret, num_new_entries;
struct exfat_dentry *epold, *epnew;
struct super_block *sb = inode->i_sb;
struct buffer_head *new_bh, *old_bh;
struct exfat_entry_set_cache old_es, new_es;
int sync = IS_DIRSYNC(inode);
epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh);
if (!epold)
return -EIO;
num_old_entries = exfat_count_ext_entries(sb, p_dir, oldentry, epold);
if (num_old_entries < 0)
return -EIO;
num_old_entries++;
num_new_entries = exfat_calc_num_entries(p_uniname);
if (num_new_entries < 0)
return num_new_entries;
if (num_old_entries < num_new_entries) {
ret = exfat_get_dentry_set(&old_es, sb, p_dir, oldentry, ES_ALL_ENTRIES);
if (ret) {
ret = -EIO;
return ret;
}
epold = exfat_get_dentry_cached(&old_es, ES_IDX_FILE);
if (old_es.num_entries < num_new_entries) {
int newentry;
newentry =
exfat_find_empty_entry(inode, p_dir, num_new_entries);
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh);
if (!epnew)
return -EIO;
newentry = exfat_find_empty_entry(inode, p_dir, num_new_entries,
&new_es);
if (newentry < 0) {
ret = newentry; /* -EIO or -ENOSPC */
goto put_old_es;
}
epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE);
*epnew = *epold;
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
ei->attr |= ATTR_ARCHIVE;
}
exfat_update_bh(new_bh, sync);
brelse(old_bh);
brelse(new_bh);
epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh);
if (!epold)
return -EIO;
epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh);
if (!epnew) {
brelse(old_bh);
return -EIO;
epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
ei->attr |= EXFAT_ATTR_ARCHIVE;
}
epold = exfat_get_dentry_cached(&old_es, ES_IDX_STREAM);
epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM);
*epnew = *epold;
exfat_update_bh(new_bh, sync);
brelse(old_bh);
brelse(new_bh);
ret = exfat_init_ext_entry(inode, p_dir, newentry,
num_new_entries, p_uniname);
exfat_init_ext_entry(&new_es, num_new_entries, p_uniname);
ret = exfat_put_dentry_set(&new_es, sync);
if (ret)
return ret;
goto put_old_es;
exfat_remove_entries(inode, p_dir, oldentry, 0,
num_old_entries);
exfat_remove_entries(inode, &old_es, ES_IDX_FILE);
ei->dir = *p_dir;
ei->entry = newentry;
} else {
if (exfat_get_entry_type(epold) == TYPE_FILE) {
epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
ei->attr |= ATTR_ARCHIVE;
epold->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
ei->attr |= EXFAT_ATTR_ARCHIVE;
}
exfat_update_bh(old_bh, sync);
brelse(old_bh);
ret = exfat_init_ext_entry(inode, p_dir, oldentry,
num_new_entries, p_uniname);
if (ret)
return ret;
exfat_remove_entries(inode, p_dir, oldentry, num_new_entries,
num_old_entries);
exfat_remove_entries(inode, &old_es, ES_IDX_FIRST_FILENAME + 1);
exfat_init_ext_entry(&old_es, num_new_entries, p_uniname);
}
return 0;
return exfat_put_dentry_set(&old_es, sync);
put_old_es:
exfat_put_dentry_set(&old_es, false);
return ret;
}
static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
int oldentry, struct exfat_chain *p_newdir,
struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, newentry, num_new_entries, num_old_entries;
int ret, newentry, num_new_entries;
struct exfat_dentry *epmov, *epnew;
struct super_block *sb = inode->i_sb;
struct buffer_head *mov_bh, *new_bh;
epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh);
if (!epmov)
return -EIO;
num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry,
epmov);
if (num_old_entries < 0)
return -EIO;
num_old_entries++;
struct exfat_entry_set_cache mov_es, new_es;
num_new_entries = exfat_calc_num_entries(p_uniname);
if (num_new_entries < 0)
return num_new_entries;
newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries);
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh);
if (!epnew)
ret = exfat_get_dentry_set(&mov_es, sb, p_olddir, oldentry,
ES_ALL_ENTRIES);
if (ret)
return -EIO;
newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries,
&new_es);
if (newentry < 0) {
ret = newentry; /* -EIO or -ENOSPC */
goto put_mov_es;
}
epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_FILE);
epnew = exfat_get_dentry_cached(&new_es, ES_IDX_FILE);
*epnew = *epmov;
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
ei->attr |= ATTR_ARCHIVE;
}
exfat_update_bh(new_bh, IS_DIRSYNC(inode));
brelse(mov_bh);
brelse(new_bh);
epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh);
if (!epmov)
return -EIO;
epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh);
if (!epnew) {
brelse(mov_bh);
return -EIO;
epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
ei->attr |= EXFAT_ATTR_ARCHIVE;
}
epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_STREAM);
epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM);
*epnew = *epmov;
exfat_update_bh(new_bh, IS_DIRSYNC(inode));
brelse(mov_bh);
brelse(new_bh);
ret = exfat_init_ext_entry(inode, p_newdir, newentry, num_new_entries,
p_uniname);
if (ret)
return ret;
exfat_remove_entries(inode, p_olddir, oldentry, 0, num_old_entries);
exfat_init_ext_entry(&new_es, num_new_entries, p_uniname);
exfat_remove_entries(inode, &mov_es, ES_IDX_FILE);
exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size,
p_newdir->flags);
ei->entry = newentry;
return 0;
ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(inode));
if (ret)
goto put_mov_es;
return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(inode));
put_mov_es:
exfat_put_dentry_set(&mov_es, false);
return ret;
}
/* rename or move a old file into a new file */
@ -1177,7 +1113,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
struct exfat_sb_info *sbi = EXFAT_SB(sb);
const unsigned char *new_path = new_dentry->d_name.name;
struct inode *new_inode = new_dentry->d_inode;
int num_entries;
struct exfat_inode_info *new_ei = NULL;
unsigned int new_entry_type = TYPE_UNUSED;
int new_entry = 0;
@ -1248,25 +1183,21 @@ static int __exfat_rename(struct inode *old_parent_inode,
&newdir, &uni_name, ei);
if (!ret && new_inode) {
struct exfat_entry_set_cache es;
/* delete entries of new_dir */
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
if (!ep) {
ret = exfat_get_dentry_set(&es, sb, p_dir, new_entry,
ES_ALL_ENTRIES);
if (ret) {
ret = -EIO;
goto del_out;
}
num_entries = exfat_count_ext_entries(sb, p_dir, new_entry, ep);
if (num_entries < 0) {
ret = -EIO;
goto del_out;
}
brelse(new_bh);
exfat_remove_entries(new_inode, &es, ES_IDX_FILE);
if (exfat_remove_entries(new_inode, p_dir, new_entry, 0,
num_entries + 1)) {
ret = -EIO;
ret = exfat_put_dentry_set(&es, IS_DIRSYNC(new_inode));
if (ret)
goto del_out;
}
/* Free the clusters if new_inode is a dir(as if exfat_rmdir) */
if (new_entry_type == TYPE_DIR &&
@ -1284,6 +1215,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
}
i_size_write(new_inode, 0);
new_ei->valid_size = 0;
new_ei->start_clu = EXFAT_EOF_CLUSTER;
new_ei->flags = ALLOC_NO_FAT_CHAIN;
}
@ -1327,7 +1259,7 @@ static int exfat_rename(struct mnt_idmap *idmap,
inode_inc_iversion(new_dir);
simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
exfat_truncate_atime(&new_dir->i_atime);
exfat_truncate_inode_atime(new_dir);
if (IS_DIRSYNC(new_dir))
exfat_sync_inode(new_dir);
else

View File

@ -655,7 +655,6 @@ static int exfat_load_upcase_table(struct super_block *sb,
unsigned int sect_size = sb->s_blocksize;
unsigned int i, index = 0;
u32 chksum = 0;
int ret;
unsigned char skip = false;
unsigned short *upcase_table;
@ -673,8 +672,7 @@ static int exfat_load_upcase_table(struct super_block *sb,
if (!bh) {
exfat_err(sb, "failed to read sector(0x%llx)",
(unsigned long long)sector);
ret = -EIO;
goto free_table;
return -EIO;
}
sector++;
for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) {
@ -701,15 +699,12 @@ static int exfat_load_upcase_table(struct super_block *sb,
exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
index, chksum, utbl_checksum);
ret = -EINVAL;
free_table:
exfat_free_upcase_table(sbi);
return ret;
return -EINVAL;
}
static int exfat_load_default_upcase_table(struct super_block *sb)
{
int i, ret = -EIO;
int i;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
unsigned char skip = false;
unsigned short uni = 0, *upcase_table;
@ -740,8 +735,7 @@ static int exfat_load_default_upcase_table(struct super_block *sb)
return 0;
/* FATAL error: default upcase table has error */
exfat_free_upcase_table(sbi);
return ret;
return -EIO;
}
int exfat_create_upcase_table(struct super_block *sb)

View File

@ -39,9 +39,6 @@ static void exfat_put_super(struct super_block *sb)
exfat_free_bitmap(sbi);
brelse(sbi->boot_bh);
mutex_unlock(&sbi->s_lock);
unload_nls(sbi->nls_io);
exfat_free_upcase_table(sbi);
}
static int exfat_sync_fs(struct super_block *sb, int wait)
@ -165,6 +162,8 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",sys_tz");
else if (opts->time_offset)
seq_printf(m, ",time_offset=%d", opts->time_offset);
if (opts->zero_size_dir)
seq_puts(m, ",zero_size_dir");
return 0;
}
@ -209,6 +208,7 @@ enum {
Opt_keep_last_dots,
Opt_sys_tz,
Opt_time_offset,
Opt_zero_size_dir,
/* Deprecated options */
Opt_utf8,
@ -237,6 +237,7 @@ static const struct fs_parameter_spec exfat_parameters[] = {
fsparam_flag("keep_last_dots", Opt_keep_last_dots),
fsparam_flag("sys_tz", Opt_sys_tz),
fsparam_s32("time_offset", Opt_time_offset),
fsparam_flag("zero_size_dir", Opt_zero_size_dir),
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
NULL),
__fsparam(NULL, "debug", Opt_debug, fs_param_deprecated,
@ -305,6 +306,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
return -EINVAL;
opts->time_offset = result.int_32;
break;
case Opt_zero_size_dir:
opts->zero_size_dir = true;
break;
case Opt_utf8:
case Opt_debug:
case Opt_namecase:
@ -360,7 +364,7 @@ static int exfat_read_root(struct inode *inode)
inode->i_gid = sbi->options.fs_gid;
inode_inc_iversion(inode);
inode->i_generation = 0;
inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777);
inode->i_mode = exfat_make_mode(sbi, EXFAT_ATTR_SUBDIR, 0777);
inode->i_op = &exfat_dir_inode_operations;
inode->i_fop = &exfat_dir_operations;
@ -369,9 +373,9 @@ static int exfat_read_root(struct inode *inode)
ei->i_size_aligned = i_size_read(inode);
ei->i_size_ondisk = i_size_read(inode);
exfat_save_attr(inode, ATTR_SUBDIR);
inode->i_mtime = inode->i_atime = ei->i_crtime = inode_set_ctime_current(inode);
exfat_truncate_atime(&inode->i_atime);
exfat_save_attr(inode, EXFAT_ATTR_SUBDIR);
ei->i_crtime = simple_inode_init_ts(inode);
exfat_truncate_inode_atime(inode);
return 0;
}
@ -593,7 +597,7 @@ static int __exfat_fill_super(struct super_block *sb)
ret = exfat_load_bitmap(sb);
if (ret) {
exfat_err(sb, "failed to load alloc-bitmap");
goto free_upcase_table;
goto free_bh;
}
ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
@ -606,8 +610,6 @@ static int __exfat_fill_super(struct super_block *sb)
free_alloc_bitmap:
exfat_free_bitmap(sbi);
free_upcase_table:
exfat_free_upcase_table(sbi);
free_bh:
brelse(sbi->boot_bh);
return ret;
@ -694,12 +696,10 @@ put_inode:
sb->s_root = NULL;
free_table:
exfat_free_upcase_table(sbi);
exfat_free_bitmap(sbi);
brelse(sbi->boot_bh);
check_nls_io:
unload_nls(sbi->nls_io);
return err;
}
@ -764,13 +764,22 @@ static int exfat_init_fs_context(struct fs_context *fc)
return 0;
}
static void delayed_free(struct rcu_head *p)
{
struct exfat_sb_info *sbi = container_of(p, struct exfat_sb_info, rcu);
unload_nls(sbi->nls_io);
exfat_free_upcase_table(sbi);
exfat_free_sbi(sbi);
}
static void exfat_kill_sb(struct super_block *sb)
{
struct exfat_sb_info *sbi = sb->s_fs_info;
kill_block_super(sb);
if (sbi)
exfat_free_sbi(sbi);
call_rcu(&sbi->rcu, delayed_free);
}
static struct file_system_type exfat_fs_type = {

View File

@ -99,7 +99,7 @@ repeat:
}
if (unlikely(!PageUptodate(page))) {
f2fs_handle_page_eio(sbi, page->index, META);
f2fs_handle_page_eio(sbi, page_folio(page), META);
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
}
@ -345,30 +345,31 @@ static int __f2fs_write_meta_page(struct page *page,
enum iostat_type io_type)
{
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
struct folio *folio = page_folio(page);
trace_f2fs_writepage(page_folio(page), META);
trace_f2fs_writepage(folio, META);
if (unlikely(f2fs_cp_error(sbi))) {
if (is_sbi_flag_set(sbi, SBI_IS_CLOSE)) {
ClearPageUptodate(page);
folio_clear_uptodate(folio);
dec_page_count(sbi, F2FS_DIRTY_META);
unlock_page(page);
folio_unlock(folio);
return 0;
}
goto redirty_out;
}
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
goto redirty_out;
if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0))
if (wbc->for_reclaim && folio->index < GET_SUM_BLOCK(sbi, 0))
goto redirty_out;
f2fs_do_write_meta_page(sbi, page, io_type);
f2fs_do_write_meta_page(sbi, folio, io_type);
dec_page_count(sbi, F2FS_DIRTY_META);
if (wbc->for_reclaim)
f2fs_submit_merged_write_cond(sbi, NULL, page, 0, META);
unlock_page(page);
folio_unlock(folio);
if (unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_write(sbi, META);
@ -1551,7 +1552,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
blk = start_blk + BLKS_PER_SEG(sbi) - nm_i->nat_bits_blocks;
for (i = 0; i < nm_i->nat_bits_blocks; i++)
f2fs_update_meta_page(sbi, nm_i->nat_bits +
(i << F2FS_BLKSIZE_BITS), blk + i);
F2FS_BLK_TO_BYTES(i), blk + i);
}
/* write out checkpoint buffer at block 0 */

View File

@ -90,11 +90,13 @@ bool f2fs_is_compressed_page(struct page *page)
static void f2fs_set_compressed_page(struct page *page,
struct inode *inode, pgoff_t index, void *data)
{
attach_page_private(page, (void *)data);
struct folio *folio = page_folio(page);
folio_attach_private(folio, (void *)data);
/* i_crypto_info and iv index */
page->index = index;
page->mapping = inode->i_mapping;
folio->index = index;
folio->mapping = inode->i_mapping;
}
static void f2fs_drop_rpages(struct compress_ctx *cc, int len, bool unlock)
@ -160,17 +162,17 @@ void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse)
cc->cluster_idx = NULL_CLUSTER;
}
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page)
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct folio *folio)
{
unsigned int cluster_ofs;
if (!f2fs_cluster_can_merge_page(cc, page->index))
if (!f2fs_cluster_can_merge_page(cc, folio->index))
f2fs_bug_on(F2FS_I_SB(cc->inode), 1);
cluster_ofs = offset_in_cluster(cc, page->index);
cc->rpages[cluster_ofs] = page;
cluster_ofs = offset_in_cluster(cc, folio->index);
cc->rpages[cluster_ofs] = folio_page(folio, 0);
cc->nr_rpages++;
cc->cluster_idx = cluster_idx(cc, page->index);
cc->cluster_idx = cluster_idx(cc, folio->index);
}
#ifdef CONFIG_F2FS_FS_LZO
@ -879,7 +881,7 @@ static bool cluster_has_invalid_data(struct compress_ctx *cc)
f2fs_bug_on(F2FS_I_SB(cc->inode), !page);
/* beyond EOF */
if (page->index >= nr_pages)
if (page_folio(page)->index >= nr_pages)
return true;
}
return false;
@ -945,7 +947,7 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
int count, i;
for (i = 1, count = 1; i < cluster_size; i++) {
for (i = 0, count = 0; i < cluster_size; i++) {
block_t blkaddr = data_blkaddr(dn->inode, dn->node_page,
dn->ofs_in_node + i);
@ -956,8 +958,8 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
return count;
}
static int __f2fs_cluster_blocks(struct inode *inode,
unsigned int cluster_idx, bool compr_blks)
static int __f2fs_cluster_blocks(struct inode *inode, unsigned int cluster_idx,
enum cluster_check_type type)
{
struct dnode_of_data dn;
unsigned int start_idx = cluster_idx <<
@ -978,10 +980,12 @@ static int __f2fs_cluster_blocks(struct inode *inode,
}
if (dn.data_blkaddr == COMPRESS_ADDR) {
if (compr_blks)
ret = __f2fs_get_cluster_blocks(inode, &dn);
else
if (type == CLUSTER_COMPR_BLKS)
ret = 1 + __f2fs_get_cluster_blocks(inode, &dn);
else if (type == CLUSTER_IS_COMPR)
ret = 1;
} else if (type == CLUSTER_RAW_BLKS) {
ret = __f2fs_get_cluster_blocks(inode, &dn);
}
fail:
f2fs_put_dnode(&dn);
@ -991,7 +995,16 @@ fail:
/* return # of compressed blocks in compressed cluster */
static int f2fs_compressed_blocks(struct compress_ctx *cc)
{
return __f2fs_cluster_blocks(cc->inode, cc->cluster_idx, true);
return __f2fs_cluster_blocks(cc->inode, cc->cluster_idx,
CLUSTER_COMPR_BLKS);
}
/* return # of raw blocks in non-compressed cluster */
static int f2fs_decompressed_blocks(struct inode *inode,
unsigned int cluster_idx)
{
return __f2fs_cluster_blocks(inode, cluster_idx,
CLUSTER_RAW_BLKS);
}
/* return whether cluster is compressed one or not */
@ -999,7 +1012,16 @@ int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index)
{
return __f2fs_cluster_blocks(inode,
index >> F2FS_I(inode)->i_log_cluster_size,
false);
CLUSTER_IS_COMPR);
}
/* return whether cluster contains non raw blocks or not */
bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index)
{
unsigned int cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size;
return f2fs_decompressed_blocks(inode, cluster_idx) !=
F2FS_I(inode)->i_cluster_size;
}
static bool cluster_may_compress(struct compress_ctx *cc)
@ -1093,7 +1115,7 @@ retry:
if (PageUptodate(page))
f2fs_put_page(page, 1);
else
f2fs_compress_ctx_add_page(cc, page);
f2fs_compress_ctx_add_page(cc, page_folio(page));
}
if (!f2fs_cluster_is_empty(cc)) {
@ -1123,7 +1145,7 @@ retry:
}
f2fs_wait_on_page_writeback(page, DATA, true, true);
f2fs_compress_ctx_add_page(cc, page);
f2fs_compress_ctx_add_page(cc, page_folio(page));
if (!PageUptodate(page)) {
release_and_retry:
@ -1523,7 +1545,8 @@ continue_unlock:
if (!clear_page_dirty_for_io(cc->rpages[i]))
goto continue_unlock;
ret = f2fs_write_single_data_page(cc->rpages[i], &submitted,
ret = f2fs_write_single_data_page(page_folio(cc->rpages[i]),
&submitted,
NULL, NULL, wbc, io_type,
compr_blocks, false);
if (ret) {

View File

@ -7,7 +7,6 @@
*/
#include <linux/fs.h>
#include <linux/f2fs_fs.h>
#include <linux/buffer_head.h>
#include <linux/sched/mm.h>
#include <linux/mpage.h>
#include <linux/writeback.h>
@ -357,7 +356,7 @@ static void f2fs_write_end_io(struct bio *bio)
}
f2fs_bug_on(sbi, page->mapping == NODE_MAPPING(sbi) &&
page->index != nid_of_node(page));
page_folio(page)->index != nid_of_node(page));
dec_page_count(sbi, type);
if (f2fs_in_warm_node_list(sbi, page))
@ -710,7 +709,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
bio = __bio_alloc(fio, 1);
f2fs_set_bio_crypt_ctx(bio, fio->page->mapping->host,
fio->page->index, fio, GFP_NOIO);
page_folio(fio->page)->index, fio, GFP_NOIO);
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
bio_put(bio);
@ -809,7 +808,7 @@ static int add_ipu_page(struct f2fs_io_info *fio, struct bio **bio,
fio->new_blkaddr));
if (f2fs_crypt_mergeable_bio(*bio,
fio->page->mapping->host,
fio->page->index, fio) &&
page_folio(fio->page)->index, fio) &&
bio_add_page(*bio, page, PAGE_SIZE, 0) ==
PAGE_SIZE) {
ret = 0;
@ -909,7 +908,7 @@ alloc_new:
if (!bio) {
bio = __bio_alloc(fio, BIO_MAX_VECS);
f2fs_set_bio_crypt_ctx(bio, fio->page->mapping->host,
fio->page->index, fio, GFP_NOIO);
page_folio(fio->page)->index, fio, GFP_NOIO);
add_bio_entry(fio->sbi, bio, page, fio->temp);
} else {
@ -1002,13 +1001,13 @@ next:
(!io_is_mergeable(sbi, io->bio, io, fio, io->last_block_in_bio,
fio->new_blkaddr) ||
!f2fs_crypt_mergeable_bio(io->bio, fio->page->mapping->host,
bio_page->index, fio)))
page_folio(bio_page)->index, fio)))
__submit_merged_bio(io);
alloc_new:
if (io->bio == NULL) {
io->bio = __bio_alloc(fio, BIO_MAX_VECS);
f2fs_set_bio_crypt_ctx(io->bio, fio->page->mapping->host,
bio_page->index, fio, GFP_NOIO);
page_folio(bio_page)->index, fio, GFP_NOIO);
io->fio = *fio;
}
@ -1093,7 +1092,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
}
/* This can handle encryption stuffs */
static int f2fs_submit_page_read(struct inode *inode, struct page *page,
static int f2fs_submit_page_read(struct inode *inode, struct folio *folio,
block_t blkaddr, blk_opf_t op_flags,
bool for_write)
{
@ -1101,14 +1100,14 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page,
struct bio *bio;
bio = f2fs_grab_read_bio(inode, blkaddr, 1, op_flags,
page->index, for_write);
folio->index, for_write);
if (IS_ERR(bio))
return PTR_ERR(bio);
/* wait for GCed page writeback via META_MAPPING */
f2fs_wait_on_block_writeback(inode, blkaddr);
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
if (!bio_add_folio(bio, folio, PAGE_SIZE, 0)) {
iostat_update_and_unbind_ctx(bio);
if (bio->bi_private)
mempool_free(bio->bi_private, bio_post_read_ctx_pool);
@ -1276,7 +1275,7 @@ got_it:
return page;
}
err = f2fs_submit_page_read(inode, page, dn.data_blkaddr,
err = f2fs_submit_page_read(inode, page_folio(page), dn.data_blkaddr,
op_flags, for_write);
if (err)
goto put_err;
@ -1719,6 +1718,14 @@ skip:
dn.ofs_in_node = end_offset;
}
if (flag == F2FS_GET_BLOCK_DIO && f2fs_lfs_mode(sbi) &&
map->m_may_create) {
/* the next block to be allocated may not be contiguous. */
if (GET_SEGOFF_FROM_SEG0(sbi, blkaddr) % BLKS_PER_SEC(sbi) ==
CAP_BLKS_PER_SEC(sbi) - 1)
goto sync_out;
}
if (pgofs >= end)
goto sync_out;
else if (dn.ofs_in_node < end_offset)
@ -1945,7 +1952,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
inode_lock_shared(inode);
maxbytes = max_file_blocks(inode) << F2FS_BLKSIZE_BITS;
maxbytes = F2FS_BLK_TO_BYTES(max_file_blocks(inode));
if (start > maxbytes) {
ret = -EFBIG;
goto out;
@ -2070,7 +2077,7 @@ out:
static inline loff_t f2fs_readpage_limit(struct inode *inode)
{
if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
return inode->i_sb->s_maxbytes;
return F2FS_BLK_TO_BYTES(max_file_blocks(inode));
return i_size_read(inode);
}
@ -2230,19 +2237,22 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
/* get rid of pages beyond EOF */
for (i = 0; i < cc->cluster_size; i++) {
struct page *page = cc->rpages[i];
struct folio *folio;
if (!page)
continue;
if ((sector_t)page->index >= last_block_in_file) {
zero_user_segment(page, 0, PAGE_SIZE);
if (!PageUptodate(page))
SetPageUptodate(page);
} else if (!PageUptodate(page)) {
folio = page_folio(page);
if ((sector_t)folio->index >= last_block_in_file) {
folio_zero_segment(folio, 0, folio_size(folio));
if (!folio_test_uptodate(folio))
folio_mark_uptodate(folio);
} else if (!folio_test_uptodate(folio)) {
continue;
}
unlock_page(page);
folio_unlock(folio);
if (for_write)
put_page(page);
folio_put(folio);
cc->rpages[i] = NULL;
cc->nr_rpages--;
}
@ -2302,7 +2312,7 @@ skip_reading_dnode:
}
for (i = 0; i < cc->nr_cpages; i++) {
struct page *page = dic->cpages[i];
struct folio *folio = page_folio(dic->cpages[i]);
block_t blkaddr;
struct bio_post_read_ctx *ctx;
@ -2312,7 +2322,8 @@ skip_reading_dnode:
f2fs_wait_on_block_writeback(inode, blkaddr);
if (f2fs_load_compressed_page(sbi, page, blkaddr)) {
if (f2fs_load_compressed_page(sbi, folio_page(folio, 0),
blkaddr)) {
if (atomic_dec_and_test(&dic->remaining_pages)) {
f2fs_decompress_cluster(dic, true);
break;
@ -2322,7 +2333,7 @@ skip_reading_dnode:
if (bio && (!page_is_mergeable(sbi, bio,
*last_block_in_bio, blkaddr) ||
!f2fs_crypt_mergeable_bio(bio, inode, page->index, NULL))) {
!f2fs_crypt_mergeable_bio(bio, inode, folio->index, NULL))) {
submit_and_realloc:
f2fs_submit_read_bio(sbi, bio, DATA);
bio = NULL;
@ -2331,7 +2342,7 @@ submit_and_realloc:
if (!bio) {
bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages,
f2fs_ra_op_flags(rac),
page->index, for_write);
folio->index, for_write);
if (IS_ERR(bio)) {
ret = PTR_ERR(bio);
f2fs_decompress_end_io(dic, ret, true);
@ -2341,7 +2352,7 @@ submit_and_realloc:
}
}
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
if (!bio_add_folio(bio, folio, blocksize, 0))
goto submit_and_realloc;
ctx = get_post_read_ctx(bio);
@ -2452,7 +2463,7 @@ static int f2fs_mpage_readpages(struct inode *inode,
if (ret)
goto set_error_page;
f2fs_compress_ctx_add_page(&cc, &folio->page);
f2fs_compress_ctx_add_page(&cc, folio);
goto next_page;
read_single_page:
@ -2667,21 +2678,24 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
int f2fs_do_write_data_page(struct f2fs_io_info *fio)
{
struct page *page = fio->page;
struct inode *inode = page->mapping->host;
struct folio *folio = page_folio(fio->page);
struct inode *inode = folio->mapping->host;
struct dnode_of_data dn;
struct node_info ni;
bool ipu_force = false;
bool atomic_commit;
int err = 0;
/* Use COW inode to make dnode_of_data for atomic write */
if (f2fs_is_atomic_file(inode))
atomic_commit = f2fs_is_atomic_file(inode) &&
page_private_atomic(folio_page(folio, 0));
if (atomic_commit)
set_new_dnode(&dn, F2FS_I(inode)->cow_inode, NULL, NULL, 0);
else
set_new_dnode(&dn, inode, NULL, NULL, 0);
if (need_inplace_update(fio) &&
f2fs_lookup_read_extent_cache_block(inode, page->index,
f2fs_lookup_read_extent_cache_block(inode, folio->index,
&fio->old_blkaddr)) {
if (!f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr,
DATA_GENERIC_ENHANCE))
@ -2696,7 +2710,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
if (fio->need_lock == LOCK_REQ && !f2fs_trylock_op(fio->sbi))
return -EAGAIN;
err = f2fs_get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
err = f2fs_get_dnode_of_data(&dn, folio->index, LOOKUP_NODE);
if (err)
goto out;
@ -2704,8 +2718,8 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
/* This page is already truncated */
if (fio->old_blkaddr == NULL_ADDR) {
ClearPageUptodate(page);
clear_page_private_gcing(page);
folio_clear_uptodate(folio);
clear_page_private_gcing(folio_page(folio, 0));
goto out_writepage;
}
got_it:
@ -2731,7 +2745,7 @@ got_it:
if (err)
goto out_writepage;
set_page_writeback(page);
folio_start_writeback(folio);
f2fs_put_dnode(&dn);
if (fio->need_lock == LOCK_REQ)
f2fs_unlock_op(fio->sbi);
@ -2739,11 +2753,11 @@ got_it:
if (err) {
if (fscrypt_inode_uses_fs_layer_crypto(inode))
fscrypt_finalize_bounce_page(&fio->encrypted_page);
end_page_writeback(page);
folio_end_writeback(folio);
} else {
set_inode_flag(inode, FI_UPDATE_WRITE);
}
trace_f2fs_do_write_data_page(page_folio(page), IPU);
trace_f2fs_do_write_data_page(folio, IPU);
return err;
}
@ -2765,15 +2779,17 @@ got_it:
if (err)
goto out_writepage;
set_page_writeback(page);
folio_start_writeback(folio);
if (fio->compr_blocks && fio->old_blkaddr == COMPRESS_ADDR)
f2fs_i_compr_blocks_update(inode, fio->compr_blocks - 1, false);
/* LFS mode write path */
f2fs_outplace_write_data(&dn, fio);
trace_f2fs_do_write_data_page(page_folio(page), OPU);
trace_f2fs_do_write_data_page(folio, OPU);
set_inode_flag(inode, FI_APPEND_WRITE);
if (atomic_commit)
clear_page_private_atomic(folio_page(folio, 0));
out_writepage:
f2fs_put_dnode(&dn);
out:
@ -2782,7 +2798,7 @@ out:
return err;
}
int f2fs_write_single_data_page(struct page *page, int *submitted,
int f2fs_write_single_data_page(struct folio *folio, int *submitted,
struct bio **bio,
sector_t *last_block,
struct writeback_control *wbc,
@ -2790,12 +2806,13 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
int compr_blocks,
bool allow_balance)
{
struct inode *inode = page->mapping->host;
struct inode *inode = folio->mapping->host;
struct page *page = folio_page(folio, 0);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
loff_t i_size = i_size_read(inode);
const pgoff_t end_index = ((unsigned long long)i_size)
>> PAGE_SHIFT;
loff_t psize = (loff_t)(page->index + 1) << PAGE_SHIFT;
loff_t psize = (loff_t)(folio->index + 1) << PAGE_SHIFT;
unsigned offset = 0;
bool need_balance_fs = false;
bool quota_inode = IS_NOQUOTA(inode);
@ -2819,11 +2836,11 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
.last_block = last_block,
};
trace_f2fs_writepage(page_folio(page), DATA);
trace_f2fs_writepage(folio, DATA);
/* we should bypass data pages to proceed the kworker jobs */
if (unlikely(f2fs_cp_error(sbi))) {
mapping_set_error(page->mapping, -EIO);
mapping_set_error(folio->mapping, -EIO);
/*
* don't drop any dirty dentry pages for keeping lastest
* directory structure.
@ -2841,7 +2858,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
goto redirty_out;
if (page->index < end_index ||
if (folio->index < end_index ||
f2fs_verity_in_progress(inode) ||
compr_blocks)
goto write;
@ -2851,10 +2868,10 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
* this page does not have to be written to disk.
*/
offset = i_size & (PAGE_SIZE - 1);
if ((page->index >= end_index + 1) || !offset)
if ((folio->index >= end_index + 1) || !offset)
goto out;
zero_user_segment(page, offset, PAGE_SIZE);
folio_zero_segment(folio, offset, folio_size(folio));
write:
/* Dentry/quota blocks are controlled by checkpoint */
if (S_ISDIR(inode->i_mode) || quota_inode) {
@ -2884,7 +2901,7 @@ write:
err = -EAGAIN;
if (f2fs_has_inline_data(inode)) {
err = f2fs_write_inline_data(inode, page);
err = f2fs_write_inline_data(inode, folio);
if (!err)
goto out;
}
@ -2914,7 +2931,7 @@ done:
out:
inode_dec_dirty_pages(inode);
if (err) {
ClearPageUptodate(page);
folio_clear_uptodate(folio);
clear_page_private_gcing(page);
}
@ -2924,7 +2941,7 @@ out:
f2fs_remove_dirty_inode(inode);
submitted = NULL;
}
unlock_page(page);
folio_unlock(folio);
if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) &&
!F2FS_I(inode)->wb_task && allow_balance)
f2fs_balance_fs(sbi, need_balance_fs);
@ -2942,7 +2959,7 @@ out:
return 0;
redirty_out:
redirty_page_for_writepage(wbc, page);
folio_redirty_for_writepage(wbc, folio);
/*
* pageout() in MM translates EAGAIN, so calls handle_write_error()
* -> mapping_set_error() -> set_bit(AS_EIO, ...).
@ -2951,29 +2968,30 @@ redirty_out:
*/
if (!err || wbc->for_reclaim)
return AOP_WRITEPAGE_ACTIVATE;
unlock_page(page);
folio_unlock(folio);
return err;
}
static int f2fs_write_data_page(struct page *page,
struct writeback_control *wbc)
{
struct folio *folio = page_folio(page);
#ifdef CONFIG_F2FS_FS_COMPRESSION
struct inode *inode = page->mapping->host;
struct inode *inode = folio->mapping->host;
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
goto out;
if (f2fs_compressed_file(inode)) {
if (f2fs_is_compressed_cluster(inode, page->index)) {
redirty_page_for_writepage(wbc, page);
if (f2fs_is_compressed_cluster(inode, folio->index)) {
folio_redirty_for_writepage(wbc, folio);
return AOP_WRITEPAGE_ACTIVATE;
}
}
out:
#endif
return f2fs_write_single_data_page(page, NULL, NULL, NULL,
return f2fs_write_single_data_page(folio, NULL, NULL, NULL,
wbc, FS_DATA_IO, 0, true);
}
@ -3179,11 +3197,11 @@ continue_unlock:
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (f2fs_compressed_file(inode)) {
folio_get(folio);
f2fs_compress_ctx_add_page(&cc, &folio->page);
f2fs_compress_ctx_add_page(&cc, folio);
continue;
}
#endif
ret = f2fs_write_single_data_page(&folio->page,
ret = f2fs_write_single_data_page(folio,
&submitted, &bio, &last_block,
wbc, io_type, 0, true);
if (ret == AOP_WRITEPAGE_ACTIVATE)
@ -3391,11 +3409,11 @@ void f2fs_write_failed(struct inode *inode, loff_t to)
}
static int prepare_write_begin(struct f2fs_sb_info *sbi,
struct page *page, loff_t pos, unsigned len,
struct folio *folio, loff_t pos, unsigned int len,
block_t *blk_addr, bool *node_changed)
{
struct inode *inode = page->mapping->host;
pgoff_t index = page->index;
struct inode *inode = folio->mapping->host;
pgoff_t index = folio->index;
struct dnode_of_data dn;
struct page *ipage;
bool locked = false;
@ -3432,13 +3450,13 @@ restart:
if (f2fs_has_inline_data(inode)) {
if (pos + len <= MAX_INLINE_DATA(inode)) {
f2fs_do_read_inline_data(page_folio(page), ipage);
f2fs_do_read_inline_data(folio, ipage);
set_inode_flag(inode, FI_DATA_EXIST);
if (inode->i_nlink)
set_page_private_inline(ipage);
goto out;
}
err = f2fs_convert_inline_page(&dn, page);
err = f2fs_convert_inline_page(&dn, folio_page(folio, 0));
if (err || dn.data_blkaddr != NULL_ADDR)
goto out;
}
@ -3531,12 +3549,12 @@ unlock_out:
}
static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
struct page *page, loff_t pos, unsigned int len,
struct folio *folio, loff_t pos, unsigned int len,
block_t *blk_addr, bool *node_changed, bool *use_cow)
{
struct inode *inode = page->mapping->host;
struct inode *inode = folio->mapping->host;
struct inode *cow_inode = F2FS_I(inode)->cow_inode;
pgoff_t index = page->index;
pgoff_t index = folio->index;
int err = 0;
block_t ori_blk_addr = NULL_ADDR;
@ -3579,6 +3597,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
struct inode *inode = mapping->host;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct page *page = NULL;
struct folio *folio;
pgoff_t index = ((unsigned long long) pos) >> PAGE_SHIFT;
bool need_balance = false;
bool use_cow = false;
@ -3638,22 +3657,23 @@ repeat:
/* TODO: cluster can be compressed due to race with .writepage */
*pagep = page;
folio = page_folio(page);
if (f2fs_is_atomic_file(inode))
err = prepare_atomic_write_begin(sbi, page, pos, len,
err = prepare_atomic_write_begin(sbi, folio, pos, len,
&blkaddr, &need_balance, &use_cow);
else
err = prepare_write_begin(sbi, page, pos, len,
err = prepare_write_begin(sbi, folio, pos, len,
&blkaddr, &need_balance);
if (err)
goto fail;
if (need_balance && !IS_NOQUOTA(inode) &&
has_not_enough_free_secs(sbi, 0, 0)) {
unlock_page(page);
folio_unlock(folio);
f2fs_balance_fs(sbi, true);
lock_page(page);
if (page->mapping != mapping) {
folio_lock(folio);
if (folio->mapping != mapping) {
/* The page got truncated from under us */
f2fs_put_page(page, 1);
goto repeat;
@ -3662,18 +3682,18 @@ repeat:
f2fs_wait_on_page_writeback(page, DATA, false, true);
if (len == PAGE_SIZE || PageUptodate(page))
if (len == PAGE_SIZE || folio_test_uptodate(folio))
return 0;
if (!(pos & (PAGE_SIZE - 1)) && (pos + len) >= i_size_read(inode) &&
!f2fs_verity_in_progress(inode)) {
zero_user_segment(page, len, PAGE_SIZE);
folio_zero_segment(folio, len, folio_size(folio));
return 0;
}
if (blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_SIZE);
SetPageUptodate(page);
folio_zero_segment(folio, 0, folio_size(folio));
folio_mark_uptodate(folio);
} else {
if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
DATA_GENERIC_ENHANCE_READ)) {
@ -3681,17 +3701,17 @@ repeat:
goto fail;
}
err = f2fs_submit_page_read(use_cow ?
F2FS_I(inode)->cow_inode : inode, page,
blkaddr, 0, true);
F2FS_I(inode)->cow_inode : inode,
folio, blkaddr, 0, true);
if (err)
goto fail;
lock_page(page);
if (unlikely(page->mapping != mapping)) {
folio_lock(folio);
if (unlikely(folio->mapping != mapping)) {
f2fs_put_page(page, 1);
goto repeat;
}
if (unlikely(!PageUptodate(page))) {
if (unlikely(!folio_test_uptodate(folio))) {
err = -EIO;
goto fail;
}
@ -3709,7 +3729,8 @@ static int f2fs_write_end(struct file *file,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
struct inode *inode = page->mapping->host;
struct folio *folio = page_folio(page);
struct inode *inode = folio->mapping->host;
trace_f2fs_write_end(inode, pos, len, copied);
@ -3718,17 +3739,17 @@ static int f2fs_write_end(struct file *file,
* should be PAGE_SIZE. Otherwise, we treat it with zero copied and
* let generic_perform_write() try to copy data again through copied=0.
*/
if (!PageUptodate(page)) {
if (!folio_test_uptodate(folio)) {
if (unlikely(copied != len))
copied = 0;
else
SetPageUptodate(page);
folio_mark_uptodate(folio);
}
#ifdef CONFIG_F2FS_FS_COMPRESSION
/* overwrite compressed file */
if (f2fs_compressed_file(inode) && fsdata) {
f2fs_compress_write_end(inode, fsdata, page->index, copied);
f2fs_compress_write_end(inode, fsdata, folio->index, copied);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
if (pos + copied > i_size_read(inode) &&
@ -3741,7 +3762,10 @@ static int f2fs_write_end(struct file *file,
if (!copied)
goto unlock_out;
set_page_dirty(page);
folio_mark_dirty(folio);
if (f2fs_is_atomic_file(inode))
set_page_private_atomic(folio_page(folio, 0));
if (pos + copied > i_size_read(inode) &&
!f2fs_verity_in_progress(inode)) {
@ -4132,13 +4156,13 @@ const struct address_space_operations f2fs_dblock_aops = {
.swap_deactivate = f2fs_swap_deactivate,
};
void f2fs_clear_page_cache_dirty_tag(struct page *page)
void f2fs_clear_page_cache_dirty_tag(struct folio *folio)
{
struct address_space *mapping = page_mapping(page);
struct address_space *mapping = folio->mapping;
unsigned long flags;
xa_lock_irqsave(&mapping->i_pages, flags);
__xa_clear_mark(&mapping->i_pages, page_index(page),
__xa_clear_mark(&mapping->i_pages, folio->index,
PAGECACHE_TAG_DIRTY);
xa_unlock_irqrestore(&mapping->i_pages, flags);
}

View File

@ -275,7 +275,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
/* build nm */
si->base_mem += sizeof(struct f2fs_nm_info);
si->base_mem += __bitmap_size(sbi, NAT_BITMAP);
si->base_mem += (NM_I(sbi)->nat_bits_blocks << F2FS_BLKSIZE_BITS);
si->base_mem += F2FS_BLK_TO_BYTES(NM_I(sbi)->nat_bits_blocks);
si->base_mem += NM_I(sbi)->nat_blocks *
f2fs_bitmap_size(NAT_ENTRY_PER_BLOCK);
si->base_mem += NM_I(sbi)->nat_blocks / 8;

View File

@ -157,7 +157,8 @@ static unsigned long dir_block_index(unsigned int level,
unsigned long bidx = 0;
for (i = 0; i < level; i++)
bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
bidx += mul_u32_u32(dir_buckets(i, dir_level),
bucket_blocks(i));
bidx += idx * bucket_blocks(level);
return bidx;
}
@ -884,6 +885,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
struct f2fs_dentry_block *dentry_blk;
unsigned int bit_pos;
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
pgoff_t index = page_folio(page)->index;
int i;
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
@ -909,8 +911,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
set_page_dirty(page);
if (bit_pos == NR_DENTRY_IN_BLOCK &&
!f2fs_truncate_hole(dir, page->index, page->index + 1)) {
f2fs_clear_page_cache_dirty_tag(page);
!f2fs_truncate_hole(dir, index, index + 1)) {
f2fs_clear_page_cache_dirty_tag(page_folio(page));
clear_page_dirty_for_io(page);
ClearPageUptodate(page);
clear_page_private_all(page);

View File

@ -366,7 +366,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
static void __drop_largest_extent(struct extent_tree *et,
pgoff_t fofs, unsigned int len)
{
if (fofs < et->largest.fofs + et->largest.len &&
if (fofs < (pgoff_t)et->largest.fofs + et->largest.len &&
fofs + len > et->largest.fofs) {
et->largest.len = 0;
et->largest_updated = true;
@ -456,7 +456,7 @@ static bool __lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
if (type == EX_READ &&
et->largest.fofs <= pgofs &&
et->largest.fofs + et->largest.len > pgofs) {
(pgoff_t)et->largest.fofs + et->largest.len > pgofs) {
*ei = et->largest;
ret = true;
stat_inc_largest_node_hit(sbi);

View File

@ -11,7 +11,6 @@
#include <linux/uio.h>
#include <linux/types.h>
#include <linux/page-flags.h>
#include <linux/buffer_head.h>
#include <linux/slab.h>
#include <linux/crc32.h>
#include <linux/magic.h>
@ -134,6 +133,12 @@ typedef u32 nid_t;
#define COMPRESS_EXT_NUM 16
enum blkzone_allocation_policy {
BLKZONE_ALLOC_PRIOR_SEQ, /* Prioritize writing to sequential zones */
BLKZONE_ALLOC_ONLY_SEQ, /* Only allow writing to sequential zones */
BLKZONE_ALLOC_PRIOR_CONV, /* Prioritize writing to conventional zones */
};
/*
* An implementation of an rwsem that is explicitly unfair to readers. This
* prevents priority inversion when a low-priority reader acquires the read lock
@ -285,6 +290,7 @@ enum {
APPEND_INO, /* for append ino list */
UPDATE_INO, /* for update ino list */
TRANS_DIR_INO, /* for transactions dir ino list */
XATTR_DIR_INO, /* for xattr updated dir ino list */
FLUSH_INO, /* for multiple device flushing */
MAX_INO_ENTRY, /* max. list */
};
@ -784,7 +790,6 @@ enum {
FI_NEED_IPU, /* used for ipu per file */
FI_ATOMIC_FILE, /* indicate atomic file */
FI_DATA_EXIST, /* indicate data exists */
FI_INLINE_DOTS, /* indicate inline dot dentries */
FI_SKIP_WRITES, /* should skip data page writeback */
FI_OPU_WRITE, /* used for opu per file */
FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */
@ -802,6 +807,7 @@ enum {
FI_ALIGNED_WRITE, /* enable aligned write */
FI_COW_FILE, /* indicate COW file */
FI_ATOMIC_COMMITTED, /* indicate atomic commit completed except disk sync */
FI_ATOMIC_DIRTIED, /* indicate atomic file is dirtied */
FI_ATOMIC_REPLACE, /* indicate atomic replace */
FI_OPENED_FILE, /* indicate file has been opened */
FI_MAX, /* max flag, never be used */
@ -1155,6 +1161,7 @@ enum cp_reason_type {
CP_FASTBOOT_MODE,
CP_SPEC_LOG_NUM,
CP_RECOVER_DIR,
CP_XATTR_DIR,
};
enum iostat_type {
@ -1292,6 +1299,7 @@ struct f2fs_gc_control {
bool no_bg_gc; /* check the space and stop bg_gc */
bool should_migrate_blocks; /* should migrate blocks */
bool err_gc_skipped; /* return EAGAIN if GC skipped */
bool one_time; /* require one time GC in one migration unit */
unsigned int nr_free_secs; /* # of free sections to do GC */
};
@ -1417,7 +1425,8 @@ static inline void f2fs_clear_bit(unsigned int nr, char *addr);
* bit 1 PAGE_PRIVATE_ONGOING_MIGRATION
* bit 2 PAGE_PRIVATE_INLINE_INODE
* bit 3 PAGE_PRIVATE_REF_RESOURCE
* bit 4- f2fs private data
* bit 4 PAGE_PRIVATE_ATOMIC_WRITE
* bit 5- f2fs private data
*
* Layout B: lowest bit should be 0
* page.private is a wrapped pointer.
@ -1427,6 +1436,7 @@ enum {
PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */
PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
PAGE_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
PAGE_PRIVATE_MAX
};
@ -1558,6 +1568,8 @@ struct f2fs_sb_info {
#ifdef CONFIG_BLK_DEV_ZONED
unsigned int blocks_per_blkz; /* F2FS blocks per zone */
unsigned int max_open_zones; /* max open zone resources of the zoned device */
/* For adjust the priority writing position of data in zone UFS */
unsigned int blkzone_alloc_policy;
#endif
/* for node-related operations */
@ -1684,6 +1696,8 @@ struct f2fs_sb_info {
unsigned int max_victim_search;
/* migration granularity of garbage collection, unit: segment */
unsigned int migration_granularity;
/* migration window granularity of garbage collection, unit: segment */
unsigned int migration_window_granularity;
/*
* for stat information.
@ -1993,6 +2007,16 @@ static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
return (struct f2fs_super_block *)(sbi->raw_super);
}
static inline struct f2fs_super_block *F2FS_SUPER_BLOCK(struct folio *folio,
pgoff_t index)
{
pgoff_t idx_in_folio = index % (1 << folio_order(folio));
return (struct f2fs_super_block *)
(page_address(folio_page(folio, idx_in_folio)) +
F2FS_SUPER_OFFSET);
}
static inline struct f2fs_checkpoint *F2FS_CKPT(struct f2fs_sb_info *sbi)
{
return (struct f2fs_checkpoint *)(sbi->ckpt);
@ -2395,14 +2419,17 @@ static inline void clear_page_private_##name(struct page *page) \
PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);
PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);
PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
static inline unsigned long get_page_private_data(struct page *page)
{
@ -2434,6 +2461,7 @@ static inline void clear_page_private_all(struct page *page)
clear_page_private_reference(page);
clear_page_private_gcing(page);
clear_page_private_inline(page);
clear_page_private_atomic(page);
f2fs_bug_on(F2FS_P_SB(page), page_private(page));
}
@ -2853,13 +2881,26 @@ static inline bool is_inflight_io(struct f2fs_sb_info *sbi, int type)
return false;
}
static inline bool is_inflight_read_io(struct f2fs_sb_info *sbi)
{
return get_pages(sbi, F2FS_RD_DATA) || get_pages(sbi, F2FS_DIO_READ);
}
static inline bool is_idle(struct f2fs_sb_info *sbi, int type)
{
bool zoned_gc = (type == GC_TIME &&
F2FS_HAS_FEATURE(sbi, F2FS_FEATURE_BLKZONED));
if (sbi->gc_mode == GC_URGENT_HIGH)
return true;
if (is_inflight_io(sbi, type))
return false;
if (zoned_gc) {
if (is_inflight_read_io(sbi))
return false;
} else {
if (is_inflight_io(sbi, type))
return false;
}
if (sbi->gc_mode == GC_URGENT_MID)
return true;
@ -2868,6 +2909,9 @@ static inline bool is_idle(struct f2fs_sb_info *sbi, int type)
(type == DISCARD_TIME || type == GC_TIME))
return true;
if (zoned_gc)
return true;
return f2fs_time_over(sbi, type);
}
@ -2899,26 +2943,27 @@ static inline __le32 *blkaddr_in_node(struct f2fs_node *node)
}
static inline int f2fs_has_extra_attr(struct inode *inode);
static inline unsigned int get_dnode_base(struct inode *inode,
struct page *node_page)
{
if (!IS_INODE(node_page))
return 0;
return inode ? get_extra_isize(inode) :
offset_in_addr(&F2FS_NODE(node_page)->i);
}
static inline __le32 *get_dnode_addr(struct inode *inode,
struct page *node_page)
{
return blkaddr_in_node(F2FS_NODE(node_page)) +
get_dnode_base(inode, node_page);
}
static inline block_t data_blkaddr(struct inode *inode,
struct page *node_page, unsigned int offset)
{
struct f2fs_node *raw_node;
__le32 *addr_array;
int base = 0;
bool is_inode = IS_INODE(node_page);
raw_node = F2FS_NODE(node_page);
if (is_inode) {
if (!inode)
/* from GC path only */
base = offset_in_addr(&raw_node->i);
else if (f2fs_has_extra_attr(inode))
base = get_extra_isize(inode);
}
addr_array = blkaddr_in_node(raw_node);
return le32_to_cpu(addr_array[base + offset]);
return le32_to_cpu(*(get_dnode_addr(inode, node_page) + offset));
}
static inline block_t f2fs_data_blkaddr(struct dnode_of_data *dn)
@ -3037,10 +3082,8 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
return;
fallthrough;
case FI_DATA_EXIST:
case FI_INLINE_DOTS:
case FI_PIN_FILE:
case FI_COMPRESS_RELEASED:
case FI_ATOMIC_COMMITTED:
f2fs_mark_inode_dirty_sync(inode, true);
}
}
@ -3162,8 +3205,6 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
set_bit(FI_INLINE_DENTRY, fi->flags);
if (ri->i_inline & F2FS_DATA_EXIST)
set_bit(FI_DATA_EXIST, fi->flags);
if (ri->i_inline & F2FS_INLINE_DOTS)
set_bit(FI_INLINE_DOTS, fi->flags);
if (ri->i_inline & F2FS_EXTRA_ATTR)
set_bit(FI_EXTRA_ATTR, fi->flags);
if (ri->i_inline & F2FS_PIN_FILE)
@ -3184,8 +3225,6 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
ri->i_inline |= F2FS_INLINE_DENTRY;
if (is_inode_flag_set(inode, FI_DATA_EXIST))
ri->i_inline |= F2FS_DATA_EXIST;
if (is_inode_flag_set(inode, FI_INLINE_DOTS))
ri->i_inline |= F2FS_INLINE_DOTS;
if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
ri->i_inline |= F2FS_EXTRA_ATTR;
if (is_inode_flag_set(inode, FI_PIN_FILE))
@ -3266,11 +3305,6 @@ static inline int f2fs_exist_data(struct inode *inode)
return is_inode_flag_set(inode, FI_DATA_EXIST);
}
static inline int f2fs_has_inline_dots(struct inode *inode)
{
return is_inode_flag_set(inode, FI_INLINE_DOTS);
}
static inline int f2fs_is_mmap_file(struct inode *inode)
{
return is_inode_flag_set(inode, FI_MMAP_FILE);
@ -3291,8 +3325,6 @@ static inline bool f2fs_is_cow_file(struct inode *inode)
return is_inode_flag_set(inode, FI_COW_FILE);
}
static inline __le32 *get_dnode_addr(struct inode *inode,
struct page *node_page);
static inline void *inline_data_addr(struct inode *inode, struct page *page)
{
__le32 *addr = get_dnode_addr(inode, page);
@ -3440,17 +3472,6 @@ static inline int get_inline_xattr_addrs(struct inode *inode)
return F2FS_I(inode)->i_inline_xattr_size;
}
static inline __le32 *get_dnode_addr(struct inode *inode,
struct page *node_page)
{
int base = 0;
if (IS_INODE(node_page) && f2fs_has_extra_attr(inode))
base = get_extra_isize(inode);
return blkaddr_in_node(F2FS_NODE(node_page)) + base;
}
#define f2fs_get_inode_mode(i) \
((is_inode_flag_set(i, FI_ACL_MODE)) ? \
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
@ -3503,7 +3524,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
int f2fs_truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count);
int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
bool readonly);
bool readonly, bool need_lock);
int f2fs_precache_extents(struct inode *inode);
int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
int f2fs_fileattr_set(struct mnt_idmap *idmap,
@ -3713,7 +3734,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi,
struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno);
void f2fs_update_meta_page(struct f2fs_sb_info *sbi, void *src,
block_t blk_addr);
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct folio *folio,
enum iostat_type io_type);
void f2fs_do_write_node_page(unsigned int nid, struct f2fs_io_info *fio);
void f2fs_outplace_write_data(struct dnode_of_data *dn,
@ -3753,8 +3774,7 @@ void f2fs_destroy_segment_manager_caches(void);
int f2fs_rw_hint_to_seg_type(struct f2fs_sb_info *sbi, enum rw_hint hint);
enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi,
enum page_type type, enum temp_type temp);
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi,
unsigned int segno);
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi);
unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi,
unsigned int segno);
@ -3862,7 +3882,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
int f2fs_encrypt_one_page(struct f2fs_io_info *fio);
bool f2fs_should_update_inplace(struct inode *inode, struct f2fs_io_info *fio);
bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio);
int f2fs_write_single_data_page(struct page *page, int *submitted,
int f2fs_write_single_data_page(struct folio *folio, int *submitted,
struct bio **bio, sector_t *last_block,
struct writeback_control *wbc,
enum iostat_type io_type,
@ -3871,7 +3891,7 @@ void f2fs_write_failed(struct inode *inode, loff_t to);
void f2fs_invalidate_folio(struct folio *folio, size_t offset, size_t length);
bool f2fs_release_folio(struct folio *folio, gfp_t wait);
bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len);
void f2fs_clear_page_cache_dirty_tag(struct page *page);
void f2fs_clear_page_cache_dirty_tag(struct folio *folio);
int f2fs_init_post_read_processing(void);
void f2fs_destroy_post_read_processing(void);
int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi);
@ -3895,7 +3915,7 @@ void f2fs_destroy_garbage_collection_cache(void);
/* victim selection function for cleaning and SSR */
int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
int gc_type, int type, char alloc_mode,
unsigned long long age);
unsigned long long age, bool one_time);
/*
* recovery.c
@ -3981,7 +4001,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
#define stat_inc_cp_call_count(sbi, foreground) \
atomic_inc(&sbi->cp_call_count[(foreground)])
#define stat_inc_cp_count(si) (F2FS_STAT(sbi)->cp_count++)
#define stat_inc_cp_count(sbi) (F2FS_STAT(sbi)->cp_count++)
#define stat_io_skip_bggc_count(sbi) ((sbi)->io_skip_bggc++)
#define stat_other_skip_bggc_count(sbi) ((sbi)->other_skip_bggc++)
#define stat_inc_dirty_inode(sbi, type) ((sbi)->ndirty_inode[type]++)
@ -4166,7 +4186,7 @@ int f2fs_read_inline_data(struct inode *inode, struct folio *folio);
int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page);
int f2fs_convert_inline_inode(struct inode *inode);
int f2fs_try_convert_inline_dir(struct inode *dir, struct dentry *dentry);
int f2fs_write_inline_data(struct inode *inode, struct page *page);
int f2fs_write_inline_data(struct inode *inode, struct folio *folio);
int f2fs_recover_inline_data(struct inode *inode, struct page *npage);
struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
const struct f2fs_filename *fname,
@ -4283,6 +4303,11 @@ static inline bool f2fs_meta_inode_gc_required(struct inode *inode)
* compress.c
*/
#ifdef CONFIG_F2FS_FS_COMPRESSION
enum cluster_check_type {
CLUSTER_IS_COMPR, /* check only if compressed cluster */
CLUSTER_COMPR_BLKS, /* return # of compressed blocks in a cluster */
CLUSTER_RAW_BLKS /* return # of raw blocks in a cluster */
};
bool f2fs_is_compressed_page(struct page *page);
struct page *f2fs_compress_control_page(struct page *page);
int f2fs_prepare_compress_overwrite(struct inode *inode,
@ -4303,12 +4328,13 @@ bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index);
bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
int index, int nr_pages, bool uptodate);
bool f2fs_sanity_check_cluster(struct dnode_of_data *dn);
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page);
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct folio *folio);
int f2fs_write_multi_pages(struct compress_ctx *cc,
int *submitted,
struct writeback_control *wbc,
enum iostat_type io_type);
int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index);
bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index);
void f2fs_update_read_extent_tree_range_compressed(struct inode *inode,
pgoff_t fofs, block_t blkaddr,
unsigned int llen, unsigned int c_len);
@ -4395,6 +4421,12 @@ static inline bool f2fs_load_compressed_page(struct f2fs_sb_info *sbi,
static inline void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi,
nid_t ino) { }
#define inc_compr_inode_stat(inode) do { } while (0)
static inline int f2fs_is_compressed_cluster(
struct inode *inode,
pgoff_t index) { return 0; }
static inline bool f2fs_is_sparse_cluster(
struct inode *inode,
pgoff_t index) { return true; }
static inline void f2fs_update_read_extent_tree_range_compressed(
struct inode *inode,
pgoff_t fofs, block_t blkaddr,
@ -4647,9 +4679,11 @@ static inline void f2fs_io_schedule_timeout(long timeout)
io_schedule_timeout(timeout);
}
static inline void f2fs_handle_page_eio(struct f2fs_sb_info *sbi, pgoff_t ofs,
enum page_type type)
static inline void f2fs_handle_page_eio(struct f2fs_sb_info *sbi,
struct folio *folio, enum page_type type)
{
pgoff_t ofs = folio->index;
if (unlikely(f2fs_cp_error(sbi)))
return;

View File

@ -8,7 +8,6 @@
#include <linux/fs.h>
#include <linux/f2fs_fs.h>
#include <linux/stat.h>
#include <linux/buffer_head.h>
#include <linux/writeback.h>
#include <linux/blkdev.h>
#include <linux/falloc.h>
@ -57,7 +56,7 @@ vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
{
struct page *page = vmf->page;
struct folio *folio = page_folio(vmf->page);
struct inode *inode = file_inode(vmf->vma->vm_file);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
@ -89,7 +88,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (f2fs_compressed_file(inode)) {
int ret = f2fs_is_compressed_cluster(inode, page->index);
int ret = f2fs_is_compressed_cluster(inode, folio->index);
if (ret < 0) {
err = ret;
@ -109,11 +108,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
file_update_time(vmf->vma->vm_file);
filemap_invalidate_lock_shared(inode->i_mapping);
lock_page(page);
if (unlikely(page->mapping != inode->i_mapping ||
page_offset(page) > i_size_read(inode) ||
!PageUptodate(page))) {
unlock_page(page);
folio_lock(folio);
if (unlikely(folio->mapping != inode->i_mapping ||
folio_pos(folio) > i_size_read(inode) ||
!folio_test_uptodate(folio))) {
folio_unlock(folio);
err = -EFAULT;
goto out_sem;
}
@ -121,9 +120,9 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
set_new_dnode(&dn, inode, NULL, NULL, 0);
if (need_alloc) {
/* block allocation */
err = f2fs_get_block_locked(&dn, page->index);
err = f2fs_get_block_locked(&dn, folio->index);
} else {
err = f2fs_get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
err = f2fs_get_dnode_of_data(&dn, folio->index, LOOKUP_NODE);
f2fs_put_dnode(&dn);
if (f2fs_is_pinned_file(inode) &&
!__is_valid_data_blkaddr(dn.data_blkaddr))
@ -131,11 +130,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
}
if (err) {
unlock_page(page);
folio_unlock(folio);
goto out_sem;
}
f2fs_wait_on_page_writeback(page, DATA, false, true);
f2fs_wait_on_page_writeback(folio_page(folio, 0), DATA, false, true);
/* wait for GCed page writeback via META_MAPPING */
f2fs_wait_on_block_writeback(inode, dn.data_blkaddr);
@ -143,18 +142,18 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
/*
* check to see if the page is mapped already (no holes)
*/
if (PageMappedToDisk(page))
if (folio_test_mappedtodisk(folio))
goto out_sem;
/* page is wholly or partially inside EOF */
if (((loff_t)(page->index + 1) << PAGE_SHIFT) >
if (((loff_t)(folio->index + 1) << PAGE_SHIFT) >
i_size_read(inode)) {
loff_t offset;
offset = i_size_read(inode) & ~PAGE_MASK;
zero_user_segment(page, offset, PAGE_SIZE);
folio_zero_segment(folio, offset, folio_size(folio));
}
set_page_dirty(page);
folio_mark_dirty(folio);
f2fs_update_iostat(sbi, inode, APP_MAPPED_IO, F2FS_BLKSIZE);
f2fs_update_time(sbi, REQ_TIME);
@ -166,7 +165,7 @@ out_sem:
out:
ret = vmf_fs_error(err);
trace_f2fs_vm_page_mkwrite(inode, page->index, vmf->vma->vm_flags, ret);
trace_f2fs_vm_page_mkwrite(inode, folio->index, vmf->vma->vm_flags, ret);
return ret;
}
@ -221,6 +220,9 @@ static inline enum cp_reason_type need_do_checkpoint(struct inode *inode)
f2fs_exist_written_data(sbi, F2FS_I(inode)->i_pino,
TRANS_DIR_INO))
cp_reason = CP_RECOVER_DIR;
else if (f2fs_exist_written_data(sbi, F2FS_I(inode)->i_pino,
XATTR_DIR_INO))
cp_reason = CP_XATTR_DIR;
return cp_reason;
}
@ -376,8 +378,7 @@ sync_nodes:
f2fs_remove_ino_entry(sbi, ino, APPEND_INO);
clear_inode_flag(inode, FI_APPEND_WRITE);
flush_out:
if ((!atomic && F2FS_OPTION(sbi).fsync_mode != FSYNC_MODE_NOBARRIER) ||
(atomic && !test_opt(sbi, NOBARRIER) && f2fs_sb_has_blkzoned(sbi)))
if (!atomic && F2FS_OPTION(sbi).fsync_mode != FSYNC_MODE_NOBARRIER)
ret = f2fs_issue_flush(sbi, inode->i_ino);
if (!ret) {
f2fs_remove_ino_entry(sbi, ino, UPDATE_INO);
@ -434,7 +435,7 @@ static bool __found_offset(struct address_space *mapping,
static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
loff_t maxbytes = inode->i_sb->s_maxbytes;
loff_t maxbytes = F2FS_BLK_TO_BYTES(max_file_blocks(inode));
struct dnode_of_data dn;
pgoff_t pgofs, end_offset;
loff_t data_ofs = offset;
@ -516,10 +517,7 @@ fail:
static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
loff_t maxbytes = inode->i_sb->s_maxbytes;
if (f2fs_compressed_file(inode))
maxbytes = max_file_blocks(inode) << F2FS_BLKSIZE_BITS;
loff_t maxbytes = F2FS_BLK_TO_BYTES(max_file_blocks(inode));
switch (whence) {
case SEEK_SET:
@ -1059,6 +1057,13 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
return err;
}
/*
* wait for inflight dio, blocks should be removed after
* IO completion.
*/
if (attr->ia_size < old_size)
inode_dio_wait(inode);
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(inode->i_mapping);
@ -1895,6 +1900,12 @@ static long f2fs_fallocate(struct file *file, int mode,
if (ret)
goto out;
/*
* wait for inflight dio, blocks should be removed after IO
* completion.
*/
inode_dio_wait(inode);
if (mode & FALLOC_FL_PUNCH_HOLE) {
if (offset >= inode->i_size)
goto out;
@ -2123,10 +2134,12 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
struct mnt_idmap *idmap = file_mnt_idmap(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct inode *pinode;
loff_t isize;
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(idmap, inode))
return -EACCES;
@ -2156,6 +2169,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
goto out;
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
f2fs_down_write(&fi->i_gc_rwsem[READ]);
/*
* Should wait end_io to count F2FS_WB_CP_DATA correctly by
@ -2165,27 +2179,18 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
f2fs_warn(sbi, "Unexpected flush for atomic writes: ino=%lu, npages=%u",
inode->i_ino, get_dirty_pages(inode));
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret) {
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
goto out;
}
if (ret)
goto out_unlock;
/* Check if the inode already has a COW inode */
if (fi->cow_inode == NULL) {
/* Create a COW inode for atomic write */
pinode = f2fs_iget(inode->i_sb, fi->i_pino);
if (IS_ERR(pinode)) {
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
ret = PTR_ERR(pinode);
goto out;
}
struct dentry *dentry = file_dentry(filp);
struct inode *dir = d_inode(dentry->d_parent);
ret = f2fs_get_tmpfile(idmap, pinode, &fi->cow_inode);
iput(pinode);
if (ret) {
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
goto out;
}
ret = f2fs_get_tmpfile(idmap, dir, &fi->cow_inode);
if (ret)
goto out_unlock;
set_inode_flag(fi->cow_inode, FI_COW_FILE);
clear_inode_flag(fi->cow_inode, FI_INLINE_DATA);
@ -2194,11 +2199,13 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
F2FS_I(fi->cow_inode)->atomic_inode = inode;
} else {
/* Reuse the already created COW inode */
f2fs_bug_on(sbi, get_dirty_pages(fi->cow_inode));
invalidate_mapping_pages(fi->cow_inode->i_mapping, 0, -1);
ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true);
if (ret) {
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
goto out;
}
if (ret)
goto out_unlock;
}
f2fs_write_inode(inode, NULL);
@ -2217,7 +2224,11 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
}
f2fs_i_size_write(fi->cow_inode, isize);
out_unlock:
f2fs_up_write(&fi->i_gc_rwsem[READ]);
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
if (ret)
goto out;
f2fs_update_time(sbi, REQ_TIME);
fi->atomic_write_task = current;
@ -2235,6 +2246,9 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
struct mnt_idmap *idmap = file_mnt_idmap(filp);
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(idmap, inode))
return -EACCES;
@ -2267,6 +2281,9 @@ static int f2fs_ioc_abort_atomic_write(struct file *filp)
struct mnt_idmap *idmap = file_mnt_idmap(filp);
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(idmap, inode))
return -EACCES;
@ -2286,7 +2303,7 @@ static int f2fs_ioc_abort_atomic_write(struct file *filp)
}
int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
bool readonly)
bool readonly, bool need_lock)
{
struct super_block *sb = sbi->sb;
int ret = 0;
@ -2333,12 +2350,19 @@ int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
if (readonly)
goto out;
/* grab sb->s_umount to avoid racing w/ remount() */
if (need_lock)
down_read(&sbi->sb->s_umount);
f2fs_stop_gc_thread(sbi);
f2fs_stop_discard_thread(sbi);
f2fs_drop_discard_cmd(sbi);
clear_opt(sbi, DISCARD);
if (need_lock)
up_read(&sbi->sb->s_umount);
f2fs_update_time(sbi, REQ_TIME);
out:
@ -2375,7 +2399,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
}
}
ret = f2fs_do_shutdown(sbi, in, readonly);
ret = f2fs_do_shutdown(sbi, in, readonly, true);
if (need_drop)
mnt_drop_write_file(filp);
@ -2693,7 +2717,8 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
(range->start + range->len) >> PAGE_SHIFT,
DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE));
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) ||
f2fs_is_atomic_file(inode)) {
err = -EINVAL;
goto unlock_out;
}
@ -2717,7 +2742,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
* block addresses are continuous.
*/
if (f2fs_lookup_read_extent_cache(inode, pg_start, &ei)) {
if (ei.fofs + ei.len >= pg_end)
if ((pgoff_t)ei.fofs + ei.len >= pg_end)
goto out;
}
@ -2800,6 +2825,8 @@ do_map:
goto clear_out;
}
f2fs_wait_on_page_writeback(page, DATA, true, true);
set_page_dirty(page);
set_page_private_gcing(page);
f2fs_put_page(page, 1);
@ -2924,6 +2951,11 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
goto out_unlock;
}
if (f2fs_is_atomic_file(src) || f2fs_is_atomic_file(dst)) {
ret = -EINVAL;
goto out_unlock;
}
ret = -EINVAL;
if (pos_in + len > src->i_size || pos_in + len < pos_in)
goto out_unlock;
@ -2975,9 +3007,9 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
}
f2fs_lock_op(sbi);
ret = __exchange_data_block(src, dst, pos_in >> F2FS_BLKSIZE_BITS,
pos_out >> F2FS_BLKSIZE_BITS,
len >> F2FS_BLKSIZE_BITS, false);
ret = __exchange_data_block(src, dst, F2FS_BYTES_TO_BLK(pos_in),
F2FS_BYTES_TO_BLK(pos_out),
F2FS_BYTES_TO_BLK(len), false);
if (!ret) {
if (dst_max_i_size)
@ -3307,6 +3339,11 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
inode_lock(inode);
if (f2fs_is_atomic_file(inode)) {
ret = -EINVAL;
goto out;
}
if (!pin) {
clear_inode_flag(inode, FI_PIN_FILE);
f2fs_i_gc_failures_write(inode, 0);
@ -4200,6 +4237,8 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
/* It will never fail, when page has pinned above */
f2fs_bug_on(F2FS_I_SB(inode), !page);
f2fs_wait_on_page_writeback(page, DATA, true, true);
set_page_dirty(page);
set_page_private_gcing(page);
f2fs_put_page(page, 1);
@ -4214,9 +4253,8 @@ static int f2fs_ioc_decompress_file(struct file *filp)
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
pgoff_t page_idx = 0, last_idx;
int cluster_size = fi->i_cluster_size;
int count, ret;
pgoff_t page_idx = 0, last_idx, cluster_idx;
int ret;
if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
@ -4251,10 +4289,15 @@ static int f2fs_ioc_decompress_file(struct file *filp)
goto out;
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
last_idx >>= fi->i_log_cluster_size;
count = last_idx - page_idx;
while (count && count >= cluster_size) {
ret = redirty_blocks(inode, page_idx, cluster_size);
for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
page_idx = cluster_idx << fi->i_log_cluster_size;
if (!f2fs_is_compressed_cluster(inode, page_idx))
continue;
ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
if (ret < 0)
break;
@ -4264,9 +4307,6 @@ static int f2fs_ioc_decompress_file(struct file *filp)
break;
}
count -= cluster_size;
page_idx += cluster_size;
cond_resched();
if (fatal_signal_pending(current)) {
ret = -EINTR;
@ -4293,9 +4333,9 @@ static int f2fs_ioc_compress_file(struct file *filp)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t page_idx = 0, last_idx;
int cluster_size = F2FS_I(inode)->i_cluster_size;
int count, ret;
struct f2fs_inode_info *fi = F2FS_I(inode);
pgoff_t page_idx = 0, last_idx, cluster_idx;
int ret;
if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
@ -4329,10 +4369,15 @@ static int f2fs_ioc_compress_file(struct file *filp)
set_inode_flag(inode, FI_ENABLE_COMPRESS);
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
last_idx >>= fi->i_log_cluster_size;
count = last_idx - page_idx;
while (count && count >= cluster_size) {
ret = redirty_blocks(inode, page_idx, cluster_size);
for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
page_idx = cluster_idx << fi->i_log_cluster_size;
if (f2fs_is_sparse_cluster(inode, page_idx))
continue;
ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
if (ret < 0)
break;
@ -4342,9 +4387,6 @@ static int f2fs_ioc_compress_file(struct file *filp)
break;
}
count -= cluster_size;
page_idx += cluster_size;
cond_resched();
if (fatal_signal_pending(current)) {
ret = -EINTR;
@ -4545,6 +4587,13 @@ static ssize_t f2fs_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)
f2fs_down_read(&fi->i_gc_rwsem[READ]);
}
/* dio is not compatible w/ atomic file */
if (f2fs_is_atomic_file(inode)) {
f2fs_up_read(&fi->i_gc_rwsem[READ]);
ret = -EOPNOTSUPP;
goto out;
}
/*
* We have to use __iomap_dio_rw() and iomap_dio_complete() instead of
* the higher-level function iomap_dio_rw() in order to ensure that the
@ -4604,6 +4653,10 @@ static ssize_t f2fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
f2fs_trace_rw_file_path(iocb->ki_filp, iocb->ki_pos,
iov_iter_count(to), READ);
/* In LFS mode, if there is inflight dio, wait for its completion */
if (f2fs_lfs_mode(F2FS_I_SB(inode)))
inode_dio_wait(inode);
if (f2fs_should_use_dio(inode, iocb, to)) {
ret = f2fs_dio_read_iter(iocb, to);
} else {
@ -4956,6 +5009,12 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
/* Determine whether we will do a direct write or a buffered write. */
dio = f2fs_should_use_dio(inode, iocb, from);
/* dio is not compatible w/ atomic write */
if (dio && f2fs_is_atomic_file(inode)) {
ret = -EOPNOTSUPP;
goto out_unlock;
}
/* Possibly preallocate the blocks for the write. */
target_size = iocb->ki_pos + iov_iter_count(from);
preallocated = f2fs_preallocate_blocks(iocb, from, dio);

View File

@ -81,6 +81,8 @@ static int gc_thread_func(void *data)
continue;
}
gc_control.one_time = false;
/*
* [GC triggering condition]
* 0. GC is not conducted currently.
@ -116,15 +118,30 @@ static int gc_thread_func(void *data)
goto next;
}
if (has_enough_invalid_blocks(sbi))
if (f2fs_sb_has_blkzoned(sbi)) {
if (has_enough_free_blocks(sbi,
gc_th->no_zoned_gc_percent)) {
wait_ms = gc_th->no_gc_sleep_time;
f2fs_up_write(&sbi->gc_lock);
goto next;
}
if (wait_ms == gc_th->no_gc_sleep_time)
wait_ms = gc_th->max_sleep_time;
}
if (need_to_boost_gc(sbi)) {
decrease_sleep_time(gc_th, &wait_ms);
else
if (f2fs_sb_has_blkzoned(sbi))
gc_control.one_time = true;
} else {
increase_sleep_time(gc_th, &wait_ms);
}
do_gc:
stat_inc_gc_call_count(sbi, foreground ?
FOREGROUND : BACKGROUND);
sync_mode = F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_SYNC;
sync_mode = (F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_SYNC) ||
gc_control.one_time;
/* foreground GC was been triggered via f2fs_balance_fs() */
if (foreground)
@ -179,9 +196,21 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
return -ENOMEM;
gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME;
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME;
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
gc_th->valid_thresh_ratio = DEF_GC_THREAD_VALID_THRESH_RATIO;
if (f2fs_sb_has_blkzoned(sbi)) {
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME_ZONED;
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME_ZONED;
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME_ZONED;
gc_th->no_zoned_gc_percent = LIMIT_NO_ZONED_GC;
gc_th->boost_zoned_gc_percent = LIMIT_BOOST_ZONED_GC;
} else {
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME;
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
gc_th->no_zoned_gc_percent = 0;
gc_th->boost_zoned_gc_percent = 0;
}
gc_th->gc_wake = false;
@ -339,7 +368,7 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno)
unsigned char age = 0;
unsigned char u;
unsigned int i;
unsigned int usable_segs_per_sec = f2fs_usable_segs_in_sec(sbi, segno);
unsigned int usable_segs_per_sec = f2fs_usable_segs_in_sec(sbi);
for (i = 0; i < usable_segs_per_sec; i++)
mtime += get_seg_entry(sbi, start + i)->mtime;
@ -368,6 +397,11 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
if (p->alloc_mode == SSR)
return get_seg_entry(sbi, segno)->ckpt_valid_blocks;
if (p->one_time_gc && (get_valid_blocks(sbi, segno, true) >=
CAP_BLKS_PER_SEC(sbi) * sbi->gc_thread->valid_thresh_ratio /
100))
return UINT_MAX;
/* alloc_mode == LFS */
if (p->gc_mode == GC_GREEDY)
return get_valid_blocks(sbi, segno, true);
@ -742,7 +776,7 @@ static int f2fs_gc_pinned_control(struct inode *inode, int gc_type,
*/
int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
int gc_type, int type, char alloc_mode,
unsigned long long age)
unsigned long long age, bool one_time)
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
struct sit_info *sm = SIT_I(sbi);
@ -759,6 +793,7 @@ int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
p.alloc_mode = alloc_mode;
p.age = age;
p.age_threshold = sbi->am.age_threshold;
p.one_time_gc = one_time;
retry:
select_policy(sbi, gc_type, type, &p);
@ -1670,13 +1705,14 @@ next_step:
}
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
int gc_type)
int gc_type, bool one_time)
{
struct sit_info *sit_i = SIT_I(sbi);
int ret;
down_write(&sit_i->sentry_lock);
ret = f2fs_get_victim(sbi, victim, gc_type, NO_CHECK_TYPE, LFS, 0);
ret = f2fs_get_victim(sbi, victim, gc_type, NO_CHECK_TYPE,
LFS, 0, one_time);
up_write(&sit_i->sentry_lock);
return ret;
}
@ -1684,30 +1720,49 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
static int do_garbage_collect(struct f2fs_sb_info *sbi,
unsigned int start_segno,
struct gc_inode_list *gc_list, int gc_type,
bool force_migrate)
bool force_migrate, bool one_time)
{
struct page *sum_page;
struct f2fs_summary_block *sum;
struct blk_plug plug;
unsigned int segno = start_segno;
unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
unsigned int sec_end_segno;
int seg_freed = 0, migrated = 0;
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
SUM_TYPE_DATA : SUM_TYPE_NODE;
unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
int submitted = 0;
if (__is_large_section(sbi))
end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
if (__is_large_section(sbi)) {
sec_end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
/*
* zone-capacity can be less than zone-size in zoned devices,
* resulting in less than expected usable segments in the zone,
* calculate the end segno in the zone which can be garbage collected
*/
if (f2fs_sb_has_blkzoned(sbi))
end_segno -= SEGS_PER_SEC(sbi) -
f2fs_usable_segs_in_sec(sbi, segno);
/*
* zone-capacity can be less than zone-size in zoned devices,
* resulting in less than expected usable segments in the zone,
* calculate the end segno in the zone which can be garbage
* collected
*/
if (f2fs_sb_has_blkzoned(sbi))
sec_end_segno -= SEGS_PER_SEC(sbi) -
f2fs_usable_segs_in_sec(sbi);
if (gc_type == BG_GC || one_time) {
unsigned int window_granularity =
sbi->migration_window_granularity;
if (f2fs_sb_has_blkzoned(sbi) &&
!has_enough_free_blocks(sbi,
sbi->gc_thread->boost_zoned_gc_percent))
window_granularity *=
BOOST_GC_MULTIPLE;
end_segno = start_segno + window_granularity;
}
if (end_segno > sec_end_segno)
end_segno = sec_end_segno;
}
sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
@ -1786,7 +1841,8 @@ freed:
if (__is_large_section(sbi))
sbi->next_victim_seg[gc_type] =
(segno + 1 < end_segno) ? segno + 1 : NULL_SEGNO;
(segno + 1 < sec_end_segno) ?
segno + 1 : NULL_SEGNO;
skip:
f2fs_put_page(sum_page, 0);
}
@ -1863,7 +1919,7 @@ gc_more:
goto stop;
}
retry:
ret = __get_victim(sbi, &segno, gc_type);
ret = __get_victim(sbi, &segno, gc_type, gc_control->one_time);
if (ret) {
/* allow to search victim from sections has pinned data */
if (ret == -ENODATA && gc_type == FG_GC &&
@ -1875,17 +1931,21 @@ retry:
}
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type,
gc_control->should_migrate_blocks);
gc_control->should_migrate_blocks,
gc_control->one_time);
if (seg_freed < 0)
goto stop;
total_freed += seg_freed;
if (seg_freed == f2fs_usable_segs_in_sec(sbi, segno)) {
if (seg_freed == f2fs_usable_segs_in_sec(sbi)) {
sec_freed++;
total_sec_freed++;
}
if (gc_control->one_time)
goto stop;
if (gc_type == FG_GC) {
sbi->cur_victim_sec = NULL_SEGNO;
@ -2010,8 +2070,7 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
.iroot = RADIX_TREE_INIT(gc_list.iroot, GFP_NOFS),
};
do_garbage_collect(sbi, segno, &gc_list, FG_GC,
dry_run_sections == 0);
do_garbage_collect(sbi, segno, &gc_list, FG_GC, true, false);
put_gc_inode(&gc_list);
if (!dry_run && get_valid_blocks(sbi, segno, true))

View File

@ -15,16 +15,27 @@
#define DEF_GC_THREAD_MAX_SLEEP_TIME 60000
#define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */
/* GC sleep parameters for zoned deivces */
#define DEF_GC_THREAD_MIN_SLEEP_TIME_ZONED 10
#define DEF_GC_THREAD_MAX_SLEEP_TIME_ZONED 20
#define DEF_GC_THREAD_NOGC_SLEEP_TIME_ZONED 60000
/* choose candidates from sections which has age of more than 7 days */
#define DEF_GC_THREAD_AGE_THRESHOLD (60 * 60 * 24 * 7)
#define DEF_GC_THREAD_CANDIDATE_RATIO 20 /* select 20% oldest sections as candidates */
#define DEF_GC_THREAD_MAX_CANDIDATE_COUNT 10 /* select at most 10 sections as candidates */
#define DEF_GC_THREAD_AGE_WEIGHT 60 /* age weight */
#define DEF_GC_THREAD_VALID_THRESH_RATIO 95 /* do not GC over 95% valid block ratio for one time GC */
#define DEFAULT_ACCURACY_CLASS 10000 /* accuracy class */
#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */
#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */
#define LIMIT_NO_ZONED_GC 60 /* percentage over total user space of no gc for zoned devices */
#define LIMIT_BOOST_ZONED_GC 25 /* percentage over total user space of boosted gc for zoned devices */
#define DEF_MIGRATION_WINDOW_GRANULARITY_ZONED 3
#define BOOST_GC_MULTIPLE 5
#define DEF_GC_FAILED_PINNED_FILES 2048
#define MAX_GC_FAILED_PINNED_FILES USHRT_MAX
@ -51,6 +62,11 @@ struct f2fs_gc_kthread {
* caller of f2fs_balance_fs()
* will wait on this wait queue.
*/
/* for gc control for zoned devices */
unsigned int no_zoned_gc_percent;
unsigned int boost_zoned_gc_percent;
unsigned int valid_thresh_ratio;
};
struct gc_inode_list {
@ -152,6 +168,12 @@ static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th,
*wait -= min_time;
}
static inline bool has_enough_free_blocks(struct f2fs_sb_info *sbi,
unsigned int limit_perc)
{
return free_sections(sbi) > ((sbi->total_sections * limit_perc) / 100);
}
static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
{
block_t user_block_count = sbi->user_block_count;
@ -167,3 +189,10 @@ static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
free_user_blocks(sbi) <
limit_free_user_blocks(invalid_user_blocks));
}
static inline bool need_to_boost_gc(struct f2fs_sb_info *sbi)
{
if (f2fs_sb_has_blkzoned(sbi))
return !has_enough_free_blocks(sbi, LIMIT_BOOST_ZONED_GC);
return has_enough_invalid_blocks(sbi);
}

View File

@ -260,35 +260,34 @@ out:
return err;
}
int f2fs_write_inline_data(struct inode *inode, struct page *page)
int f2fs_write_inline_data(struct inode *inode, struct folio *folio)
{
struct dnode_of_data dn;
int err;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct page *ipage;
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_get_dnode_of_data(&dn, 0, LOOKUP_NODE);
if (err)
return err;
ipage = f2fs_get_node_page(sbi, inode->i_ino);
if (IS_ERR(ipage))
return PTR_ERR(ipage);
if (!f2fs_has_inline_data(inode)) {
f2fs_put_dnode(&dn);
f2fs_put_page(ipage, 1);
return -EAGAIN;
}
f2fs_bug_on(F2FS_I_SB(inode), page->index);
f2fs_bug_on(F2FS_I_SB(inode), folio->index);
f2fs_wait_on_page_writeback(dn.inode_page, NODE, true, true);
memcpy_from_page(inline_data_addr(inode, dn.inode_page),
page, 0, MAX_INLINE_DATA(inode));
set_page_dirty(dn.inode_page);
f2fs_wait_on_page_writeback(ipage, NODE, true, true);
memcpy_from_folio(inline_data_addr(inode, ipage),
folio, 0, MAX_INLINE_DATA(inode));
set_page_dirty(ipage);
f2fs_clear_page_cache_dirty_tag(page);
f2fs_clear_page_cache_dirty_tag(folio);
set_inode_flag(inode, FI_APPEND_WRITE);
set_inode_flag(inode, FI_DATA_EXIST);
clear_page_private_inline(dn.inode_page);
f2fs_put_dnode(&dn);
clear_page_private_inline(ipage);
f2fs_put_page(ipage, 1);
return 0;
}

View File

@ -7,7 +7,6 @@
*/
#include <linux/fs.h>
#include <linux/f2fs_fs.h>
#include <linux/buffer_head.h>
#include <linux/writeback.h>
#include <linux/sched/mm.h>
#include <linux/lz4.h>
@ -35,6 +34,11 @@ void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync)
if (f2fs_inode_dirtied(inode, sync))
return;
if (f2fs_is_atomic_file(inode)) {
set_inode_flag(inode, FI_ATOMIC_DIRTIED);
return;
}
mark_inode_dirty_sync(inode);
}
@ -175,7 +179,8 @@ bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page)
if (provided != calculated)
f2fs_warn(sbi, "checksum invalid, nid = %lu, ino_of_node = %x, %x vs. %x",
page->index, ino_of_node(page), provided, calculated);
page_folio(page)->index, ino_of_node(page),
provided, calculated);
return provided == calculated;
}

View File

@ -457,62 +457,6 @@ struct dentry *f2fs_get_parent(struct dentry *child)
return d_obtain_alias(f2fs_iget(child->d_sb, ino));
}
static int __recover_dot_dentries(struct inode *dir, nid_t pino)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct qstr dot = QSTR_INIT(".", 1);
struct f2fs_dir_entry *de;
struct page *page;
int err = 0;
if (f2fs_readonly(sbi->sb)) {
f2fs_info(sbi, "skip recovering inline_dots inode (ino:%lu, pino:%u) in readonly mountpoint",
dir->i_ino, pino);
return 0;
}
if (!S_ISDIR(dir->i_mode)) {
f2fs_err(sbi, "inconsistent inode status, skip recovering inline_dots inode (ino:%lu, i_mode:%u, pino:%u)",
dir->i_ino, dir->i_mode, pino);
set_sbi_flag(sbi, SBI_NEED_FSCK);
return -ENOTDIR;
}
err = f2fs_dquot_initialize(dir);
if (err)
return err;
f2fs_balance_fs(sbi, true);
f2fs_lock_op(sbi);
de = f2fs_find_entry(dir, &dot, &page);
if (de) {
f2fs_put_page(page, 0);
} else if (IS_ERR(page)) {
err = PTR_ERR(page);
goto out;
} else {
err = f2fs_do_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR);
if (err)
goto out;
}
de = f2fs_find_entry(dir, &dotdot_name, &page);
if (de)
f2fs_put_page(page, 0);
else if (IS_ERR(page))
err = PTR_ERR(page);
else
err = f2fs_do_add_link(dir, &dotdot_name, NULL, pino, S_IFDIR);
out:
if (!err)
clear_inode_flag(dir, FI_INLINE_DOTS);
f2fs_unlock_op(sbi);
return err;
}
static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
@ -522,7 +466,6 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
struct dentry *new;
nid_t ino = -1;
int err = 0;
unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir));
struct f2fs_filename fname;
trace_f2fs_lookup_start(dir, dentry, flags);
@ -559,17 +502,6 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
goto out;
}
if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) {
err = __recover_dot_dentries(dir, root_ino);
if (err)
goto out_iput;
}
if (f2fs_has_inline_dots(inode)) {
err = __recover_dot_dentries(inode, dir->i_ino);
if (err)
goto out_iput;
}
if (IS_ENCRYPTED(dir) &&
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
!fscrypt_has_permitted_context(dir, inode)) {

View File

@ -20,7 +20,7 @@
#include "iostat.h"
#include <trace/events/f2fs.h>
#define on_f2fs_build_free_nids(nmi) mutex_is_locked(&(nm_i)->build_lock)
#define on_f2fs_build_free_nids(nm_i) mutex_is_locked(&(nm_i)->build_lock)
static struct kmem_cache *nat_entry_slab;
static struct kmem_cache *free_nid_slab;
@ -123,7 +123,7 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
static void clear_node_page_dirty(struct page *page)
{
if (PageDirty(page)) {
f2fs_clear_page_cache_dirty_tag(page);
f2fs_clear_page_cache_dirty_tag(page_folio(page));
clear_page_dirty_for_io(page);
dec_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES);
}
@ -919,7 +919,7 @@ static int truncate_node(struct dnode_of_data *dn)
clear_node_page_dirty(dn->node_page);
set_sbi_flag(sbi, SBI_IS_DIRTY);
index = dn->node_page->index;
index = page_folio(dn->node_page)->index;
f2fs_put_page(dn->node_page, 1);
invalidate_mapping_pages(NODE_MAPPING(sbi),
@ -1369,6 +1369,7 @@ fail:
*/
static int read_node_page(struct page *page, blk_opf_t op_flags)
{
struct folio *folio = page_folio(page);
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
struct node_info ni;
struct f2fs_io_info fio = {
@ -1381,21 +1382,21 @@ static int read_node_page(struct page *page, blk_opf_t op_flags)
};
int err;
if (PageUptodate(page)) {
if (folio_test_uptodate(folio)) {
if (!f2fs_inode_chksum_verify(sbi, page)) {
ClearPageUptodate(page);
folio_clear_uptodate(folio);
return -EFSBADCRC;
}
return LOCKED_PAGE;
}
err = f2fs_get_node_info(sbi, page->index, &ni, false);
err = f2fs_get_node_info(sbi, folio->index, &ni, false);
if (err)
return err;
/* NEW_ADDR can be seen, after cp_error drops some dirty node pages */
if (unlikely(ni.blk_addr == NULL_ADDR || ni.blk_addr == NEW_ADDR)) {
ClearPageUptodate(page);
folio_clear_uptodate(folio);
return -ENOENT;
}
@ -1492,7 +1493,7 @@ out_err:
out_put_err:
/* ENOENT comes from read_node_page which is not an error. */
if (err != -ENOENT)
f2fs_handle_page_eio(sbi, page->index, NODE);
f2fs_handle_page_eio(sbi, page_folio(page), NODE);
f2fs_put_page(page, 1);
return ERR_PTR(err);
}
@ -1535,7 +1536,7 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
if (!clear_page_dirty_for_io(page))
goto page_out;
ret = f2fs_write_inline_data(inode, page);
ret = f2fs_write_inline_data(inode, page_folio(page));
inode_dec_dirty_pages(inode);
f2fs_remove_dirty_inode(inode);
if (ret)
@ -1608,6 +1609,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
enum iostat_type io_type, unsigned int *seq_id)
{
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
struct folio *folio = page_folio(page);
nid_t nid;
struct node_info ni;
struct f2fs_io_info fio = {
@ -1624,15 +1626,15 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
};
unsigned int seq;
trace_f2fs_writepage(page_folio(page), NODE);
trace_f2fs_writepage(folio, NODE);
if (unlikely(f2fs_cp_error(sbi))) {
/* keep node pages in remount-ro mode */
if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_READONLY)
goto redirty_out;
ClearPageUptodate(page);
folio_clear_uptodate(folio);
dec_page_count(sbi, F2FS_DIRTY_NODES);
unlock_page(page);
folio_unlock(folio);
return 0;
}
@ -1646,7 +1648,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
/* get old block addr of this node page */
nid = nid_of_node(page);
f2fs_bug_on(sbi, page->index != nid);
f2fs_bug_on(sbi, folio->index != nid);
if (f2fs_get_node_info(sbi, nid, &ni, !do_balance))
goto redirty_out;
@ -1660,10 +1662,10 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
/* This page is already truncated */
if (unlikely(ni.blk_addr == NULL_ADDR)) {
ClearPageUptodate(page);
folio_clear_uptodate(folio);
dec_page_count(sbi, F2FS_DIRTY_NODES);
f2fs_up_read(&sbi->node_write);
unlock_page(page);
folio_unlock(folio);
return 0;
}
@ -1674,7 +1676,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
goto redirty_out;
}
if (atomic && !test_opt(sbi, NOBARRIER) && !f2fs_sb_has_blkzoned(sbi))
if (atomic && !test_opt(sbi, NOBARRIER))
fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
/* should add to global list before clearing PAGECACHE status */
@ -1684,7 +1686,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
*seq_id = seq;
}
set_page_writeback(page);
folio_start_writeback(folio);
fio.old_blkaddr = ni.blk_addr;
f2fs_do_write_node_page(nid, &fio);
@ -1697,7 +1699,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
submitted = NULL;
}
unlock_page(page);
folio_unlock(folio);
if (unlikely(f2fs_cp_error(sbi))) {
f2fs_submit_merged_write(sbi, NODE);
@ -1711,7 +1713,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
return 0;
redirty_out:
redirty_page_for_writepage(wbc, page);
folio_redirty_for_writepage(wbc, folio);
return AOP_WRITEPAGE_ACTIVATE;
}
@ -1867,7 +1869,7 @@ continue_unlock:
}
if (!ret && atomic && !marked) {
f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx",
ino, last_page->index);
ino, page_folio(last_page)->index);
lock_page(last_page);
f2fs_wait_on_page_writeback(last_page, NODE, true, true);
set_page_dirty(last_page);
@ -3166,7 +3168,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi)
nm_i->nat_bits_blocks = F2FS_BLK_ALIGN((nat_bits_bytes << 1) + 8);
nm_i->nat_bits = f2fs_kvzalloc(sbi,
nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, GFP_KERNEL);
F2FS_BLK_TO_BYTES(nm_i->nat_bits_blocks), GFP_KERNEL);
if (!nm_i->nat_bits)
return -ENOMEM;
@ -3185,7 +3187,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi)
if (IS_ERR(page))
return PTR_ERR(page);
memcpy(nm_i->nat_bits + (i << F2FS_BLKSIZE_BITS),
memcpy(nm_i->nat_bits + F2FS_BLK_TO_BYTES(i),
page_address(page), F2FS_BLKSIZE);
f2fs_put_page(page, 1);
}

View File

@ -199,6 +199,10 @@ void f2fs_abort_atomic_write(struct inode *inode, bool clean)
clear_inode_flag(inode, FI_ATOMIC_COMMITTED);
clear_inode_flag(inode, FI_ATOMIC_REPLACE);
clear_inode_flag(inode, FI_ATOMIC_FILE);
if (is_inode_flag_set(inode, FI_ATOMIC_DIRTIED)) {
clear_inode_flag(inode, FI_ATOMIC_DIRTIED);
f2fs_mark_inode_dirty_sync(inode, true);
}
stat_dec_atomic_inode(inode);
F2FS_I(inode)->atomic_write_task = NULL;
@ -366,6 +370,10 @@ out:
} else {
sbi->committed_atomic_block += fi->atomic_write_cnt;
set_inode_flag(inode, FI_ATOMIC_COMMITTED);
if (is_inode_flag_set(inode, FI_ATOMIC_DIRTIED)) {
clear_inode_flag(inode, FI_ATOMIC_DIRTIED);
f2fs_mark_inode_dirty_sync(inode, true);
}
}
__complete_revoke_list(inode, &revoke_list, ret ? true : false);
@ -1282,6 +1290,13 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
wait_list, issued);
return 0;
}
/*
* Issue discard for conventional zones only if the device
* supports discard.
*/
if (!bdev_max_discard_sectors(bdev))
return -EOPNOTSUPP;
}
#endif
@ -2680,22 +2695,47 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
goto got_it;
}
#ifdef CONFIG_BLK_DEV_ZONED
/*
* If we format f2fs on zoned storage, let's try to get pinned sections
* from beginning of the storage, which should be a conventional one.
*/
if (f2fs_sb_has_blkzoned(sbi)) {
segno = pinning ? 0 : max(first_zoned_segno(sbi), *newseg);
/* Prioritize writing to conventional zones */
if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_PRIOR_CONV || pinning)
segno = 0;
else
segno = max(first_zoned_segno(sbi), *newseg);
hint = GET_SEC_FROM_SEG(sbi, segno);
}
#endif
find_other_zone:
secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
#ifdef CONFIG_BLK_DEV_ZONED
if (secno >= MAIN_SECS(sbi) && f2fs_sb_has_blkzoned(sbi)) {
/* Write only to sequential zones */
if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_ONLY_SEQ) {
hint = GET_SEC_FROM_SEG(sbi, first_zoned_segno(sbi));
secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
} else
secno = find_first_zero_bit(free_i->free_secmap,
MAIN_SECS(sbi));
if (secno >= MAIN_SECS(sbi)) {
ret = -ENOSPC;
f2fs_bug_on(sbi, 1);
goto out_unlock;
}
}
#endif
if (secno >= MAIN_SECS(sbi)) {
secno = find_first_zero_bit(free_i->free_secmap,
MAIN_SECS(sbi));
if (secno >= MAIN_SECS(sbi)) {
ret = -ENOSPC;
f2fs_bug_on(sbi, 1);
goto out_unlock;
}
}
@ -2737,10 +2777,8 @@ got_it:
out_unlock:
spin_unlock(&free_i->segmap_lock);
if (ret == -ENOSPC) {
if (ret == -ENOSPC)
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_NO_SEGMENT);
f2fs_bug_on(sbi, 1);
}
return ret;
}
@ -3046,7 +3084,8 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type,
sanity_check_seg_type(sbi, seg_type);
/* f2fs_need_SSR() already forces to do this */
if (!f2fs_get_victim(sbi, &segno, BG_GC, seg_type, alloc_mode, age)) {
if (!f2fs_get_victim(sbi, &segno, BG_GC, seg_type,
alloc_mode, age, false)) {
curseg->next_segno = segno;
return 1;
}
@ -3073,7 +3112,8 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type,
for (; cnt-- > 0; reversed ? i-- : i++) {
if (i == seg_type)
continue;
if (!f2fs_get_victim(sbi, &segno, BG_GC, i, alloc_mode, age)) {
if (!f2fs_get_victim(sbi, &segno, BG_GC, i,
alloc_mode, age, false)) {
curseg->next_segno = segno;
return 1;
}
@ -3516,7 +3556,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
if (file_is_cold(inode) || f2fs_need_compress_data(inode))
return CURSEG_COLD_DATA;
type = __get_age_segment_type(inode, fio->page->index);
type = __get_age_segment_type(inode,
page_folio(fio->page)->index);
if (type != NO_CHECK_TYPE)
return type;
@ -3775,7 +3816,7 @@ out:
f2fs_up_read(&fio->sbi->io_order_lock);
}
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct folio *folio,
enum iostat_type io_type)
{
struct f2fs_io_info fio = {
@ -3784,20 +3825,20 @@ void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
.temp = HOT,
.op = REQ_OP_WRITE,
.op_flags = REQ_SYNC | REQ_META | REQ_PRIO,
.old_blkaddr = page->index,
.new_blkaddr = page->index,
.page = page,
.old_blkaddr = folio->index,
.new_blkaddr = folio->index,
.page = folio_page(folio, 0),
.encrypted_page = NULL,
.in_list = 0,
};
if (unlikely(page->index >= MAIN_BLKADDR(sbi)))
if (unlikely(folio->index >= MAIN_BLKADDR(sbi)))
fio.op_flags &= ~REQ_META;
set_page_writeback(page);
folio_start_writeback(folio);
f2fs_submit_page_write(&fio);
stat_inc_meta_count(sbi, page->index);
stat_inc_meta_count(sbi, folio->index);
f2fs_update_iostat(sbi, NULL, io_type, F2FS_BLKSIZE);
}
@ -5372,8 +5413,7 @@ unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi,
return BLKS_PER_SEG(sbi);
}
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi,
unsigned int segno)
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi)
{
if (f2fs_sb_has_blkzoned(sbi))
return CAP_SEGS_PER_SEC(sbi);

View File

@ -188,6 +188,7 @@ struct victim_sel_policy {
unsigned int min_segno; /* segment # having min. cost */
unsigned long long age; /* mtime of GCed section*/
unsigned long long age_threshold;/* age threshold */
bool one_time_gc; /* one time GC */
};
struct seg_entry {
@ -430,7 +431,7 @@ static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno)
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);
unsigned int next;
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi, segno);
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi);
spin_lock(&free_i->segmap_lock);
clear_bit(segno, free_i->free_segmap);
@ -464,7 +465,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi,
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);
unsigned int next;
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi, segno);
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi);
spin_lock(&free_i->segmap_lock);
if (test_and_clear_bit(segno, free_i->free_segmap)) {

View File

@ -11,7 +11,6 @@
#include <linux/fs_context.h>
#include <linux/sched/mm.h>
#include <linux/statfs.h>
#include <linux/buffer_head.h>
#include <linux/kthread.h>
#include <linux/parser.h>
#include <linux/mount.h>
@ -697,6 +696,11 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
if (!strcmp(name, "on")) {
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
} else if (!strcmp(name, "off")) {
if (f2fs_sb_has_blkzoned(sbi)) {
f2fs_warn(sbi, "zoned devices need bggc");
kfree(name);
return -EINVAL;
}
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
} else if (!strcmp(name, "sync")) {
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
@ -2557,7 +2561,7 @@ restore_opts:
static void f2fs_shutdown(struct super_block *sb)
{
f2fs_do_shutdown(F2FS_SB(sb), F2FS_GOING_DOWN_NOSYNC, false);
f2fs_do_shutdown(F2FS_SB(sb), F2FS_GOING_DOWN_NOSYNC, false, false);
}
#ifdef CONFIG_QUOTA
@ -3313,29 +3317,47 @@ loff_t max_file_blocks(struct inode *inode)
* fit within U32_MAX + 1 data units.
*/
result = min(result, (((loff_t)U32_MAX + 1) * 4096) >> F2FS_BLKSIZE_BITS);
result = min(result, F2FS_BYTES_TO_BLK(((loff_t)U32_MAX + 1) * 4096));
return result;
}
static int __f2fs_commit_super(struct buffer_head *bh,
struct f2fs_super_block *super)
static int __f2fs_commit_super(struct f2fs_sb_info *sbi, struct folio *folio,
pgoff_t index, bool update)
{
lock_buffer(bh);
if (super)
memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super));
set_buffer_dirty(bh);
unlock_buffer(bh);
struct bio *bio;
/* it's rare case, we can do fua all the time */
return __sync_dirty_buffer(bh, REQ_SYNC | REQ_PREFLUSH | REQ_FUA);
blk_opf_t opf = REQ_OP_WRITE | REQ_SYNC | REQ_PREFLUSH | REQ_FUA;
int ret;
folio_lock(folio);
folio_wait_writeback(folio);
if (update)
memcpy(F2FS_SUPER_BLOCK(folio, index), F2FS_RAW_SUPER(sbi),
sizeof(struct f2fs_super_block));
folio_mark_dirty(folio);
folio_clear_dirty_for_io(folio);
folio_start_writeback(folio);
folio_unlock(folio);
bio = bio_alloc(sbi->sb->s_bdev, 1, opf, GFP_NOFS);
/* it doesn't need to set crypto context for superblock update */
bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(folio_index(folio));
if (!bio_add_folio(bio, folio, folio_size(folio), 0))
f2fs_bug_on(sbi, 1);
ret = submit_bio_wait(bio);
folio_end_writeback(folio);
return ret;
}
static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
struct buffer_head *bh)
struct folio *folio, pgoff_t index)
{
struct f2fs_super_block *raw_super = (struct f2fs_super_block *)
(bh->b_data + F2FS_SUPER_OFFSET);
struct f2fs_super_block *raw_super = F2FS_SUPER_BLOCK(folio, index);
struct super_block *sb = sbi->sb;
u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr);
u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr);
@ -3351,9 +3373,9 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
u32 segment_count = le32_to_cpu(raw_super->segment_count);
u32 log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
u64 main_end_blkaddr = main_blkaddr +
(segment_count_main << log_blocks_per_seg);
((u64)segment_count_main << log_blocks_per_seg);
u64 seg_end_blkaddr = segment0_blkaddr +
(segment_count << log_blocks_per_seg);
((u64)segment_count << log_blocks_per_seg);
if (segment0_blkaddr != cp_blkaddr) {
f2fs_info(sbi, "Mismatch start address, segment0(%u) cp_blkaddr(%u)",
@ -3410,7 +3432,7 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
set_sbi_flag(sbi, SBI_NEED_SB_WRITE);
res = "internally";
} else {
err = __f2fs_commit_super(bh, NULL);
err = __f2fs_commit_super(sbi, folio, index, false);
res = err ? "failed" : "done";
}
f2fs_info(sbi, "Fix alignment : %s, start(%u) end(%llu) block(%u)",
@ -3423,12 +3445,11 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
}
static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
struct buffer_head *bh)
struct folio *folio, pgoff_t index)
{
block_t segment_count, segs_per_sec, secs_per_zone, segment_count_main;
block_t total_sections, blocks_per_seg;
struct f2fs_super_block *raw_super = (struct f2fs_super_block *)
(bh->b_data + F2FS_SUPER_OFFSET);
struct f2fs_super_block *raw_super = F2FS_SUPER_BLOCK(folio, index);
size_t crc_offset = 0;
__u32 crc = 0;
@ -3586,7 +3607,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
}
/* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
if (sanity_check_area_boundary(sbi, bh))
if (sanity_check_area_boundary(sbi, folio, index))
return -EFSCORRUPTED;
return 0;
@ -3781,6 +3802,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
sbi->next_victim_seg[FG_GC] = NULL_SEGNO;
sbi->max_victim_search = DEF_MAX_VICTIM_SEARCH;
sbi->migration_granularity = SEGS_PER_SEC(sbi);
sbi->migration_window_granularity = f2fs_sb_has_blkzoned(sbi) ?
DEF_MIGRATION_WINDOW_GRANULARITY_ZONED : SEGS_PER_SEC(sbi);
sbi->seq_file_ra_mul = MIN_RA_MUL;
sbi->max_fragment_chunk = DEF_FRAGMENT_SIZE;
sbi->max_fragment_hole = DEF_FRAGMENT_SIZE;
@ -3933,7 +3956,7 @@ static int read_raw_super_block(struct f2fs_sb_info *sbi,
{
struct super_block *sb = sbi->sb;
int block;
struct buffer_head *bh;
struct folio *folio;
struct f2fs_super_block *super;
int err = 0;
@ -3942,32 +3965,33 @@ static int read_raw_super_block(struct f2fs_sb_info *sbi,
return -ENOMEM;
for (block = 0; block < 2; block++) {
bh = sb_bread(sb, block);
if (!bh) {
folio = read_mapping_folio(sb->s_bdev->bd_inode->i_mapping,
block, NULL);
if (IS_ERR(folio)) {
f2fs_err(sbi, "Unable to read %dth superblock",
block + 1);
err = -EIO;
err = PTR_ERR(folio);
*recovery = 1;
continue;
}
/* sanity checking of raw super */
err = sanity_check_raw_super(sbi, bh);
err = sanity_check_raw_super(sbi, folio, block);
if (err) {
f2fs_err(sbi, "Can't find valid F2FS filesystem in %dth superblock",
block + 1);
brelse(bh);
folio_put(folio);
*recovery = 1;
continue;
}
if (!*raw_super) {
memcpy(super, bh->b_data + F2FS_SUPER_OFFSET,
memcpy(super, F2FS_SUPER_BLOCK(folio, block),
sizeof(*super));
*valid_super_block = block;
*raw_super = super;
}
brelse(bh);
folio_put(folio);
}
/* No valid superblock */
@ -3981,7 +4005,8 @@ static int read_raw_super_block(struct f2fs_sb_info *sbi,
int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
{
struct buffer_head *bh;
struct folio *folio;
pgoff_t index;
__u32 crc = 0;
int err;
@ -3999,22 +4024,26 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
}
/* write back-up superblock first */
bh = sb_bread(sbi->sb, sbi->valid_super_block ? 0 : 1);
if (!bh)
return -EIO;
err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi));
brelse(bh);
index = sbi->valid_super_block ? 0 : 1;
folio = read_mapping_folio(sbi->sb->s_bdev->bd_inode->i_mapping,
index, NULL);
if (IS_ERR(folio))
return PTR_ERR(folio);
err = __f2fs_commit_super(sbi, folio, index, true);
folio_put(folio);
/* if we are in recovery path, skip writing valid superblock */
if (recover || err)
return err;
/* write current valid superblock */
bh = sb_bread(sbi->sb, sbi->valid_super_block);
if (!bh)
return -EIO;
err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi));
brelse(bh);
index = sbi->valid_super_block;
folio = read_mapping_folio(sbi->sb->s_bdev->bd_inode->i_mapping,
index, NULL);
if (IS_ERR(folio))
return PTR_ERR(folio);
err = __f2fs_commit_super(sbi, folio, index, true);
folio_put(folio);
return err;
}
@ -4168,12 +4197,14 @@ void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason,
}
f2fs_warn(sbi, "Remounting filesystem read-only");
/*
* Make sure updated value of ->s_mount_flags will be visible before
* ->s_flags update
* We have already set CP_ERROR_FLAG flag to stop all updates
* to filesystem, so it doesn't need to set SB_RDONLY flag here
* because the flag should be set covered w/ sb->s_umount semaphore
* via remount procedure, otherwise, it will confuse code like
* freeze_super() which will lead to deadlocks and other problems.
*/
smp_wmb();
sb->s_flags |= SB_RDONLY;
}
static void f2fs_record_error_work(struct work_struct *work)
@ -4214,6 +4245,7 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
sbi->aligned_blksize = true;
#ifdef CONFIG_BLK_DEV_ZONED
sbi->max_open_zones = UINT_MAX;
sbi->blkzone_alloc_policy = BLKZONE_ALLOC_PRIOR_SEQ;
#endif
for (i = 0; i < max_devices; i++) {

View File

@ -170,6 +170,12 @@ static ssize_t undiscard_blks_show(struct f2fs_attr *a,
SM_I(sbi)->dcc_info->undiscard_blks);
}
static ssize_t atgc_enabled_show(struct f2fs_attr *a,
struct f2fs_sb_info *sbi, char *buf)
{
return sysfs_emit(buf, "%d\n", sbi->am.atgc_enabled ? 1 : 0);
}
static ssize_t gc_mode_show(struct f2fs_attr *a,
struct f2fs_sb_info *sbi, char *buf)
{
@ -182,50 +188,50 @@ static ssize_t features_show(struct f2fs_attr *a,
int len = 0;
if (f2fs_sb_has_encrypt(sbi))
len += scnprintf(buf, PAGE_SIZE - len, "%s",
len += sysfs_emit_at(buf, len, "%s",
"encryption");
if (f2fs_sb_has_blkzoned(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "blkzoned");
if (f2fs_sb_has_extra_attr(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "extra_attr");
if (f2fs_sb_has_project_quota(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "projquota");
if (f2fs_sb_has_inode_chksum(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "inode_checksum");
if (f2fs_sb_has_flexible_inline_xattr(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "flexible_inline_xattr");
if (f2fs_sb_has_quota_ino(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "quota_ino");
if (f2fs_sb_has_inode_crtime(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "inode_crtime");
if (f2fs_sb_has_lost_found(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "lost_found");
if (f2fs_sb_has_verity(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "verity");
if (f2fs_sb_has_sb_chksum(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "sb_checksum");
if (f2fs_sb_has_casefold(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "casefold");
if (f2fs_sb_has_readonly(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "readonly");
if (f2fs_sb_has_compression(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "compression");
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len += sysfs_emit_at(buf, len, "%s%s",
len ? ", " : "", "pin_file");
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
len += sysfs_emit_at(buf, len, "\n");
return len;
}
@ -323,17 +329,14 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
int hot_count = sbi->raw_super->hot_ext_count;
int len = 0, i;
len += scnprintf(buf + len, PAGE_SIZE - len,
"cold file extension:\n");
len += sysfs_emit_at(buf, len, "cold file extension:\n");
for (i = 0; i < cold_count; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n",
extlist[i]);
len += sysfs_emit_at(buf, len, "%s\n", extlist[i]);
len += scnprintf(buf + len, PAGE_SIZE - len,
"hot file extension:\n");
len += sysfs_emit_at(buf, len, "hot file extension:\n");
for (i = cold_count; i < cold_count + hot_count; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n",
extlist[i]);
len += sysfs_emit_at(buf, len, "%s\n", extlist[i]);
return len;
}
@ -561,6 +564,11 @@ out:
return -EINVAL;
}
if (!strcmp(a->attr.name, "migration_window_granularity")) {
if (t == 0 || t > SEGS_PER_SEC(sbi))
return -EINVAL;
}
if (!strcmp(a->attr.name, "gc_urgent")) {
if (t == 0) {
sbi->gc_mode = GC_NORMAL;
@ -627,6 +635,15 @@ out:
}
#endif
#ifdef CONFIG_BLK_DEV_ZONED
if (!strcmp(a->attr.name, "blkzone_alloc_policy")) {
if (t < BLKZONE_ALLOC_PRIOR_SEQ || t > BLKZONE_ALLOC_PRIOR_CONV)
return -EINVAL;
sbi->blkzone_alloc_policy = t;
return count;
}
#endif
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (!strcmp(a->attr.name, "compr_written_block") ||
!strcmp(a->attr.name, "compr_saved_block")) {
@ -775,7 +792,8 @@ out:
if (!strcmp(a->attr.name, "ipu_policy")) {
if (t >= BIT(F2FS_IPU_MAX))
return -EINVAL;
if (t && f2fs_lfs_mode(sbi))
/* allow F2FS_IPU_NOCACHE only for IPU in the pinned file */
if (f2fs_lfs_mode(sbi) && (t & ~BIT(F2FS_IPU_NOCACHE)))
return -EINVAL;
SM_I(sbi)->ipu_policy = (unsigned int)t;
return count;
@ -960,6 +978,9 @@ GC_THREAD_RW_ATTR(gc_urgent_sleep_time, urgent_sleep_time);
GC_THREAD_RW_ATTR(gc_min_sleep_time, min_sleep_time);
GC_THREAD_RW_ATTR(gc_max_sleep_time, max_sleep_time);
GC_THREAD_RW_ATTR(gc_no_gc_sleep_time, no_gc_sleep_time);
GC_THREAD_RW_ATTR(gc_no_zoned_gc_percent, no_zoned_gc_percent);
GC_THREAD_RW_ATTR(gc_boost_zoned_gc_percent, boost_zoned_gc_percent);
GC_THREAD_RW_ATTR(gc_valid_thresh_ratio, valid_thresh_ratio);
/* SM_INFO ATTR */
SM_INFO_RW_ATTR(reclaim_segments, rec_prefree_segments);
@ -969,6 +990,7 @@ SM_INFO_GENERAL_RW_ATTR(min_fsync_blocks);
SM_INFO_GENERAL_RW_ATTR(min_seq_blocks);
SM_INFO_GENERAL_RW_ATTR(min_hot_blocks);
SM_INFO_GENERAL_RW_ATTR(min_ssr_sections);
SM_INFO_GENERAL_RW_ATTR(reserved_segments);
/* DCC_INFO ATTR */
DCC_INFO_RW_ATTR(max_small_discards, max_discards);
@ -1001,6 +1023,7 @@ F2FS_SBI_RW_ATTR(gc_pin_file_thresh, gc_pin_file_threshold);
F2FS_SBI_RW_ATTR(gc_reclaimed_segments, gc_reclaimed_segs);
F2FS_SBI_GENERAL_RW_ATTR(max_victim_search);
F2FS_SBI_GENERAL_RW_ATTR(migration_granularity);
F2FS_SBI_GENERAL_RW_ATTR(migration_window_granularity);
F2FS_SBI_GENERAL_RW_ATTR(dir_level);
#ifdef CONFIG_F2FS_IOSTAT
F2FS_SBI_GENERAL_RW_ATTR(iostat_enable);
@ -1033,6 +1056,7 @@ F2FS_SBI_GENERAL_RW_ATTR(warm_data_age_threshold);
F2FS_SBI_GENERAL_RW_ATTR(last_age_weight);
#ifdef CONFIG_BLK_DEV_ZONED
F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec);
F2FS_SBI_GENERAL_RW_ATTR(blkzone_alloc_policy);
#endif
/* STAT_INFO ATTR */
@ -1072,6 +1096,7 @@ F2FS_GENERAL_RO_ATTR(encoding);
F2FS_GENERAL_RO_ATTR(mounted_time_sec);
F2FS_GENERAL_RO_ATTR(main_blkaddr);
F2FS_GENERAL_RO_ATTR(pending_discard);
F2FS_GENERAL_RO_ATTR(atgc_enabled);
F2FS_GENERAL_RO_ATTR(gc_mode);
#ifdef CONFIG_F2FS_STAT_FS
F2FS_GENERAL_RO_ATTR(moved_blocks_background);
@ -1116,6 +1141,9 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(gc_min_sleep_time),
ATTR_LIST(gc_max_sleep_time),
ATTR_LIST(gc_no_gc_sleep_time),
ATTR_LIST(gc_no_zoned_gc_percent),
ATTR_LIST(gc_boost_zoned_gc_percent),
ATTR_LIST(gc_valid_thresh_ratio),
ATTR_LIST(gc_idle),
ATTR_LIST(gc_urgent),
ATTR_LIST(reclaim_segments),
@ -1138,8 +1166,10 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(min_seq_blocks),
ATTR_LIST(min_hot_blocks),
ATTR_LIST(min_ssr_sections),
ATTR_LIST(reserved_segments),
ATTR_LIST(max_victim_search),
ATTR_LIST(migration_granularity),
ATTR_LIST(migration_window_granularity),
ATTR_LIST(dir_level),
ATTR_LIST(ram_thresh),
ATTR_LIST(ra_nid_pages),
@ -1187,6 +1217,7 @@ static struct attribute *f2fs_attrs[] = {
#endif
#ifdef CONFIG_BLK_DEV_ZONED
ATTR_LIST(unusable_blocks_per_sec),
ATTR_LIST(blkzone_alloc_policy),
#endif
#ifdef CONFIG_F2FS_FS_COMPRESSION
ATTR_LIST(compr_written_block),
@ -1200,6 +1231,7 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(atgc_candidate_count),
ATTR_LIST(atgc_age_weight),
ATTR_LIST(atgc_age_threshold),
ATTR_LIST(atgc_enabled),
ATTR_LIST(seq_file_ra_mul),
ATTR_LIST(gc_segment_mode),
ATTR_LIST(gc_reclaimed_segments),

Some files were not shown because too many files have changed in this diff Show More