mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-06 17:35:20 +02:00
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:
commit
886869d11e
16
BUILD.bazel
16
BUILD.bazel
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
99
Documentation/device-mapper/dm-bow.txt
Normal file
99
Documentation/device-mapper/dm-bow.txt
Normal 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.
|
||||
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
38
android/abi_gki_aarch64_kunit
Normal file
38
android/abi_gki_aarch64_kunit
Normal 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
|
|
@ -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
|
||||
|
|
1835
android/abi_gki_aarch64_mtktv
Normal file
1835
android/abi_gki_aarch64_mtktv
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
2401
android/abi_gki_aarch64_pixel_watch
Normal file
2401
android/abi_gki_aarch64_pixel_watch
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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) \
|
||||
)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -255,5 +255,15 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"kernel-presubmit": [
|
||||
{
|
||||
"name": "CtsIncrementalInstallHostTestCases",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "android.incrementalinstall.cts.IncrementalInstallTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
1309
drivers/md/dm-bow.c
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
252
fs/exfat/dir.c
252
fs/exfat/dir.c
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
304
fs/exfat/file.c
304
fs/exfat/file.c
|
@ -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,
|
||||
|
|
174
fs/exfat/inode.c
174
fs/exfat/inode.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
390
fs/exfat/namei.c
390
fs/exfat/namei.c
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) {
|
||||
|
|
198
fs/f2fs/data.c
198
fs/f2fs/data.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
150
fs/f2fs/f2fs.h
150
fs/f2fs/f2fs.h
|
@ -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;
|
||||
|
||||
|
|
199
fs/f2fs/file.c
199
fs/f2fs/file.c
|
@ -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);
|
||||
|
|
113
fs/f2fs/gc.c
113
fs/f2fs/gc.c
|
@ -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))
|
||||
|
|
29
fs/f2fs/gc.h
29
fs/f2fs/gc.h
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
122
fs/f2fs/super.c
122
fs/f2fs/super.c
|
@ -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++) {
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue
Block a user